OroCommerce v6.1 Entity Development
Canonical Entity (PHP 8 Attributes)
This is the reference pattern combining ExtendEntityInterface, ownership, security, and #[ConfigField]:
<?php
namespace Acme\Bundle\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Oro\Bundle\EntityConfigBundle\Metadata\Attribute\Config;
use Oro\Bundle\EntityConfigBundle\Metadata\Attribute\ConfigField;
use Oro\Bundle\EntityExtendBundle\Entity\ExtendEntityInterface;
use Oro\Bundle\EntityExtendBundle\Entity\ExtendEntityTrait;
use Oro\Bundle\OrganizationBundle\Entity\Organization;
use Oro\Bundle\UserBundle\Entity\User;
#[ORM\Entity]
#[ORM\Table(name: 'acme_demo_document')]
#[Config(
routeName: 'acme_demo_document_index',
routeView: 'acme_demo_document_view',
defaultValues: [
'entity' => ['icon' => 'fa-file', 'label' => 'Document', 'plural_label' => 'Documents'],
'ownership' => [
'owner_type' => 'USER',
'owner_field_name' => 'owner',
'owner_column_name' => 'user_owner_id',
'organization_field_name' => 'organization',
'organization_column_name' => 'organization_id',
],
'security' => ['type' => 'ACL', 'permissions' => 'VIEW;CREATE;EDIT;DELETE', 'group_name' => ''],
]
)]
class Document implements ExtendEntityInterface
{
use ExtendEntityTrait;
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 255)]
#[ConfigField(defaultValues: ['dataaudit' => ['auditable' => true]])]
private string $title;
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(name: 'user_owner_id', referencedColumnName: 'id', onDelete: 'SET NULL')]
private ?User $owner = null;
#[ORM\ManyToOne(targetEntity: Organization::class)]
#[ORM\JoinColumn(name: 'organization_id', referencedColumnName: 'id', onDelete: 'SET NULL')]
private ?Organization $organization = null;
// Getters/setters...
}
ExtendEntityInterface
Use ExtendEntityInterface + ExtendEntityTrait only when admins add fields at runtime via System → Entity Management; the trait supplies the magic __get/__set/__isset/__call for them. Entities not extended at runtime do not need it.
Ownership Decision Tree
- Static/reference data? -> GLOBAL
- Shared within org, not by department? -> ORGANIZATION
- Department/team owned? -> BUSINESS_UNIT (include both org + BU fields)
- Personal/assigned to user? -> USER (include both org + user fields)
CRITICAL: USER and BUSINESS_UNIT ownership both require an organization field. Missing it causes silent access control failures. See references/ownership-types.md for full config of all four types.
Migration: Creating a Table
Migrations live in src/Acme/Bundle/DemoBundle/Migrations/Schema/, one subdirectory per version (v1_0/, v1_1/). A Migration implementation builds the table in up(Schema $schema, QueryBag $queries) — worked example, including the ownership columns a USER/BUSINESS_UNIT entity needs, in references/v6.1.md.
Key Pitfalls
- Missing organization field on USER/BUSINESS_UNIT ownership — Access control fails silently
- Using old
@ORM\ annotations instead of #[ORM\...] attributes — Doctrine won't recognize them in v6.1
- Enum codes over 21 characters — Oro uses them to generate table names; exceeding the limit causes silent failures
- Expecting admin-UI field delete to remove the column — it only soft-deletes (config
state = Deleted, column kept), which blocks re-creating a field with the same name; see references/removing-extend-fields.md
See Also
references/ownership-types.md — all four ownership types and their field requirements
references/entity-patterns.md — enum entities, ConfigField, extending core entities, repositories, commands
references/v6.1.md — v6.1 specifics, the migration example, common failures
references/removing-extend-fields.md — RemoveFieldQuery, attribute-family cleanup, POST_UP listeners, multi-host caveats
1---2name: oro-entity3description: Use when creating OroCommerce v6.1 Doctrine entities, extending Oro core entities (Product, Order, Customer), writing schema migrations, configuring ownership (USER, BUSINESS_UNIT, ORGANIZATION, GLOBAL), ConfigField attributes, enum entities, ExtendEntity traits, or removing/hard-deleting extend fields and attributes (soft-delete, RemoveFieldQuery, dropColumn). Triggers on 'create entity', 'add field to Product', 'write migration', 'extend entity', 'custom field', 'remove field', 'delete attribute'.4---56# OroCommerce v6.1 Entity Development78## Canonical Entity (PHP 8 Attributes)910This is the reference pattern combining `ExtendEntityInterface`, ownership, security, and `#[ConfigField]`:1112```php13<?php14namespace Acme\Bundle\DemoBundle\Entity;1516use Doctrine\ORM\Mapping as ORM;17use Oro\Bundle\EntityConfigBundle\Metadata\Attribute\Config;18use Oro\Bundle\EntityConfigBundle\Metadata\Attribute\ConfigField;19use Oro\Bundle\EntityExtendBundle\Entity\ExtendEntityInterface;20use Oro\Bundle\EntityExtendBundle\Entity\ExtendEntityTrait;21use Oro\Bundle\OrganizationBundle\Entity\Organization;22use Oro\Bundle\UserBundle\Entity\User;2324#[ORM\Entity]25#[ORM\Table(name: 'acme_demo_document')]26#[Config(27 routeName: 'acme_demo_document_index',28 routeView: 'acme_demo_document_view',29 defaultValues: [30 'entity' => ['icon' => 'fa-file', 'label' => 'Document', 'plural_label' => 'Documents'],31 'ownership' => [32 'owner_type' => 'USER',33 'owner_field_name' => 'owner',34 'owner_column_name' => 'user_owner_id',35 'organization_field_name' => 'organization',36 'organization_column_name' => 'organization_id',37 ],38 'security' => ['type' => 'ACL', 'permissions' => 'VIEW;CREATE;EDIT;DELETE', 'group_name' => ''],39 ]40)]41class Document implements ExtendEntityInterface42{43 use ExtendEntityTrait;4445 #[ORM\Id]46 #[ORM\GeneratedValue(strategy: 'AUTO')]47 #[ORM\Column(type: 'integer')]48 private ?int $id = null;4950 #[ORM\Column(type: 'string', length: 255)]51 #[ConfigField(defaultValues: ['dataaudit' => ['auditable' => true]])]52 private string $title;5354 #[ORM\ManyToOne(targetEntity: User::class)]55 #[ORM\JoinColumn(name: 'user_owner_id', referencedColumnName: 'id', onDelete: 'SET NULL')]56 private ?User $owner = null;5758 #[ORM\ManyToOne(targetEntity: Organization::class)]59 #[ORM\JoinColumn(name: 'organization_id', referencedColumnName: 'id', onDelete: 'SET NULL')]60 private ?Organization $organization = null;6162 // Getters/setters...63}64```6566## ExtendEntityInterface6768Use `ExtendEntityInterface` + `ExtendEntityTrait` only when admins add fields at runtime via System → Entity Management; the trait supplies the magic `__get`/`__set`/`__isset`/`__call` for them. Entities not extended at runtime do not need it.6970## Ownership Decision Tree7172- Static/reference data? -> **GLOBAL**73- Shared within org, not by department? -> **ORGANIZATION**74- Department/team owned? -> **BUSINESS_UNIT** (include both org + BU fields)75- Personal/assigned to user? -> **USER** (include both org + user fields)7677**CRITICAL: USER and BUSINESS_UNIT ownership both require an `organization` field.** Missing it causes silent access control failures. See `references/ownership-types.md` for full config of all four types.7879## Migration: Creating a Table8081Migrations live in `src/Acme/Bundle/DemoBundle/Migrations/Schema/`, one subdirectory per version (`v1_0/`, `v1_1/`). A `Migration` implementation builds the table in `up(Schema $schema, QueryBag $queries)` — worked example, including the ownership columns a USER/BUSINESS_UNIT entity needs, in `references/v6.1.md`.8283## Key Pitfalls84851. **Missing organization field on USER/BUSINESS_UNIT ownership** — Access control fails silently862. **Using old `@ORM\` annotations instead of `#[ORM\...]` attributes** — Doctrine won't recognize them in v6.1873. **Enum codes over 21 characters** — Oro uses them to generate table names; exceeding the limit causes silent failures884. **Expecting admin-UI field delete to remove the column** — it only soft-deletes (config `state = Deleted`, column kept), which blocks re-creating a field with the same name; see `references/removing-extend-fields.md`8990## See Also9192- `references/ownership-types.md` — all four ownership types and their field requirements93- `references/entity-patterns.md` — enum entities, ConfigField, extending core entities, repositories, commands94- `references/v6.1.md` — v6.1 specifics, the migration example, common failures95- `references/removing-extend-fields.md` — RemoveFieldQuery, attribute-family cleanup, POST_UP listeners, multi-host caveats