inheritance-mapping.rst 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. Inheritance Mapping
  2. ===================
  3. Mapped Superclasses
  4. -------------------
  5. A mapped superclass is an abstract or concrete class that provides
  6. persistent entity state and mapping information for its subclasses,
  7. but which is not itself an entity. Typically, the purpose of such a
  8. mapped superclass is to define state and mapping information that
  9. is common to multiple entity classes.
  10. Mapped superclasses, just as regular, non-mapped classes, can
  11. appear in the middle of an otherwise mapped inheritance hierarchy
  12. (through Single Table Inheritance or Class Table Inheritance).
  13. .. note::
  14. A mapped superclass cannot be an entity, it is not query-able and
  15. persistent relationships defined by a mapped superclass must be
  16. unidirectional (with an owning side only). This means that One-To-Many
  17. associations are not possible on a mapped superclass at all.
  18. Furthermore Many-To-Many associations are only possible if the
  19. mapped superclass is only used in exactly one entity at the moment.
  20. For further support of inheritance, the single or
  21. joined table inheritance features have to be used.
  22. Example:
  23. .. code-block:: php
  24. <?php
  25. /** @MappedSuperclass */
  26. class MappedSuperclassBase
  27. {
  28. /** @Column(type="integer") */
  29. protected $mapped1;
  30. /** @Column(type="string") */
  31. protected $mapped2;
  32. /**
  33. * @OneToOne(targetEntity="MappedSuperclassRelated1")
  34. * @JoinColumn(name="related1_id", referencedColumnName="id")
  35. */
  36. protected $mappedRelated1;
  37. // ... more fields and methods
  38. }
  39. /** @Entity */
  40. class EntitySubClass extends MappedSuperclassBase
  41. {
  42. /** @Id @Column(type="integer") */
  43. private $id;
  44. /** @Column(type="string") */
  45. private $name;
  46. // ... more fields and methods
  47. }
  48. The DDL for the corresponding database schema would look something
  49. like this (this is for SQLite):
  50. .. code-block:: sql
  51. CREATE TABLE EntitySubClass (mapped1 INTEGER NOT NULL, mapped2 TEXT NOT NULL, id INTEGER NOT NULL, name TEXT NOT NULL, related1_id INTEGER DEFAULT NULL, PRIMARY KEY(id))
  52. As you can see from this DDL snippet, there is only a single table
  53. for the entity subclass. All the mappings from the mapped
  54. superclass were inherited to the subclass as if they had been
  55. defined on that class directly.
  56. Single Table Inheritance
  57. ------------------------
  58. `Single Table Inheritance <http://martinfowler.com/eaaCatalog/singleTableInheritance.html>`_
  59. is an inheritance mapping strategy where all classes of a hierarchy
  60. are mapped to a single database table. In order to distinguish
  61. which row represents which type in the hierarchy a so-called
  62. discriminator column is used.
  63. Example:
  64. .. code-block:: php
  65. <?php
  66. namespace MyProject\Model;
  67. /**
  68. * @Entity
  69. * @InheritanceType("SINGLE_TABLE")
  70. * @DiscriminatorColumn(name="discr", type="string")
  71. * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
  72. */
  73. class Person
  74. {
  75. // ...
  76. }
  77. /**
  78. * @Entity
  79. */
  80. class Employee extends Person
  81. {
  82. // ...
  83. }
  84. Things to note:
  85. - The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap
  86. must be specified on the topmost class that is part of the mapped
  87. entity hierarchy.
  88. - The @DiscriminatorMap specifies which values of the
  89. discriminator column identify a row as being of a certain type. In
  90. the case above a value of "person" identifies a row as being of
  91. type ``Person`` and "employee" identifies a row as being of type
  92. ``Employee``.
  93. - The names of the classes in the discriminator map do not need to
  94. be fully qualified if the classes are contained in the same
  95. namespace as the entity class on which the discriminator map is
  96. applied.
  97. Design-time considerations
  98. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  99. This mapping approach works well when the type hierarchy is fairly
  100. simple and stable. Adding a new type to the hierarchy and adding
  101. fields to existing supertypes simply involves adding new columns to
  102. the table, though in large deployments this may have an adverse
  103. impact on the index and column layout inside the database.
  104. Performance impact
  105. ~~~~~~~~~~~~~~~~~~
  106. This strategy is very efficient for querying across all types in
  107. the hierarchy or for specific types. No table joins are required,
  108. only a WHERE clause listing the type identifiers. In particular,
  109. relationships involving types that employ this mapping strategy are
  110. very performant.
  111. There is a general performance consideration with Single Table
  112. Inheritance: If the target-entity of a many-to-one or one-to-one
  113. association is an STI entity, it is preferable for performance reasons that it
  114. be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
  115. Otherwise Doctrine *CANNOT* create proxy instances
  116. of this entity and will *ALWAYS* load the entity eagerly.
  117. SQL Schema considerations
  118. ~~~~~~~~~~~~~~~~~~~~~~~~~
  119. For Single-Table-Inheritance to work in scenarios where you are
  120. using either a legacy database schema or a self-written database
  121. schema you have to make sure that all columns that are not in the
  122. root entity but in any of the different sub-entities has to allows
  123. null values. Columns that have NOT NULL constraints have to be on
  124. the root entity of the single-table inheritance hierarchy.
  125. Class Table Inheritance
  126. -----------------------
  127. `Class Table Inheritance <http://martinfowler.com/eaaCatalog/classTableInheritance.html>`_
  128. is an inheritance mapping strategy where each class in a hierarchy
  129. is mapped to several tables: its own table and the tables of all
  130. parent classes. The table of a child class is linked to the table
  131. of a parent class through a foreign key constraint. Doctrine 2
  132. implements this strategy through the use of a discriminator column
  133. in the topmost table of the hierarchy because this is the easiest
  134. way to achieve polymorphic queries with Class Table Inheritance.
  135. Example:
  136. .. code-block:: php
  137. <?php
  138. namespace MyProject\Model;
  139. /**
  140. * @Entity
  141. * @InheritanceType("JOINED")
  142. * @DiscriminatorColumn(name="discr", type="string")
  143. * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
  144. */
  145. class Person
  146. {
  147. // ...
  148. }
  149. /** @Entity */
  150. class Employee extends Person
  151. {
  152. // ...
  153. }
  154. Things to note:
  155. - The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap
  156. must be specified on the topmost class that is part of the mapped
  157. entity hierarchy.
  158. - The @DiscriminatorMap specifies which values of the
  159. discriminator column identify a row as being of which type. In the
  160. case above a value of "person" identifies a row as being of type
  161. ``Person`` and "employee" identifies a row as being of type
  162. ``Employee``.
  163. - The names of the classes in the discriminator map do not need to
  164. be fully qualified if the classes are contained in the same
  165. namespace as the entity class on which the discriminator map is
  166. applied.
  167. .. note::
  168. When you do not use the SchemaTool to generate the
  169. required SQL you should know that deleting a class table
  170. inheritance makes use of the foreign key property
  171. ``ON DELETE CASCADE`` in all database implementations. A failure to
  172. implement this yourself will lead to dead rows in the database.
  173. Design-time considerations
  174. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  175. Introducing a new type to the hierarchy, at any level, simply
  176. involves interjecting a new table into the schema. Subtypes of that
  177. type will automatically join with that new type at runtime.
  178. Similarly, modifying any entity type in the hierarchy by adding,
  179. modifying or removing fields affects only the immediate table
  180. mapped to that type. This mapping strategy provides the greatest
  181. flexibility at design time, since changes to any type are always
  182. limited to that type's dedicated table.
  183. Performance impact
  184. ~~~~~~~~~~~~~~~~~~
  185. This strategy inherently requires multiple JOIN operations to
  186. perform just about any query which can have a negative impact on
  187. performance, especially with large tables and/or large hierarchies.
  188. When partial objects are allowed, either globally or on the
  189. specific query, then querying for any type will not cause the
  190. tables of subtypes to be OUTER JOINed which can increase
  191. performance but the resulting partial objects will not fully load
  192. themselves on access of any subtype fields, so accessing fields of
  193. subtypes after such a query is not safe.
  194. There is a general performance consideration with Class Table
  195. Inheritance: If the target-entity of a many-to-one or one-to-one
  196. association is a CTI entity, it is preferable for performance reasons that it
  197. be a leaf entity in the inheritance hierarchy, (ie. have no subclasses).
  198. Otherwise Doctrine *CANNOT* create proxy instances
  199. of this entity and will *ALWAYS* load the entity eagerly.
  200. SQL Schema considerations
  201. ~~~~~~~~~~~~~~~~~~~~~~~~~
  202. For each entity in the Class-Table Inheritance hierarchy all the
  203. mapped fields have to be columns on the table of this entity.
  204. Additionally each child table has to have an id column that matches
  205. the id column definition on the root table (except for any sequence
  206. or auto-increment details). Furthermore each child table has to
  207. have a foreign key pointing from the id column to the root table id
  208. column and cascading on delete.
  209. Overrides
  210. ---------
  211. Used to override a mapping for an entity field or relationship.
  212. May be applied to an entity that extends a mapped superclass
  213. to override a relationship or field mapping defined by the mapped superclass.
  214. Association Override
  215. ~~~~~~~~~~~~~~~~~~~~
  216. Override a mapping for an entity relationship.
  217. Could be used by an entity that extends a mapped superclass
  218. to override a relationship mapping defined by the mapped superclass.
  219. Example:
  220. .. configuration-block::
  221. .. code-block:: php
  222. <?php
  223. // user mapping
  224. namespace MyProject\Model;
  225. /**
  226. * @MappedSuperclass
  227. */
  228. class User
  229. {
  230. //other fields mapping
  231. /**
  232. * @ManyToMany(targetEntity="Group", inversedBy="users")
  233. * @JoinTable(name="users_groups",
  234. * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
  235. * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}
  236. * )
  237. */
  238. protected $groups;
  239. /**
  240. * @ManyToOne(targetEntity="Address")
  241. * @JoinColumn(name="address_id", referencedColumnName="id")
  242. */
  243. protected $address;
  244. }
  245. // admin mapping
  246. namespace MyProject\Model;
  247. /**
  248. * @Entity
  249. * @AssociationOverrides({
  250. * @AssociationOverride(name="groups",
  251. * joinTable=@JoinTable(
  252. * name="users_admingroups",
  253. * joinColumns=@JoinColumn(name="adminuser_id"),
  254. * inverseJoinColumns=@JoinColumn(name="admingroup_id")
  255. * )
  256. * ),
  257. * @AssociationOverride(name="address",
  258. * joinColumns=@JoinColumn(
  259. * name="adminaddress_id", referencedColumnName="id"
  260. * )
  261. * )
  262. * })
  263. */
  264. class Admin extends User
  265. {
  266. }
  267. .. code-block:: xml
  268. <!-- user mapping -->
  269. <doctrine-mapping>
  270. <mapped-superclass name="MyProject\Model\User">
  271. <!-- other fields mapping -->
  272. <many-to-many field="groups" target-entity="Group" inversed-by="users">
  273. <cascade>
  274. <cascade-persist/>
  275. <cascade-merge/>
  276. <cascade-detach/>
  277. </cascade>
  278. <join-table name="users_groups">
  279. <join-columns>
  280. <join-column name="user_id" referenced-column-name="id" />
  281. </join-columns>
  282. <inverse-join-columns>
  283. <join-column name="group_id" referenced-column-name="id" />
  284. </inverse-join-columns>
  285. </join-table>
  286. </many-to-many>
  287. </mapped-superclass>
  288. </doctrine-mapping>
  289. <!-- admin mapping -->
  290. <doctrine-mapping>
  291. <entity name="MyProject\Model\Admin">
  292. <association-overrides>
  293. <association-override name="groups">
  294. <join-table name="users_admingroups">
  295. <join-columns>
  296. <join-column name="adminuser_id"/>
  297. </join-columns>
  298. <inverse-join-columns>
  299. <join-column name="admingroup_id"/>
  300. </inverse-join-columns>
  301. </join-table>
  302. </association-override>
  303. <association-override name="address">
  304. <join-columns>
  305. <join-column name="adminaddress_id" referenced-column-name="id"/>
  306. </join-columns>
  307. </association-override>
  308. </association-overrides>
  309. </entity>
  310. </doctrine-mapping>
  311. .. code-block:: yaml
  312. # user mapping
  313. MyProject\Model\User:
  314. type: mappedSuperclass
  315. # other fields mapping
  316. manyToOne:
  317. address:
  318. targetEntity: Address
  319. joinColumn:
  320. name: address_id
  321. referencedColumnName: id
  322. cascade: [ persist, merge ]
  323. manyToMany:
  324. groups:
  325. targetEntity: Group
  326. joinTable:
  327. name: users_groups
  328. joinColumns:
  329. user_id:
  330. referencedColumnName: id
  331. inverseJoinColumns:
  332. group_id:
  333. referencedColumnName: id
  334. cascade: [ persist, merge, detach ]
  335. # admin mapping
  336. MyProject\Model\Admin:
  337. type: entity
  338. associationOverride:
  339. address:
  340. joinColumn:
  341. adminaddress_id:
  342. name: adminaddress_id
  343. referencedColumnName: id
  344. groups:
  345. joinTable:
  346. name: users_admingroups
  347. joinColumns:
  348. adminuser_id:
  349. referencedColumnName: id
  350. inverseJoinColumns:
  351. admingroup_id:
  352. referencedColumnName: id
  353. Things to note:
  354. - The "association override" specifies the overrides base on the property name.
  355. - This feature is available for all kind of associations. (OneToOne, OneToMany, ManyToOne, ManyToMany)
  356. - The association type *CANNOT* be changed.
  357. - The override could redefine the joinTables or joinColumns depending on the association type.
  358. Attribute Override
  359. ~~~~~~~~~~~~~~~~~~~~
  360. Override the mapping of a field.
  361. Could be used by an entity that extends a mapped superclass to override a field mapping defined by the mapped superclass.
  362. .. configuration-block::
  363. .. code-block:: php
  364. <?php
  365. // user mapping
  366. namespace MyProject\Model;
  367. /**
  368. * @MappedSuperclass
  369. */
  370. class User
  371. {
  372. /** @Id @GeneratedValue @Column(type="integer", name="user_id", length=150) */
  373. protected $id;
  374. /** @Column(name="user_name", nullable=true, unique=false, length=250) */
  375. protected $name;
  376. // other fields mapping
  377. }
  378. // guest mapping
  379. namespace MyProject\Model;
  380. /**
  381. * @Entity
  382. * @AttributeOverrides({
  383. * @AttributeOverride(name="id",
  384. * column=@Column(
  385. * name = "guest_id",
  386. * type = "integer",
  387. length = 140
  388. * )
  389. * ),
  390. * @AttributeOverride(name="name",
  391. * column=@Column(
  392. * name = "guest_name",
  393. * nullable = false,
  394. * unique = true,
  395. length = 240
  396. * )
  397. * )
  398. * })
  399. */
  400. class Guest extends User
  401. {
  402. }
  403. .. code-block:: xml
  404. <!-- user mapping -->
  405. <doctrine-mapping>
  406. <mapped-superclass name="MyProject\Model\User">
  407. <id name="id" type="integer" column="user_id" length="150">
  408. <generator strategy="AUTO"/>
  409. </id>
  410. <field name="name" column="user_name" type="string" length="250" nullable="true" unique="false" />
  411. <many-to-one field="address" target-entity="Address">
  412. <cascade>
  413. <cascade-persist/>
  414. <cascade-merge/>
  415. </cascade>
  416. <join-column name="address_id" referenced-column-name="id"/>
  417. </many-to-one>
  418. <!-- other fields mapping -->
  419. </mapped-superclass>
  420. </doctrine-mapping>
  421. <!-- admin mapping -->
  422. <doctrine-mapping>
  423. <entity name="MyProject\Model\Guest">
  424. <attribute-overrides>
  425. <attribute-override name="id">
  426. <field column="guest_id" length="140"/>
  427. </attribute-override>
  428. <attribute-override name="name">
  429. <field column="guest_name" type="string" length="240" nullable="false" unique="true" />
  430. </attribute-override>
  431. </attribute-overrides>
  432. </entity>
  433. </doctrine-mapping>
  434. .. code-block:: yaml
  435. # user mapping
  436. MyProject\Model\User:
  437. type: mappedSuperclass
  438. id:
  439. id:
  440. type: integer
  441. column: user_id
  442. length: 150
  443. generator:
  444. strategy: AUTO
  445. fields:
  446. name:
  447. type: string
  448. column: user_name
  449. length: 250
  450. nullable: true
  451. unique: false
  452. #other fields mapping
  453. # guest mapping
  454. MyProject\Model\Guest:
  455. type: entity
  456. attributeOverride:
  457. id:
  458. column: guest_id
  459. type: integer
  460. length: 140
  461. name:
  462. column: guest_name
  463. type: string
  464. length: 240
  465. nullable: false
  466. unique: true
  467. Things to note:
  468. - The "attribute override" specifies the overrides base on the property name.
  469. - The column type *CANNOT* be changed. if the column type is not equals you got a ``MappingException``
  470. - The override can redefine all the column except the type.