# Id

> The primary key. Every model with a table has exactly one.

Source: CBX documentation, version 3.x (released). Canonical page: https://docs.configbox.at/docs/technical/property-types/id. Last updated 2026-07-27.

---
The primary key. Every model with a table has exactly one.

**Storage kind:** `column` · **Column:** `int unsigned`, auto_increment

You do not choose this type so much as declare it. The one decision it carries is `makeEditLink`:
whether the id cell in the list links through to the record's edit form.

## Settings

| Setting | Meaning |
|---|---|
| `makeEditLink` | Render the id as a link to the record's edit form. Needs `component` and `controller` |
| `default` | Conventionally `0`, meaning "not saved yet" |

## It renders itself, in two places

There is no `tmpl/id.php`, and its absence is not a bug — this type overrides the rendering methods
directly:

- **`getBodyAdmin()` returns an empty string.** The id is not an editable form field; the edit form
  shows nothing for it. The value round-trips through the form's own hidden id input, not through
  this property.
- **`getCellContentInListingTable()`** emits the row's `cid[]` checkbox and then the id itself,
  wrapped in an edit link when `makeEditLink` is set. **`getHeaderCellContentInListingTable()`**
  emits the check-all box.

So the checkbox column every admin list has is this property. `isInListing()` is hard-coded `true`
rather than deriving from `positionList` the way every other type does — the selection column is not
optional. Give it a `positionList` anyway: that is what orders it, and without one it falls back to
`0`.

## Insert versus update

`isInsert()` on the model is `empty($data->{$tableKey})`, so a `0` or absent id is what makes a
store an insert rather than an update. `getDataFromRequest()` maps a submitted `'0'` to `NULL`,
which lands in the same place.

Two things follow:

- **A submitted id of `0` creates a record.** There is no "update record 0".
- **`getDataKeysForBaseTable()` returns the id only when the data object actually carries the key.**
  On an insert it is left out entirely, so MySQL's auto_increment assigns it rather than the code
  writing an explicit `0`.

It is read-only to clients: generated schemas and the MCP tool descriptions mark it so. A create call
supplies no id; an update identifies the record by it.

@see ordering.md
@see ../com_configbox_kenedo_model.md — `isInsert()` and the store flow
