Skip to main content

Mapping attributes

Mapping is read from attributes and from the property's own type. A class with no attributes at all is still mappable as long as something marks the identifier, because everything else has a convention behind it.

The attributes

AttributeTargetOptions
#[Entity]classtable, connection
#[Id]propertycolumn, converter
#[Column]propertycolumn, converter
#[Generated]propertynone
#[Ignore]propertynone

#[Entity]

Optional. Names the table or the connection when the convention is wrong.

OptionTypeDefaultMeaning
table?stringnullThe table name. null derives it from the class name.
connection?stringnullThe connection to use. null uses the default connection.
#[Entity(table: 'memberships', connection: 'replica')]
final class Membership { /* ... */ }

A class without #[Entity] maps the same way; the attribute exists only to carry those two options.

The class has to be instantiable. An interface, an abstract class, a trait or an enum throws MappingException.

#[Id]

Marks a property as part of the identifier. At least one is required: a class with none throws MappingException. Its options are the same as #[Column]'s, so an identifier does not need both attributes.

#[Id]
public int $id;

#[Id(column: 'user_uuid')]
public string $uuid;

More than one #[Id] makes the identifier composite, described in Identifiers.

#[Column]

Optional. Renames the column or names the converter.

OptionTypeDefaultMeaning
column?stringnullThe column name. null derives it from the property name.
converter?stringnullThe converter to use. null looks one up by the property's type.
#[Column(column: 'display_name')]
public string $displayName;

#[Column(converter: 'json')]
public array $meta;

The converter option is a registry key, not a class name. See Converters.

caution

#[Id] and #[Column] on the same property throw MappingException. #[Id] already accepts both options, so use it alone.

#[Generated]

Says the database supplies the value. The column is left out of inserts and updates.

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

When it is on a single-property identifier, the value the database assigned is read back onto the entity after an insert. On any other property nothing is read back — see what a write reads back.

#[Ignore]

Leaves a property out of the mapping entirely. It is never read, never written, and its type does not need a converter.

#[Ignore]
public string $transient;
caution

#[Ignore] together with #[Id], #[Column] or #[Generated] throws MappingException naming every attribute involved. Ignoring a property and mapping it are contradictory instructions, so the package refuses rather than picking one.

What is mapped without any attribute

Every non-static property is mapped, with its column derived from its name and its converter looked up by its type.

Static properties are skipped, because a column belongs to a row and a static property does not.

What a property type has to be

PropertyResult
A single type with a converter, such as string or ?intMapped.
A backed enumMapped; a converter is built for it without registration.
No type at allMappingException: missing property type.
A union or intersection type, such as string|intMappingException: unsupported property type.
mixedMappingException: unsupported property type.
A single type with no converter, such as DateTimeImmutable, a bare array or an enum with no backing typeTypeConversionException: unsupported type.

A nullable type is recorded as nullable, which is what lets a NULL column read back as null. See Converters.

Two properties resolving to the same column throw MappingException naming both of them, because one would silently overwrite the other.