agentleFS
AnyOrgNameAnyOrgName/php-skills/skills/doctrine/entities-mapping/SKILL.md

entities-mapping

Skill4 starsChanged 4 months agoMITView on GitHub

Map PHP classes to database tables with Doctrine — PHP 8 attribute mapping, identifiers and generation strategies, column types, and nullability, so entities load and persist with the right SQL types. Use when the user must pick a primary key and an id generation strategy (auto, identity, sequence, or UUID) for a Doctrine entity; the user needs to map PHP property types to Doctrine column types, lengths, and nullability; the user needs to declare how a PHP class maps to a table with Doctrine — entity, columns, and identifier. Keywords: doctrine, orm, mapping, attributes, entity, identifier, sql.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do not use Doctrine mapping attributes on an Active Record model (for example an Eloquent model) that already maps itself by convention.

    line 66
  2. Distinctive:

    Do not map an entity for data that is only queried read-only and never persisted, such as ad-hoc report rows.

    line 67
  3. Very specific:

    Add #[ORM\Entity] (and #[ORM\Table(name: 'orders')] if the table name differs) to the class so Doctrine treats it as a mapped entity.

    line 80
  4. Very specific:

    Add an id property with #[ORM\Id], #[ORM\GeneratedValue], and #[ORM\Column] so the entity has a primary key and a generation strategy.

    line 82

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- generated: do not edit — source: knowledge/atoms/doctrine/entities-mapping/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->

Doctrine Entity Mapping

When to use

  • The user must pick a primary key and an id generation strategy (auto, identity, sequence, or UUID) for a Doctrine entity.
  • The user needs to map PHP property types to Doctrine column types, lengths, and nullability.
  • The user needs to declare how a PHP class maps to a table with Doctrine — entity, columns, and identifier.

When NOT to use

  • Do not use Doctrine mapping attributes on an Active Record model (for example an Eloquent model) that already maps itself by convention.
  • Do not map an entity for data that is only queried read-only and never persisted, such as ad-hoc report rows.

Core guidance

  • Keep large columns out of the default hydration path — For large text or BLOB columns rarely needed at read time, split them into a separate entity or use a partial/projection query, so the common load does not hydrate megabytes per row.
  • Prefer PHP 8 attribute mapping over annotations — On PHP 8.1+, map entities with native attributes (#[ORM\Entity], #[ORM\Column]) rather than doc-block annotations. Reserve XML mapping for cases where mapping must live outside the class.

Quick recipe

  1. Add #[ORM\Entity] (and #[ORM\Table(name: 'orders')] if the table name differs) to the class so Doctrine treats it as a mapped entity. (verify: orm:mapping:describe App\Entity\Order lists the class without a mapping error.)
  2. Add an id property with #[ORM\Id], #[ORM\GeneratedValue], and #[ORM\Column] so the entity has a primary key and a generation strategy. (verify: The mapping describe output shows the id field flagged as the identifier.)
  3. Map each field with #[ORM\Column(type: ..., nullable: ...)], then generate a reviewed migration from the mapping diff and apply it. (verify: The generated migration creates the table with the expected columns, types, and nullability.)

Best practices

  • Persist a value object (money, email, coordinates) with an #[ORM\Embedded] embeddable or a registered custom DBAL type, rather than flattening it into loose scalar columns by hand
  • Declare the identifier with #[ORM\Id], #[ORM\GeneratedValue], and choose the strategy to fit the driver — IDENTITY for MySQL auto-increment, SEQUENCE for PostgreSQL, or an application-assigned UUID when ids must be known before insert
  • Set nullable: true on the column only when the PHP property type is nullable (?string), and keep them in sync — a non-nullable typed property must map to a NOT NULL column

Anti-patterns

Anti-patternWhy it failsDo instead
New annotation-based mapping on ORM 3ORM 3 removed the annotation driver, so annotation mapping simply does not load and the entity is treated as unmapped.Use native PHP attributes (or XML) for all new mapping, and convert legacy annotations during the ORM 3 upgrade.
Public mutable properties on the entityPublic setters everywhere let any caller push an entity into an invalid state, defeating the invariants the domain should enforce.Keep mapped properties private and expose intent-revealing methods (for example markPaid()) that enforce valid transitions.
Running schema-tool update against productionThe tool issues unversioned, potentially destructive ALTER and DROP statements with no review or rollback path.Generate a reviewed migration with doctrine/migrations from the mapping diff and apply that, so schema changes are versioned and reversible.

See references/anti-patterns.md.

Common mistakes

  1. Mapping to datetime lets mutable DateTime corrupt change tracking — Mapping a property as the mutable datetime type with a DateTime object lets in-place mutation slip past dirty checking. Fix: Map as datetime_immutable and type the property DateTimeImmutable so every change produces a new object the unit of work can detect.
  2. Entity without an — An entity class annotated #[ORM\Entity] but with no #[ORM\Id] property throws a MappingException at boot. Fix: Declare exactly one identifier property with #[ORM\Id] (or a composite of several) before using the entity.
  3. Unspecified string length silently defaults to 255 — A #[ORM\Column(type: 'string')] without an explicit length generates a VARCHAR(255), truncating longer values. Fix: Set length explicitly, or map to the text type when the field has no natural bound.

Version notes

  • Attributes are the only in-core annotation format on ORM 3 (PHP >=8.1) — Doctrine ORM 3 ships attribute and XML mapping in core and drops the doc-block annotation driver; on ORM 2 both attributes and annotations still work.

Security

  • Do not hydrate entities directly from request input — Do not build or populate a mapped entity by copying raw request arrays field by field; map validated input onto the entity through explicit setters or a DTO, and never expose the identifier or internal flags to mass assignment.

Testing

  • Activation and decision behavior are verified in eval/scenarios/doctrine/entities-mapping.yaml.

Additional resources

  • references/anti-patterns.md
  • references/core.md
  • references/examples.md
  • references/pitfalls.md
  • references/recipes.md
  • references/version-differences.md

Knowledge graph

Prerequisites: doctrine/orm-basics
Related: doctrine/associations, doctrine/orm-basics, php/value-objects

<!-- graph-fragment: graph/fragments/doctrine/entities-mapping.yaml -->

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.