Skip to main content

Identifiers

Every entity needs an identifier: it is how find(), update() and delete() locate a row. One or more #[Id] properties declare it, and a class with none throws MappingException.

Generated by the database

#[Id]
#[Generated]
public int $id;

The column is left out of the insert and the value the database assigned is written back onto the entity afterwards. This works on every supported driver, whether the value comes from RETURNING or from lastInsertId().

If the database reports no generated value, insert() throws PersistenceException rather than leaving the property uninitialised.

Assigned by the application

Leave #[Generated] off and the value you set is written like any other column.

#[Entity]
final class Country
{
#[Id]
public string $code;

public string $name;
}

$country = new Country();
$country->code = 'nl';
$country->name = 'Netherlands';

$countries->insert($country);

Nothing is read back, because nothing was generated.

caution

An assigned identifier has to be set before insert(). An uninitialised property throws PersistenceException naming it, rather than writing a row with a value the package invented.

Composite

More than one #[Id] makes the identifier composite.

#[Entity(table: 'memberships')]
final class Membership
{
#[Id]
public int $teamId;

#[Id]
public int $userId;

public string $role;
}

find() then takes an array keyed by property name, not column name:

$memberships->find(['teamId' => 1, 'userId' => 2]);
What you passWhat happens
An array with every identifier propertyThe row is looked up.
An array missing oneInvalidIdentifierException, naming the property.
A scalarInvalidIdentifierException: a composite identifier was expected.
Extra keys that are not identifier propertiesIgnored.

A composite identifier is never read back from an insert. The database reports one generated key at most, so #[Generated] on a composite identifier property has nothing to return; set the values yourself.

Which identifier a store uses

update() and delete() read the identifier off the entity you hand them, so the object has to carry the values that locate its row. For a generated identifier that means the entity came from a find() or has been through insert().

An identifier that matches more than one row makes update() and delete() throw PersistenceException, described in what a write returns.