ClassMetadataInfo.php 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM\Mapping;
  20. use Doctrine\DBAL\Types\Type;
  21. use ReflectionClass;
  22. use Doctrine\Common\Persistence\Mapping\ClassMetadata;
  23. /**
  24. * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
  25. * of an entity and it's associations.
  26. *
  27. * Once populated, ClassMetadata instances are usually cached in a serialized form.
  28. *
  29. * <b>IMPORTANT NOTE:</b>
  30. *
  31. * The fields of this class are only public for 2 reasons:
  32. * 1) To allow fast READ access.
  33. * 2) To drastically reduce the size of a serialized instance (private/protected members
  34. * get the whole class name, namespace inclusive, prepended to every property in
  35. * the serialized representation).
  36. *
  37. * @author Roman Borschel <roman@code-factory.org>
  38. * @author Jonathan H. Wage <jonwage@gmail.com>
  39. * @since 2.0
  40. */
  41. class ClassMetadataInfo implements ClassMetadata
  42. {
  43. /* The inheritance mapping types */
  44. /**
  45. * NONE means the class does not participate in an inheritance hierarchy
  46. * and therefore does not need an inheritance mapping type.
  47. */
  48. const INHERITANCE_TYPE_NONE = 1;
  49. /**
  50. * JOINED means the class will be persisted according to the rules of
  51. * <tt>Class Table Inheritance</tt>.
  52. */
  53. const INHERITANCE_TYPE_JOINED = 2;
  54. /**
  55. * SINGLE_TABLE means the class will be persisted according to the rules of
  56. * <tt>Single Table Inheritance</tt>.
  57. */
  58. const INHERITANCE_TYPE_SINGLE_TABLE = 3;
  59. /**
  60. * TABLE_PER_CLASS means the class will be persisted according to the rules
  61. * of <tt>Concrete Table Inheritance</tt>.
  62. */
  63. const INHERITANCE_TYPE_TABLE_PER_CLASS = 4;
  64. /* The Id generator types. */
  65. /**
  66. * AUTO means the generator type will depend on what the used platform prefers.
  67. * Offers full portability.
  68. */
  69. const GENERATOR_TYPE_AUTO = 1;
  70. /**
  71. * SEQUENCE means a separate sequence object will be used. Platforms that do
  72. * not have native sequence support may emulate it. Full portability is currently
  73. * not guaranteed.
  74. */
  75. const GENERATOR_TYPE_SEQUENCE = 2;
  76. /**
  77. * TABLE means a separate table is used for id generation.
  78. * Offers full portability.
  79. */
  80. const GENERATOR_TYPE_TABLE = 3;
  81. /**
  82. * IDENTITY means an identity column is used for id generation. The database
  83. * will fill in the id column on insertion. Platforms that do not support
  84. * native identity columns may emulate them. Full portability is currently
  85. * not guaranteed.
  86. */
  87. const GENERATOR_TYPE_IDENTITY = 4;
  88. /**
  89. * NONE means the class does not have a generated id. That means the class
  90. * must have a natural, manually assigned id.
  91. */
  92. const GENERATOR_TYPE_NONE = 5;
  93. /**
  94. * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
  95. * by doing a property-by-property comparison with the original data. This will
  96. * be done for all entities that are in MANAGED state at commit-time.
  97. *
  98. * This is the default change tracking policy.
  99. */
  100. const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
  101. /**
  102. * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
  103. * by doing a property-by-property comparison with the original data. This will
  104. * be done only for entities that were explicitly saved (through persist() or a cascade).
  105. */
  106. const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
  107. /**
  108. * NOTIFY means that Doctrine relies on the entities sending out notifications
  109. * when their properties change. Such entity classes must implement
  110. * the <tt>NotifyPropertyChanged</tt> interface.
  111. */
  112. const CHANGETRACKING_NOTIFY = 3;
  113. /**
  114. * Specifies that an association is to be fetched when it is first accessed.
  115. */
  116. const FETCH_LAZY = 2;
  117. /**
  118. * Specifies that an association is to be fetched when the owner of the
  119. * association is fetched.
  120. */
  121. const FETCH_EAGER = 3;
  122. /**
  123. * Specifies that an association is to be fetched lazy (on first access) and that
  124. * commands such as Collection#count, Collection#slice are issued directly against
  125. * the database if the collection is not yet initialized.
  126. */
  127. const FETCH_EXTRA_LAZY = 4;
  128. /**
  129. * Identifies a one-to-one association.
  130. */
  131. const ONE_TO_ONE = 1;
  132. /**
  133. * Identifies a many-to-one association.
  134. */
  135. const MANY_TO_ONE = 2;
  136. /**
  137. * Identifies a one-to-many association.
  138. */
  139. const ONE_TO_MANY = 4;
  140. /**
  141. * Identifies a many-to-many association.
  142. */
  143. const MANY_TO_MANY = 8;
  144. /**
  145. * Combined bitmask for to-one (single-valued) associations.
  146. */
  147. const TO_ONE = 3;
  148. /**
  149. * Combined bitmask for to-many (collection-valued) associations.
  150. */
  151. const TO_MANY = 12;
  152. /**
  153. * READ-ONLY: The name of the entity class.
  154. */
  155. public $name;
  156. /**
  157. * READ-ONLY: The namespace the entity class is contained in.
  158. *
  159. * @var string
  160. * @todo Not really needed. Usage could be localized.
  161. */
  162. public $namespace;
  163. /**
  164. * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance
  165. * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
  166. * as {@link $entityName}.
  167. *
  168. * @var string
  169. */
  170. public $rootEntityName;
  171. /**
  172. * The name of the custom repository class used for the entity class.
  173. * (Optional).
  174. *
  175. * @var string
  176. */
  177. public $customRepositoryClassName;
  178. /**
  179. * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
  180. *
  181. * @var boolean
  182. */
  183. public $isMappedSuperclass = false;
  184. /**
  185. * READ-ONLY: The names of the parent classes (ancestors).
  186. *
  187. * @var array
  188. */
  189. public $parentClasses = array();
  190. /**
  191. * READ-ONLY: The names of all subclasses (descendants).
  192. *
  193. * @var array
  194. */
  195. public $subClasses = array();
  196. /**
  197. * READ-ONLY: The named queries allowed to be called directly from Repository.
  198. *
  199. * @var array
  200. */
  201. public $namedQueries = array();
  202. /**
  203. * READ-ONLY: The field names of all fields that are part of the identifier/primary key
  204. * of the mapped entity class.
  205. *
  206. * @var array
  207. */
  208. public $identifier = array();
  209. /**
  210. * READ-ONLY: The inheritance mapping type used by the class.
  211. *
  212. * @var integer
  213. */
  214. public $inheritanceType = self::INHERITANCE_TYPE_NONE;
  215. /**
  216. * READ-ONLY: The Id generator type used by the class.
  217. *
  218. * @var string
  219. */
  220. public $generatorType = self::GENERATOR_TYPE_NONE;
  221. /**
  222. * READ-ONLY: The field mappings of the class.
  223. * Keys are field names and values are mapping definitions.
  224. *
  225. * The mapping definition array has the following values:
  226. *
  227. * - <b>fieldName</b> (string)
  228. * The name of the field in the Entity.
  229. *
  230. * - <b>type</b> (string)
  231. * The type name of the mapped field. Can be one of Doctrine's mapping types
  232. * or a custom mapping type.
  233. *
  234. * - <b>columnName</b> (string, optional)
  235. * The column name. Optional. Defaults to the field name.
  236. *
  237. * - <b>length</b> (integer, optional)
  238. * The database length of the column. Optional. Default value taken from
  239. * the type.
  240. *
  241. * - <b>id</b> (boolean, optional)
  242. * Marks the field as the primary key of the entity. Multiple fields of an
  243. * entity can have the id attribute, forming a composite key.
  244. *
  245. * - <b>nullable</b> (boolean, optional)
  246. * Whether the column is nullable. Defaults to FALSE.
  247. *
  248. * - <b>columnDefinition</b> (string, optional, schema-only)
  249. * The SQL fragment that is used when generating the DDL for the column.
  250. *
  251. * - <b>precision</b> (integer, optional, schema-only)
  252. * The precision of a decimal column. Only valid if the column type is decimal.
  253. *
  254. * - <b>scale</b> (integer, optional, schema-only)
  255. * The scale of a decimal column. Only valid if the column type is decimal.
  256. *
  257. [* - <b>'unique'] (string, optional, schema-only)</b>
  258. * Whether a unique constraint should be generated for the column.
  259. *
  260. * @var array
  261. */
  262. public $fieldMappings = array();
  263. /**
  264. * READ-ONLY: An array of field names. Used to look up field names from column names.
  265. * Keys are column names and values are field names.
  266. * This is the reverse lookup map of $_columnNames.
  267. *
  268. * @var array
  269. */
  270. public $fieldNames = array();
  271. /**
  272. * READ-ONLY: A map of field names to column names. Keys are field names and values column names.
  273. * Used to look up column names from field names.
  274. * This is the reverse lookup map of $_fieldNames.
  275. *
  276. * @var array
  277. * @todo We could get rid of this array by just using $fieldMappings[$fieldName]['columnName'].
  278. */
  279. public $columnNames = array();
  280. /**
  281. * READ-ONLY: The discriminator value of this class.
  282. *
  283. * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
  284. * where a discriminator column is used.</b>
  285. *
  286. * @var mixed
  287. * @see discriminatorColumn
  288. */
  289. public $discriminatorValue;
  290. /**
  291. * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
  292. *
  293. * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
  294. * where a discriminator column is used.</b>
  295. *
  296. * @var mixed
  297. * @see discriminatorColumn
  298. */
  299. public $discriminatorMap = array();
  300. /**
  301. * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
  302. * inheritance mappings.
  303. *
  304. * @var array
  305. */
  306. public $discriminatorColumn;
  307. /**
  308. * READ-ONLY: The primary table definition. The definition is an array with the
  309. * following entries:
  310. *
  311. * name => <tableName>
  312. * schema => <schemaName>
  313. * indexes => array
  314. * uniqueConstraints => array
  315. *
  316. * @var array
  317. */
  318. public $table;
  319. /**
  320. * READ-ONLY: The registered lifecycle callbacks for entities of this class.
  321. *
  322. * @var array
  323. */
  324. public $lifecycleCallbacks = array();
  325. /**
  326. * READ-ONLY: The association mappings of this class.
  327. *
  328. * The mapping definition array supports the following keys:
  329. *
  330. * - <b>fieldName</b> (string)
  331. * The name of the field in the entity the association is mapped to.
  332. *
  333. * - <b>targetEntity</b> (string)
  334. * The class name of the target entity. If it is fully-qualified it is used as is.
  335. * If it is a simple, unqualified class name the namespace is assumed to be the same
  336. * as the namespace of the source entity.
  337. *
  338. * - <b>mappedBy</b> (string, required for bidirectional associations)
  339. * The name of the field that completes the bidirectional association on the owning side.
  340. * This key must be specified on the inverse side of a bidirectional association.
  341. *
  342. * - <b>inversedBy</b> (string, required for bidirectional associations)
  343. * The name of the field that completes the bidirectional association on the inverse side.
  344. * This key must be specified on the owning side of a bidirectional association.
  345. *
  346. * - <b>cascade</b> (array, optional)
  347. * The names of persistence operations to cascade on the association. The set of possible
  348. * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
  349. *
  350. * - <b>orderBy</b> (array, one-to-many/many-to-many only)
  351. * A map of field names (of the target entity) to sorting directions (ASC/DESC).
  352. * Example: array('priority' => 'desc')
  353. *
  354. * - <b>fetch</b> (integer, optional)
  355. * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
  356. * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
  357. *
  358. * - <b>joinTable</b> (array, optional, many-to-many only)
  359. * Specification of the join table and its join columns (foreign keys).
  360. * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
  361. * through a join table by simply mapping the association as many-to-many with a unique
  362. * constraint on the join table.
  363. *
  364. * - <b>indexBy</b> (string, optional, to-many only)
  365. * Specification of a field on target-entity that is used to index the collection by.
  366. * This field HAS to be either the primary key or a unique column. Otherwise the collection
  367. * does not contain all the entities that are actually related.
  368. *
  369. * A join table definition has the following structure:
  370. * <pre>
  371. * array(
  372. * 'name' => <join table name>,
  373. * 'joinColumns' => array(<join column mapping from join table to source table>),
  374. * 'inverseJoinColumns' => array(<join column mapping from join table to target table>)
  375. * )
  376. * </pre>
  377. *
  378. *
  379. * @var array
  380. */
  381. public $associationMappings = array();
  382. /**
  383. * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
  384. *
  385. * @var boolean
  386. */
  387. public $isIdentifierComposite = false;
  388. /**
  389. * READ-ONLY: Flag indicating wheather the identifier/primary key contains at least one foreign key association.
  390. *
  391. * This flag is necessary because some code blocks require special treatment of this cases.
  392. *
  393. * @var boolean
  394. */
  395. public $containsForeignIdentifier = false;
  396. /**
  397. * READ-ONLY: The ID generator used for generating IDs for this class.
  398. *
  399. * @var AbstractIdGenerator
  400. * @todo Remove!
  401. */
  402. public $idGenerator;
  403. /**
  404. * READ-ONLY: The definition of the sequence generator of this class. Only used for the
  405. * SEQUENCE generation strategy.
  406. *
  407. * The definition has the following structure:
  408. * <code>
  409. * array(
  410. * 'sequenceName' => 'name',
  411. * 'allocationSize' => 20,
  412. * 'initialValue' => 1
  413. * )
  414. * </code>
  415. *
  416. * @var array
  417. * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
  418. */
  419. public $sequenceGeneratorDefinition;
  420. /**
  421. * READ-ONLY: The definition of the table generator of this class. Only used for the
  422. * TABLE generation strategy.
  423. *
  424. * @var array
  425. * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
  426. */
  427. public $tableGeneratorDefinition;
  428. /**
  429. * READ-ONLY: The policy used for change-tracking on entities of this class.
  430. *
  431. * @var integer
  432. */
  433. public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
  434. /**
  435. * READ-ONLY: A flag for whether or not instances of this class are to be versioned
  436. * with optimistic locking.
  437. *
  438. * @var boolean $isVersioned
  439. */
  440. public $isVersioned;
  441. /**
  442. * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
  443. *
  444. * @var mixed $versionField
  445. */
  446. public $versionField;
  447. /**
  448. * The ReflectionClass instance of the mapped class.
  449. *
  450. * @var ReflectionClass
  451. */
  452. public $reflClass;
  453. /**
  454. * Is this entity marked as "read-only"?
  455. *
  456. * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
  457. * optimization for entities that are immutable, either in your domain or through the relation database
  458. * (coming from a view, or a history table for example).
  459. *
  460. * @var bool
  461. */
  462. public $isReadOnly = false;
  463. /**
  464. * The ReflectionProperty instances of the mapped class.
  465. *
  466. * @var array
  467. */
  468. public $reflFields = array();
  469. /**
  470. * The prototype from which new instances of the mapped class are created.
  471. *
  472. * @var object
  473. */
  474. private $_prototype;
  475. /**
  476. * Initializes a new ClassMetadata instance that will hold the object-relational mapping
  477. * metadata of the class with the given name.
  478. *
  479. * @param string $entityName The name of the entity class the new instance is used for.
  480. */
  481. public function __construct($entityName)
  482. {
  483. $this->name = $entityName;
  484. $this->rootEntityName = $entityName;
  485. }
  486. /**
  487. * Gets the ReflectionPropertys of the mapped class.
  488. *
  489. * @return array An array of ReflectionProperty instances.
  490. */
  491. public function getReflectionProperties()
  492. {
  493. return $this->reflFields;
  494. }
  495. /**
  496. * Gets a ReflectionProperty for a specific field of the mapped class.
  497. *
  498. * @param string $name
  499. * @return ReflectionProperty
  500. */
  501. public function getReflectionProperty($name)
  502. {
  503. return $this->reflFields[$name];
  504. }
  505. /**
  506. * Gets the ReflectionProperty for the single identifier field.
  507. *
  508. * @return ReflectionProperty
  509. * @throws BadMethodCallException If the class has a composite identifier.
  510. */
  511. public function getSingleIdReflectionProperty()
  512. {
  513. if ($this->isIdentifierComposite) {
  514. throw new \BadMethodCallException("Class " . $this->name . " has a composite identifier.");
  515. }
  516. return $this->reflFields[$this->identifier[0]];
  517. }
  518. /**
  519. * Extracts the identifier values of an entity of this class.
  520. *
  521. * For composite identifiers, the identifier values are returned as an array
  522. * with the same order as the field order in {@link identifier}.
  523. *
  524. * @param object $entity
  525. * @return array
  526. */
  527. public function getIdentifierValues($entity)
  528. {
  529. if ($this->isIdentifierComposite) {
  530. $id = array();
  531. foreach ($this->identifier as $idField) {
  532. $value = $this->reflFields[$idField]->getValue($entity);
  533. if ($value !== null) {
  534. $id[$idField] = $value;
  535. }
  536. }
  537. return $id;
  538. }
  539. $value = $this->reflFields[$this->identifier[0]]->getValue($entity);
  540. if ($value !== null) {
  541. return array($this->identifier[0] => $value);
  542. }
  543. return array();
  544. }
  545. /**
  546. * Populates the entity identifier of an entity.
  547. *
  548. * @param object $entity
  549. * @param mixed $id
  550. * @todo Rename to assignIdentifier()
  551. */
  552. public function setIdentifierValues($entity, array $id)
  553. {
  554. foreach ($id as $idField => $idValue) {
  555. $this->reflFields[$idField]->setValue($entity, $idValue);
  556. }
  557. }
  558. /**
  559. * Sets the specified field to the specified value on the given entity.
  560. *
  561. * @param object $entity
  562. * @param string $field
  563. * @param mixed $value
  564. */
  565. public function setFieldValue($entity, $field, $value)
  566. {
  567. $this->reflFields[$field]->setValue($entity, $value);
  568. }
  569. /**
  570. * Gets the specified field's value off the given entity.
  571. *
  572. * @param object $entity
  573. * @param string $field
  574. */
  575. public function getFieldValue($entity, $field)
  576. {
  577. return $this->reflFields[$field]->getValue($entity);
  578. }
  579. /**
  580. * Creates a string representation of this instance.
  581. *
  582. * @return string The string representation of this instance.
  583. * @todo Construct meaningful string representation.
  584. */
  585. public function __toString()
  586. {
  587. return __CLASS__ . '@' . spl_object_hash($this);
  588. }
  589. /**
  590. * Determines which fields get serialized.
  591. *
  592. * It is only serialized what is necessary for best unserialization performance.
  593. * That means any metadata properties that are not set or empty or simply have
  594. * their default value are NOT serialized.
  595. *
  596. * Parts that are also NOT serialized because they can not be properly unserialized:
  597. * - reflClass (ReflectionClass)
  598. * - reflFields (ReflectionProperty array)
  599. *
  600. * @return array The names of all the fields that should be serialized.
  601. */
  602. public function __sleep()
  603. {
  604. // This metadata is always serialized/cached.
  605. $serialized = array(
  606. 'associationMappings',
  607. 'columnNames', //TODO: Not really needed. Can use fieldMappings[$fieldName]['columnName']
  608. 'fieldMappings',
  609. 'fieldNames',
  610. 'identifier',
  611. 'isIdentifierComposite', // TODO: REMOVE
  612. 'name',
  613. 'namespace', // TODO: REMOVE
  614. 'table',
  615. 'rootEntityName',
  616. 'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
  617. );
  618. // The rest of the metadata is only serialized if necessary.
  619. if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
  620. $serialized[] = 'changeTrackingPolicy';
  621. }
  622. if ($this->customRepositoryClassName) {
  623. $serialized[] = 'customRepositoryClassName';
  624. }
  625. if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
  626. $serialized[] = 'inheritanceType';
  627. $serialized[] = 'discriminatorColumn';
  628. $serialized[] = 'discriminatorValue';
  629. $serialized[] = 'discriminatorMap';
  630. $serialized[] = 'parentClasses';
  631. $serialized[] = 'subClasses';
  632. }
  633. if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
  634. $serialized[] = 'generatorType';
  635. if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
  636. $serialized[] = 'sequenceGeneratorDefinition';
  637. }
  638. }
  639. if ($this->isMappedSuperclass) {
  640. $serialized[] = 'isMappedSuperclass';
  641. }
  642. if ($this->containsForeignIdentifier) {
  643. $serialized[] = 'containsForeignIdentifier';
  644. }
  645. if ($this->isVersioned) {
  646. $serialized[] = 'isVersioned';
  647. $serialized[] = 'versionField';
  648. }
  649. if ($this->lifecycleCallbacks) {
  650. $serialized[] = 'lifecycleCallbacks';
  651. }
  652. if ($this->namedQueries) {
  653. $serialized[] = 'namedQueries';
  654. }
  655. if ($this->isReadOnly) {
  656. $serialized[] = 'isReadOnly';
  657. }
  658. return $serialized;
  659. }
  660. /**
  661. * Creates a new instance of the mapped class, without invoking the constructor.
  662. *
  663. * @return object
  664. */
  665. public function newInstance()
  666. {
  667. if ($this->_prototype === null) {
  668. $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
  669. }
  670. return clone $this->_prototype;
  671. }
  672. /**
  673. * Restores some state that can not be serialized/unserialized.
  674. *
  675. * @param ReflectionService $reflService
  676. * @return void
  677. */
  678. public function wakeupReflection($reflService)
  679. {
  680. // Restore ReflectionClass and properties
  681. $this->reflClass = $reflService->getClass($this->name);
  682. foreach ($this->fieldMappings as $field => $mapping) {
  683. $this->reflFields[$field] = isset($mapping['declared'])
  684. ? $reflService->getAccessibleProperty($mapping['declared'], $field)
  685. : $reflService->getAccessibleProperty($this->name, $field);
  686. }
  687. foreach ($this->associationMappings as $field => $mapping) {
  688. $this->reflFields[$field] = isset($mapping['declared'])
  689. ? $reflService->getAccessibleProperty($mapping['declared'], $field)
  690. : $reflService->getAccessibleProperty($this->name, $field);
  691. }
  692. }
  693. /**
  694. * Initializes a new ClassMetadata instance that will hold the object-relational mapping
  695. * metadata of the class with the given name.
  696. *
  697. * @param string $entityName The name of the entity class the new instance is used for.
  698. */
  699. public function initializeReflection($reflService)
  700. {
  701. $this->reflClass = $reflService->getClass($this->name);
  702. $this->namespace = $reflService->getClassNamespace($this->name);
  703. $this->table['name'] = $reflService->getClassShortName($this->name);
  704. if ($this->reflClass) {
  705. $this->name = $this->rootEntityName = $this->reflClass->getName();
  706. }
  707. }
  708. /**
  709. * Validate Identifier
  710. *
  711. * @return void
  712. */
  713. public function validateIdentifier()
  714. {
  715. // Verify & complete identifier mapping
  716. if ( ! $this->identifier && ! $this->isMappedSuperclass) {
  717. throw MappingException::identifierRequired($this->name);
  718. }
  719. if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
  720. throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
  721. }
  722. }
  723. /**
  724. * Validate association targets actually exist.
  725. *
  726. * @return void
  727. */
  728. public function validateAssocations()
  729. {
  730. foreach ($this->associationMappings as $field => $mapping) {
  731. if ( ! \Doctrine\Common\ClassLoader::classExists($mapping['targetEntity']) ) {
  732. throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
  733. }
  734. }
  735. }
  736. /**
  737. * Validate lifecycle callbacks
  738. *
  739. * @param ReflectionService $reflService
  740. * @return void
  741. */
  742. public function validateLifecycleCallbacks($reflService)
  743. {
  744. foreach ($this->lifecycleCallbacks as $event => $callbacks) {
  745. foreach ($callbacks as $callbackFuncName) {
  746. if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
  747. throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
  748. }
  749. }
  750. }
  751. }
  752. /**
  753. * Gets the ReflectionClass instance of the mapped class.
  754. *
  755. * @return ReflectionClass
  756. */
  757. public function getReflectionClass()
  758. {
  759. return $this->reflClass;
  760. }
  761. /**
  762. * Sets the change tracking policy used by this class.
  763. *
  764. * @param integer $policy
  765. */
  766. public function setChangeTrackingPolicy($policy)
  767. {
  768. $this->changeTrackingPolicy = $policy;
  769. }
  770. /**
  771. * Whether the change tracking policy of this class is "deferred explicit".
  772. *
  773. * @return boolean
  774. */
  775. public function isChangeTrackingDeferredExplicit()
  776. {
  777. return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
  778. }
  779. /**
  780. * Whether the change tracking policy of this class is "deferred implicit".
  781. *
  782. * @return boolean
  783. */
  784. public function isChangeTrackingDeferredImplicit()
  785. {
  786. return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
  787. }
  788. /**
  789. * Whether the change tracking policy of this class is "notify".
  790. *
  791. * @return boolean
  792. */
  793. public function isChangeTrackingNotify()
  794. {
  795. return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
  796. }
  797. /**
  798. * Checks whether a field is part of the identifier/primary key field(s).
  799. *
  800. * @param string $fieldName The field name
  801. * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
  802. * FALSE otherwise.
  803. */
  804. public function isIdentifier($fieldName)
  805. {
  806. if ( ! $this->isIdentifierComposite) {
  807. return $fieldName === $this->identifier[0];
  808. }
  809. return in_array($fieldName, $this->identifier);
  810. }
  811. /**
  812. * Check if the field is unique.
  813. *
  814. * @param string $fieldName The field name
  815. * @return boolean TRUE if the field is unique, FALSE otherwise.
  816. */
  817. public function isUniqueField($fieldName)
  818. {
  819. $mapping = $this->getFieldMapping($fieldName);
  820. if ($mapping !== false) {
  821. return isset($mapping['unique']) && $mapping['unique'] == true;
  822. }
  823. return false;
  824. }
  825. /**
  826. * Check if the field is not null.
  827. *
  828. * @param string $fieldName The field name
  829. * @return boolean TRUE if the field is not null, FALSE otherwise.
  830. */
  831. public function isNullable($fieldName)
  832. {
  833. $mapping = $this->getFieldMapping($fieldName);
  834. if ($mapping !== false) {
  835. return isset($mapping['nullable']) && $mapping['nullable'] == true;
  836. }
  837. return false;
  838. }
  839. /**
  840. * Gets a column name for a field name.
  841. * If the column name for the field cannot be found, the given field name
  842. * is returned.
  843. *
  844. * @param string $fieldName The field name.
  845. * @return string The column name.
  846. */
  847. public function getColumnName($fieldName)
  848. {
  849. return isset($this->columnNames[$fieldName]) ?
  850. $this->columnNames[$fieldName] : $fieldName;
  851. }
  852. /**
  853. * Gets the mapping of a (regular) field that holds some data but not a
  854. * reference to another object.
  855. *
  856. * @param string $fieldName The field name.
  857. * @return array The field mapping.
  858. */
  859. public function getFieldMapping($fieldName)
  860. {
  861. if ( ! isset($this->fieldMappings[$fieldName])) {
  862. throw MappingException::mappingNotFound($this->name, $fieldName);
  863. }
  864. return $this->fieldMappings[$fieldName];
  865. }
  866. /**
  867. * Gets the mapping of an association.
  868. *
  869. * @see ClassMetadataInfo::$associationMappings
  870. * @param string $fieldName The field name that represents the association in
  871. * the object model.
  872. * @return array The mapping.
  873. */
  874. public function getAssociationMapping($fieldName)
  875. {
  876. if ( ! isset($this->associationMappings[$fieldName])) {
  877. throw MappingException::mappingNotFound($this->name, $fieldName);
  878. }
  879. return $this->associationMappings[$fieldName];
  880. }
  881. /**
  882. * Gets all association mappings of the class.
  883. *
  884. * @return array
  885. */
  886. public function getAssociationMappings()
  887. {
  888. return $this->associationMappings;
  889. }
  890. /**
  891. * Gets the field name for a column name.
  892. * If no field name can be found the column name is returned.
  893. *
  894. * @param string $columnName column name
  895. * @return string column alias
  896. */
  897. public function getFieldName($columnName)
  898. {
  899. return isset($this->fieldNames[$columnName]) ?
  900. $this->fieldNames[$columnName] : $columnName;
  901. }
  902. /**
  903. * Gets the named query.
  904. *
  905. * @see ClassMetadataInfo::$namedQueries
  906. * @throws MappingException
  907. * @param string $queryName The query name
  908. * @return string
  909. */
  910. public function getNamedQuery($queryName)
  911. {
  912. if ( ! isset($this->namedQueries[$queryName])) {
  913. throw MappingException::queryNotFound($this->name, $queryName);
  914. }
  915. return $this->namedQueries[$queryName]['dql'];
  916. }
  917. /**
  918. * Gets all named queries of the class.
  919. *
  920. * @return array
  921. */
  922. public function getNamedQueries()
  923. {
  924. return $this->namedQueries;
  925. }
  926. /**
  927. * Validates & completes the given field mapping.
  928. *
  929. * @param array $mapping The field mapping to validated & complete.
  930. * @return array The validated and completed field mapping.
  931. */
  932. protected function _validateAndCompleteFieldMapping(array &$mapping)
  933. {
  934. // Check mandatory fields
  935. if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
  936. throw MappingException::missingFieldName($this->name);
  937. }
  938. if ( ! isset($mapping['type'])) {
  939. // Default to string
  940. $mapping['type'] = 'string';
  941. }
  942. // Complete fieldName and columnName mapping
  943. if ( ! isset($mapping['columnName'])) {
  944. $mapping['columnName'] = $mapping['fieldName'];
  945. } else {
  946. if ($mapping['columnName'][0] == '`') {
  947. $mapping['columnName'] = trim($mapping['columnName'], '`');
  948. $mapping['quoted'] = true;
  949. }
  950. }
  951. $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
  952. if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
  953. throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
  954. }
  955. $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
  956. // Complete id mapping
  957. if (isset($mapping['id']) && $mapping['id'] === true) {
  958. if ($this->versionField == $mapping['fieldName']) {
  959. throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
  960. }
  961. if ( ! in_array($mapping['fieldName'], $this->identifier)) {
  962. $this->identifier[] = $mapping['fieldName'];
  963. }
  964. // Check for composite key
  965. if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
  966. $this->isIdentifierComposite = true;
  967. }
  968. }
  969. if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
  970. if (isset($mapping['id']) && $mapping['id'] === true) {
  971. throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
  972. }
  973. $mapping['requireSQLConversion'] = true;
  974. }
  975. }
  976. /**
  977. * Validates & completes the basic mapping information that is common to all
  978. * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
  979. *
  980. * @param array $mapping The mapping.
  981. * @return array The updated mapping.
  982. * @throws MappingException If something is wrong with the mapping.
  983. */
  984. protected function _validateAndCompleteAssociationMapping(array $mapping)
  985. {
  986. if ( ! isset($mapping['mappedBy'])) {
  987. $mapping['mappedBy'] = null;
  988. }
  989. if ( ! isset($mapping['inversedBy'])) {
  990. $mapping['inversedBy'] = null;
  991. }
  992. $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
  993. // unset optional indexBy attribute if its empty
  994. if (!isset($mapping['indexBy']) || !$mapping['indexBy']) {
  995. unset($mapping['indexBy']);
  996. }
  997. // If targetEntity is unqualified, assume it is in the same namespace as
  998. // the sourceEntity.
  999. $mapping['sourceEntity'] = $this->name;
  1000. if (isset($mapping['targetEntity'])) {
  1001. if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
  1002. $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
  1003. }
  1004. $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
  1005. }
  1006. if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
  1007. isset($mapping['orphanRemoval']) &&
  1008. $mapping['orphanRemoval'] == true) {
  1009. throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
  1010. }
  1011. // Complete id mapping
  1012. if (isset($mapping['id']) && $mapping['id'] === true) {
  1013. if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
  1014. throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
  1015. }
  1016. if ( ! in_array($mapping['fieldName'], $this->identifier)) {
  1017. if (count($mapping['joinColumns']) >= 2) {
  1018. throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
  1019. $mapping['targetEntity'], $this->name, $mapping['fieldName']
  1020. );
  1021. }
  1022. $this->identifier[] = $mapping['fieldName'];
  1023. $this->containsForeignIdentifier = true;
  1024. }
  1025. // Check for composite key
  1026. if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
  1027. $this->isIdentifierComposite = true;
  1028. }
  1029. }
  1030. // Mandatory attributes for both sides
  1031. // Mandatory: fieldName, targetEntity
  1032. if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
  1033. throw MappingException::missingFieldName($this->name);
  1034. }
  1035. if ( ! isset($mapping['targetEntity'])) {
  1036. throw MappingException::missingTargetEntity($mapping['fieldName']);
  1037. }
  1038. // Mandatory and optional attributes for either side
  1039. if ( ! $mapping['mappedBy']) {
  1040. if (isset($mapping['joinTable']) && $mapping['joinTable']) {
  1041. if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] == '`') {
  1042. $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
  1043. $mapping['joinTable']['quoted'] = true;
  1044. }
  1045. }
  1046. } else {
  1047. $mapping['isOwningSide'] = false;
  1048. }
  1049. if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
  1050. throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
  1051. }
  1052. // Fetch mode. Default fetch mode to LAZY, if not set.
  1053. if ( ! isset($mapping['fetch'])) {
  1054. $mapping['fetch'] = self::FETCH_LAZY;
  1055. }
  1056. // Cascades
  1057. $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
  1058. if (in_array('all', $cascades)) {
  1059. $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
  1060. }
  1061. $mapping['cascade'] = $cascades;
  1062. $mapping['isCascadeRemove'] = in_array('remove', $cascades);
  1063. $mapping['isCascadePersist'] = in_array('persist', $cascades);
  1064. $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
  1065. $mapping['isCascadeMerge'] = in_array('merge', $cascades);
  1066. $mapping['isCascadeDetach'] = in_array('detach', $cascades);
  1067. return $mapping;
  1068. }
  1069. /**
  1070. * Validates & completes a one-to-one association mapping.
  1071. *
  1072. * @param array $mapping The mapping to validate & complete.
  1073. * @return array The validated & completed mapping.
  1074. * @override
  1075. */
  1076. protected function _validateAndCompleteOneToOneMapping(array $mapping)
  1077. {
  1078. $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
  1079. if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
  1080. $mapping['isOwningSide'] = true;
  1081. }
  1082. if ($mapping['isOwningSide']) {
  1083. if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
  1084. // Apply default join column
  1085. $mapping['joinColumns'] = array(array(
  1086. 'name' => $mapping['fieldName'] . '_id',
  1087. 'referencedColumnName' => 'id'
  1088. ));
  1089. }
  1090. $uniqueContraintColumns = array();
  1091. foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
  1092. if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
  1093. if (count($mapping['joinColumns']) == 1) {
  1094. if (! isset($mapping['id']) || ! $mapping['id']) {
  1095. $joinColumn['unique'] = true;
  1096. }
  1097. } else {
  1098. $uniqueContraintColumns[] = $joinColumn['name'];
  1099. }
  1100. }
  1101. if (empty($joinColumn['name'])) {
  1102. $joinColumn['name'] = $mapping['fieldName'] . '_id';
  1103. }
  1104. if (empty($joinColumn['referencedColumnName'])) {
  1105. $joinColumn['referencedColumnName'] = 'id';
  1106. }
  1107. $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
  1108. $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
  1109. ? $joinColumn['fieldName'] : $joinColumn['name'];
  1110. }
  1111. if ($uniqueContraintColumns) {
  1112. if (!$this->table) {
  1113. throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
  1114. }
  1115. $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
  1116. 'columns' => $uniqueContraintColumns
  1117. );
  1118. }
  1119. $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
  1120. }
  1121. $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
  1122. $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
  1123. if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
  1124. throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
  1125. }
  1126. return $mapping;
  1127. }
  1128. /**
  1129. * Validates and completes the mapping.
  1130. *
  1131. * @param array $mapping The mapping to validate and complete.
  1132. * @return array The validated and completed mapping.
  1133. * @override
  1134. */
  1135. protected function _validateAndCompleteOneToManyMapping(array $mapping)
  1136. {
  1137. $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
  1138. // OneToMany-side MUST be inverse (must have mappedBy)
  1139. if ( ! isset($mapping['mappedBy'])) {
  1140. throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
  1141. }
  1142. $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
  1143. $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
  1144. if (isset($mapping['orderBy'])) {
  1145. if ( ! is_array($mapping['orderBy'])) {
  1146. throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
  1147. }
  1148. }
  1149. return $mapping;
  1150. }
  1151. protected function _validateAndCompleteManyToManyMapping(array $mapping)
  1152. {
  1153. $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
  1154. if ($mapping['isOwningSide']) {
  1155. if (strpos($mapping['sourceEntity'], '\\') !== false) {
  1156. $sourceShortName = strtolower(substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1));
  1157. } else {
  1158. $sourceShortName = strtolower($mapping['sourceEntity']);
  1159. }
  1160. if (strpos($mapping['targetEntity'], '\\') !== false) {
  1161. $targetShortName = strtolower(substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1));
  1162. } else {
  1163. $targetShortName = strtolower($mapping['targetEntity']);
  1164. }
  1165. // owning side MUST have a join table
  1166. if ( ! isset($mapping['joinTable']['name'])) {
  1167. $mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
  1168. }
  1169. if ( ! isset($mapping['joinTable']['joinColumns'])) {
  1170. $mapping['joinTable']['joinColumns'] = array(array(
  1171. 'name' => $sourceShortName . '_id',
  1172. 'referencedColumnName' => 'id',
  1173. 'onDelete' => 'CASCADE'));
  1174. }
  1175. if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
  1176. $mapping['joinTable']['inverseJoinColumns'] = array(array(
  1177. 'name' => $targetShortName . '_id',
  1178. 'referencedColumnName' => 'id',
  1179. 'onDelete' => 'CASCADE'));
  1180. }
  1181. foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
  1182. if (empty($joinColumn['name'])) {
  1183. $joinColumn['name'] = $sourceShortName . '_id';
  1184. }
  1185. if (empty($joinColumn['referencedColumnName'])) {
  1186. $joinColumn['referencedColumnName'] = 'id';
  1187. }
  1188. if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
  1189. $mapping['isOnDeleteCascade'] = true;
  1190. }
  1191. $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
  1192. $mapping['joinTableColumns'][] = $joinColumn['name'];
  1193. }
  1194. foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
  1195. if (empty($inverseJoinColumn['name'])) {
  1196. $inverseJoinColumn['name'] = $targetShortName . '_id';
  1197. }
  1198. if (empty($inverseJoinColumn['referencedColumnName'])) {
  1199. $inverseJoinColumn['referencedColumnName'] = 'id';
  1200. }
  1201. if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
  1202. $mapping['isOnDeleteCascade'] = true;
  1203. }
  1204. $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
  1205. $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
  1206. }
  1207. }
  1208. $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
  1209. if (isset($mapping['orderBy'])) {
  1210. if ( ! is_array($mapping['orderBy'])) {
  1211. throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
  1212. }
  1213. }
  1214. return $mapping;
  1215. }
  1216. /**
  1217. * Gets the identifier (primary key) field names of the class.
  1218. *
  1219. * @return mixed
  1220. */
  1221. public function getIdentifierFieldNames()
  1222. {
  1223. return $this->identifier;
  1224. }
  1225. /**
  1226. * Gets the name of the single id field. Note that this only works on
  1227. * entity classes that have a single-field pk.
  1228. *
  1229. * @return string
  1230. * @throws MappingException If the class has a composite primary key.
  1231. */
  1232. public function getSingleIdentifierFieldName()
  1233. {
  1234. if ($this->isIdentifierComposite) {
  1235. throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
  1236. }
  1237. return $this->identifier[0];
  1238. }
  1239. /**
  1240. * Gets the column name of the single id column. Note that this only works on
  1241. * entity classes that have a single-field pk.
  1242. *
  1243. * @return string
  1244. * @throws MappingException If the class has a composite primary key.
  1245. */
  1246. public function getSingleIdentifierColumnName()
  1247. {
  1248. return $this->getColumnName($this->getSingleIdentifierFieldName());
  1249. }
  1250. /**
  1251. * INTERNAL:
  1252. * Sets the mapped identifier/primary key fields of this class.
  1253. * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
  1254. *
  1255. * @param array $identifier
  1256. */
  1257. public function setIdentifier(array $identifier)
  1258. {
  1259. $this->identifier = $identifier;
  1260. $this->isIdentifierComposite = (count($this->identifier) > 1);
  1261. }
  1262. /**
  1263. * Gets the mapped identifier field of this class.
  1264. *
  1265. * @return string $identifier
  1266. */
  1267. public function getIdentifier()
  1268. {
  1269. return $this->identifier;
  1270. }
  1271. /**
  1272. * Checks whether the class has a (mapped) field with a certain name.
  1273. *
  1274. * @return boolean
  1275. */
  1276. public function hasField($fieldName)
  1277. {
  1278. return isset($this->fieldMappings[$fieldName]);
  1279. }
  1280. /**
  1281. * Gets an array containing all the column names.
  1282. *
  1283. * @return array
  1284. */
  1285. public function getColumnNames(array $fieldNames = null)
  1286. {
  1287. if ($fieldNames === null) {
  1288. return array_keys($this->fieldNames);
  1289. } else {
  1290. $columnNames = array();
  1291. foreach ($fieldNames as $fieldName) {
  1292. $columnNames[] = $this->getColumnName($fieldName);
  1293. }
  1294. return $columnNames;
  1295. }
  1296. }
  1297. /**
  1298. * Returns an array with all the identifier column names.
  1299. *
  1300. * @return array
  1301. */
  1302. public function getIdentifierColumnNames()
  1303. {
  1304. $columnNames = array();
  1305. foreach ($this->identifier as $idProperty) {
  1306. if (isset($this->fieldMappings[$idProperty])) {
  1307. $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
  1308. continue;
  1309. }
  1310. // Association defined as Id field
  1311. $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
  1312. $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
  1313. $columnNames = array_merge($columnNames, $assocColumnNames);
  1314. }
  1315. return $columnNames;
  1316. }
  1317. /**
  1318. * Sets the type of Id generator to use for the mapped class.
  1319. */
  1320. public function setIdGeneratorType($generatorType)
  1321. {
  1322. $this->generatorType = $generatorType;
  1323. }
  1324. /**
  1325. * Checks whether the mapped class uses an Id generator.
  1326. *
  1327. * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
  1328. */
  1329. public function usesIdGenerator()
  1330. {
  1331. return $this->generatorType != self::GENERATOR_TYPE_NONE;
  1332. }
  1333. /**
  1334. * @return boolean
  1335. */
  1336. public function isInheritanceTypeNone()
  1337. {
  1338. return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
  1339. }
  1340. /**
  1341. * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
  1342. *
  1343. * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
  1344. * FALSE otherwise.
  1345. */
  1346. public function isInheritanceTypeJoined()
  1347. {
  1348. return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
  1349. }
  1350. /**
  1351. * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
  1352. *
  1353. * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
  1354. * FALSE otherwise.
  1355. */
  1356. public function isInheritanceTypeSingleTable()
  1357. {
  1358. return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
  1359. }
  1360. /**
  1361. * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
  1362. *
  1363. * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
  1364. * FALSE otherwise.
  1365. */
  1366. public function isInheritanceTypeTablePerClass()
  1367. {
  1368. return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
  1369. }
  1370. /**
  1371. * Checks whether the class uses an identity column for the Id generation.
  1372. *
  1373. * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
  1374. */
  1375. public function isIdGeneratorIdentity()
  1376. {
  1377. return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
  1378. }
  1379. /**
  1380. * Checks whether the class uses a sequence for id generation.
  1381. *
  1382. * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
  1383. */
  1384. public function isIdGeneratorSequence()
  1385. {
  1386. return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
  1387. }
  1388. /**
  1389. * Checks whether the class uses a table for id generation.
  1390. *
  1391. * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
  1392. */
  1393. public function isIdGeneratorTable()
  1394. {
  1395. $this->generatorType == self::GENERATOR_TYPE_TABLE;
  1396. }
  1397. /**
  1398. * Checks whether the class has a natural identifier/pk (which means it does
  1399. * not use any Id generator.
  1400. *
  1401. * @return boolean
  1402. */
  1403. public function isIdentifierNatural()
  1404. {
  1405. return $this->generatorType == self::GENERATOR_TYPE_NONE;
  1406. }
  1407. /**
  1408. * Gets the type of a field.
  1409. *
  1410. * @param string $fieldName
  1411. * @return \Doctrine\DBAL\Types\Type
  1412. */
  1413. public function getTypeOfField($fieldName)
  1414. {
  1415. return isset($this->fieldMappings[$fieldName]) ?
  1416. $this->fieldMappings[$fieldName]['type'] : null;
  1417. }
  1418. /**
  1419. * Gets the type of a column.
  1420. *
  1421. * @return \Doctrine\DBAL\Types\Type
  1422. */
  1423. public function getTypeOfColumn($columnName)
  1424. {
  1425. return $this->getTypeOfField($this->getFieldName($columnName));
  1426. }
  1427. /**
  1428. * Gets the name of the primary table.
  1429. *
  1430. * @return string
  1431. */
  1432. public function getTableName()
  1433. {
  1434. return $this->table['name'];
  1435. }
  1436. /**
  1437. * Gets the table name to use for temporary identifier tables of this class.
  1438. *
  1439. * @return string
  1440. */
  1441. public function getTemporaryIdTableName()
  1442. {
  1443. // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
  1444. return str_replace('.', '_', $this->getTableName() . '_id_tmp');
  1445. }
  1446. /**
  1447. * Sets the mapped subclasses of this class.
  1448. *
  1449. * @param array $subclasses The names of all mapped subclasses.
  1450. */
  1451. public function setSubclasses(array $subclasses)
  1452. {
  1453. foreach ($subclasses as $subclass) {
  1454. if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
  1455. $this->subClasses[] = $this->namespace . '\\' . $subclass;
  1456. } else {
  1457. $this->subClasses[] = $subclass;
  1458. }
  1459. }
  1460. }
  1461. /**
  1462. * Sets the parent class names.
  1463. * Assumes that the class names in the passed array are in the order:
  1464. * directParent -> directParentParent -> directParentParentParent ... -> root.
  1465. */
  1466. public function setParentClasses(array $classNames)
  1467. {
  1468. $this->parentClasses = $classNames;
  1469. if (count($classNames) > 0) {
  1470. $this->rootEntityName = array_pop($classNames);
  1471. }
  1472. }
  1473. /**
  1474. * Sets the inheritance type used by the class and it's subclasses.
  1475. *
  1476. * @param integer $type
  1477. */
  1478. public function setInheritanceType($type)
  1479. {
  1480. if ( ! $this->_isInheritanceType($type)) {
  1481. throw MappingException::invalidInheritanceType($this->name, $type);
  1482. }
  1483. $this->inheritanceType = $type;
  1484. }
  1485. /**
  1486. * Checks whether a mapped field is inherited from an entity superclass.
  1487. *
  1488. * @return boolean TRUE if the field is inherited, FALSE otherwise.
  1489. */
  1490. public function isInheritedField($fieldName)
  1491. {
  1492. return isset($this->fieldMappings[$fieldName]['inherited']);
  1493. }
  1494. /**
  1495. * Checks whether a mapped association field is inherited from a superclass.
  1496. *
  1497. * @param string $fieldName
  1498. * @return boolean TRUE if the field is inherited, FALSE otherwise.
  1499. */
  1500. public function isInheritedAssociation($fieldName)
  1501. {
  1502. return isset($this->associationMappings[$fieldName]['inherited']);
  1503. }
  1504. /**
  1505. * Sets the name of the primary table the class is mapped to.
  1506. *
  1507. * @param string $tableName The table name.
  1508. * @deprecated Use {@link setPrimaryTable}.
  1509. */
  1510. public function setTableName($tableName)
  1511. {
  1512. $this->table['name'] = $tableName;
  1513. }
  1514. /**
  1515. * Sets the primary table definition. The provided array supports the
  1516. * following structure:
  1517. *
  1518. * name => <tableName> (optional, defaults to class name)
  1519. * indexes => array of indexes (optional)
  1520. * uniqueConstraints => array of constraints (optional)
  1521. *
  1522. * If a key is omitted, the current value is kept.
  1523. *
  1524. * @param array $table The table description.
  1525. */
  1526. public function setPrimaryTable(array $table)
  1527. {
  1528. if (isset($table['name'])) {
  1529. if ($table['name'][0] == '`') {
  1530. $this->table['name'] = str_replace("`", "", $table['name']);
  1531. $this->table['quoted'] = true;
  1532. } else {
  1533. $this->table['name'] = $table['name'];
  1534. }
  1535. }
  1536. if (isset($table['indexes'])) {
  1537. $this->table['indexes'] = $table['indexes'];
  1538. }
  1539. if (isset($table['uniqueConstraints'])) {
  1540. $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
  1541. }
  1542. }
  1543. /**
  1544. * Checks whether the given type identifies an inheritance type.
  1545. *
  1546. * @param integer $type
  1547. * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
  1548. */
  1549. private function _isInheritanceType($type)
  1550. {
  1551. return $type == self::INHERITANCE_TYPE_NONE ||
  1552. $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
  1553. $type == self::INHERITANCE_TYPE_JOINED ||
  1554. $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
  1555. }
  1556. /**
  1557. * Adds a mapped field to the class.
  1558. *
  1559. * @param array $mapping The field mapping.
  1560. */
  1561. public function mapField(array $mapping)
  1562. {
  1563. $this->_validateAndCompleteFieldMapping($mapping);
  1564. if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
  1565. throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
  1566. }
  1567. $this->fieldMappings[$mapping['fieldName']] = $mapping;
  1568. }
  1569. /**
  1570. * INTERNAL:
  1571. * Adds an association mapping without completing/validating it.
  1572. * This is mainly used to add inherited association mappings to derived classes.
  1573. *
  1574. * @param array $mapping
  1575. */
  1576. public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
  1577. {
  1578. if (isset($this->associationMappings[$mapping['fieldName']])) {
  1579. throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
  1580. }
  1581. $this->associationMappings[$mapping['fieldName']] = $mapping;
  1582. }
  1583. /**
  1584. * INTERNAL:
  1585. * Adds a field mapping without completing/validating it.
  1586. * This is mainly used to add inherited field mappings to derived classes.
  1587. *
  1588. * @param array $mapping
  1589. */
  1590. public function addInheritedFieldMapping(array $fieldMapping)
  1591. {
  1592. $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
  1593. $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
  1594. $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
  1595. }
  1596. /**
  1597. * INTERNAL:
  1598. * Adds a named query to this class.
  1599. *
  1600. * @throws MappingException
  1601. * @param array $queryMapping
  1602. */
  1603. public function addNamedQuery(array $queryMapping)
  1604. {
  1605. if (isset($this->namedQueries[$queryMapping['name']])) {
  1606. throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
  1607. }
  1608. $name = $queryMapping['name'];
  1609. $query = $queryMapping['query'];
  1610. $dql = str_replace('__CLASS__', $this->name, $query);
  1611. $this->namedQueries[$name] = array(
  1612. 'name' => $name,
  1613. 'query' => $query,
  1614. 'dql' => $dql
  1615. );
  1616. }
  1617. /**
  1618. * Adds a one-to-one mapping.
  1619. *
  1620. * @param array $mapping The mapping.
  1621. */
  1622. public function mapOneToOne(array $mapping)
  1623. {
  1624. $mapping['type'] = self::ONE_TO_ONE;
  1625. $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
  1626. $this->_storeAssociationMapping($mapping);
  1627. }
  1628. /**
  1629. * Adds a one-to-many mapping.
  1630. *
  1631. * @param array $mapping The mapping.
  1632. */
  1633. public function mapOneToMany(array $mapping)
  1634. {
  1635. $mapping['type'] = self::ONE_TO_MANY;
  1636. $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
  1637. $this->_storeAssociationMapping($mapping);
  1638. }
  1639. /**
  1640. * Adds a many-to-one mapping.
  1641. *
  1642. * @param array $mapping The mapping.
  1643. */
  1644. public function mapManyToOne(array $mapping)
  1645. {
  1646. $mapping['type'] = self::MANY_TO_ONE;
  1647. // A many-to-one mapping is essentially a one-one backreference
  1648. $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
  1649. $this->_storeAssociationMapping($mapping);
  1650. }
  1651. /**
  1652. * Adds a many-to-many mapping.
  1653. *
  1654. * @param array $mapping The mapping.
  1655. */
  1656. public function mapManyToMany(array $mapping)
  1657. {
  1658. $mapping['type'] = self::MANY_TO_MANY;
  1659. $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
  1660. $this->_storeAssociationMapping($mapping);
  1661. }
  1662. /**
  1663. * Stores the association mapping.
  1664. *
  1665. * @param array $assocMapping
  1666. */
  1667. protected function _storeAssociationMapping(array $assocMapping)
  1668. {
  1669. $sourceFieldName = $assocMapping['fieldName'];
  1670. if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
  1671. throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
  1672. }
  1673. $this->associationMappings[$sourceFieldName] = $assocMapping;
  1674. }
  1675. /**
  1676. * Registers a custom repository class for the entity class.
  1677. *
  1678. * @param string $mapperClassName The class name of the custom mapper.
  1679. */
  1680. public function setCustomRepositoryClass($repositoryClassName)
  1681. {
  1682. if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
  1683. && strlen($this->namespace) > 0) {
  1684. $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
  1685. }
  1686. $this->customRepositoryClassName = $repositoryClassName;
  1687. }
  1688. /**
  1689. * Dispatches the lifecycle event of the given entity to the registered
  1690. * lifecycle callbacks and lifecycle listeners.
  1691. *
  1692. * @param string $event The lifecycle event.
  1693. * @param Entity $entity The Entity on which the event occured.
  1694. */
  1695. public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
  1696. {
  1697. foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
  1698. $entity->$callback();
  1699. }
  1700. }
  1701. /**
  1702. * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
  1703. *
  1704. * @param string $lifecycleEvent
  1705. * @return boolean
  1706. */
  1707. public function hasLifecycleCallbacks($lifecycleEvent)
  1708. {
  1709. return isset($this->lifecycleCallbacks[$lifecycleEvent]);
  1710. }
  1711. /**
  1712. * Gets the registered lifecycle callbacks for an event.
  1713. *
  1714. * @param string $event
  1715. * @return array
  1716. */
  1717. public function getLifecycleCallbacks($event)
  1718. {
  1719. return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
  1720. }
  1721. /**
  1722. * Adds a lifecycle callback for entities of this class.
  1723. *
  1724. * @param string $callback
  1725. * @param string $event
  1726. */
  1727. public function addLifecycleCallback($callback, $event)
  1728. {
  1729. $this->lifecycleCallbacks[$event][] = $callback;
  1730. }
  1731. /**
  1732. * Sets the lifecycle callbacks for entities of this class.
  1733. * Any previously registered callbacks are overwritten.
  1734. *
  1735. * @param array $callbacks
  1736. */
  1737. public function setLifecycleCallbacks(array $callbacks)
  1738. {
  1739. $this->lifecycleCallbacks = $callbacks;
  1740. }
  1741. /**
  1742. * Sets the discriminator column definition.
  1743. *
  1744. * @param array $columnDef
  1745. * @see getDiscriminatorColumn()
  1746. */
  1747. public function setDiscriminatorColumn($columnDef)
  1748. {
  1749. if ($columnDef !== null) {
  1750. if (isset($this->fieldNames[$columnDef['name']])) {
  1751. throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
  1752. }
  1753. if ( ! isset($columnDef['name'])) {
  1754. throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name, $columnDef);
  1755. }
  1756. if ( ! isset($columnDef['fieldName'])) {
  1757. $columnDef['fieldName'] = $columnDef['name'];
  1758. }
  1759. if ( ! isset($columnDef['type'])) {
  1760. $columnDef['type'] = "string";
  1761. }
  1762. if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
  1763. throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
  1764. }
  1765. $this->discriminatorColumn = $columnDef;
  1766. }
  1767. }
  1768. /**
  1769. * Sets the discriminator values used by this class.
  1770. * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
  1771. *
  1772. * @param array $map
  1773. */
  1774. public function setDiscriminatorMap(array $map)
  1775. {
  1776. foreach ($map as $value => $className) {
  1777. $this->addDiscriminatorMapClass($value, $className);
  1778. }
  1779. }
  1780. /**
  1781. * Add one entry of the discriminator map with a new class and corresponding name.
  1782. *
  1783. * @param string $name
  1784. * @param string $className
  1785. */
  1786. public function addDiscriminatorMapClass($name, $className)
  1787. {
  1788. if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
  1789. $className = $this->namespace . '\\' . $className;
  1790. }
  1791. $className = ltrim($className, '\\');
  1792. $this->discriminatorMap[$name] = $className;
  1793. if ($this->name == $className) {
  1794. $this->discriminatorValue = $name;
  1795. } else {
  1796. if ( ! class_exists($className)) {
  1797. throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
  1798. }
  1799. if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
  1800. $this->subClasses[] = $className;
  1801. }
  1802. }
  1803. }
  1804. /**
  1805. * Checks whether the class has a named query with the given query name.
  1806. *
  1807. * @param string $fieldName
  1808. * @return boolean
  1809. */
  1810. public function hasNamedQuery($queryName)
  1811. {
  1812. return isset($this->namedQueries[$queryName]);
  1813. }
  1814. /**
  1815. * Checks whether the class has a mapped association with the given field name.
  1816. *
  1817. * @param string $fieldName
  1818. * @return boolean
  1819. */
  1820. public function hasAssociation($fieldName)
  1821. {
  1822. return isset($this->associationMappings[$fieldName]);
  1823. }
  1824. /**
  1825. * Checks whether the class has a mapped association for the specified field
  1826. * and if yes, checks whether it is a single-valued association (to-one).
  1827. *
  1828. * @param string $fieldName
  1829. * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
  1830. */
  1831. public function isSingleValuedAssociation($fieldName)
  1832. {
  1833. return isset($this->associationMappings[$fieldName]) &&
  1834. ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
  1835. }
  1836. /**
  1837. * Checks whether the class has a mapped association for the specified field
  1838. * and if yes, checks whether it is a collection-valued association (to-many).
  1839. *
  1840. * @param string $fieldName
  1841. * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
  1842. */
  1843. public function isCollectionValuedAssociation($fieldName)
  1844. {
  1845. return isset($this->associationMappings[$fieldName]) &&
  1846. ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
  1847. }
  1848. /**
  1849. * Is this an association that only has a single join column?
  1850. *
  1851. * @param string $fieldName
  1852. * @return bool
  1853. */
  1854. public function isAssociationWithSingleJoinColumn($fieldName)
  1855. {
  1856. return (
  1857. isset($this->associationMappings[$fieldName]) &&
  1858. isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
  1859. !isset($this->associationMappings[$fieldName]['joinColumns'][1])
  1860. );
  1861. }
  1862. /**
  1863. * Return the single association join column (if any).
  1864. *
  1865. * @param string $fieldName
  1866. * @return string
  1867. */
  1868. public function getSingleAssociationJoinColumnName($fieldName)
  1869. {
  1870. if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
  1871. throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
  1872. }
  1873. return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
  1874. }
  1875. /**
  1876. * Return the single association referenced join column name (if any).
  1877. *
  1878. * @param string $fieldName
  1879. * @return string
  1880. */
  1881. public function getSingleAssociationReferencedJoinColumnName($fieldName)
  1882. {
  1883. if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
  1884. throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
  1885. }
  1886. return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
  1887. }
  1888. /**
  1889. * Used to retrieve a fieldname for either field or association from a given column,
  1890. *
  1891. * This method is used in foreign-key as primary-key contexts.
  1892. *
  1893. * @param string $columnName
  1894. * @return string
  1895. */
  1896. public function getFieldForColumn($columnName)
  1897. {
  1898. if (isset($this->fieldNames[$columnName])) {
  1899. return $this->fieldNames[$columnName];
  1900. } else {
  1901. foreach ($this->associationMappings AS $assocName => $mapping) {
  1902. if ($this->isAssociationWithSingleJoinColumn($assocName) &&
  1903. $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
  1904. return $assocName;
  1905. }
  1906. }
  1907. throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
  1908. }
  1909. }
  1910. /**
  1911. * Sets the ID generator used to generate IDs for instances of this class.
  1912. *
  1913. * @param AbstractIdGenerator $generator
  1914. */
  1915. public function setIdGenerator($generator)
  1916. {
  1917. $this->idGenerator = $generator;
  1918. }
  1919. /**
  1920. * Sets the definition of the sequence ID generator for this class.
  1921. *
  1922. * The definition must have the following structure:
  1923. * <code>
  1924. * array(
  1925. * 'sequenceName' => 'name',
  1926. * 'allocationSize' => 20,
  1927. * 'initialValue' => 1
  1928. * )
  1929. * </code>
  1930. *
  1931. * @param array $definition
  1932. */
  1933. public function setSequenceGeneratorDefinition(array $definition)
  1934. {
  1935. $this->sequenceGeneratorDefinition = $definition;
  1936. }
  1937. /**
  1938. * Sets the version field mapping used for versioning. Sets the default
  1939. * value to use depending on the column type.
  1940. *
  1941. * @param array $mapping The version field mapping array
  1942. */
  1943. public function setVersionMapping(array &$mapping)
  1944. {
  1945. $this->isVersioned = true;
  1946. $this->versionField = $mapping['fieldName'];
  1947. if ( ! isset($mapping['default'])) {
  1948. if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
  1949. $mapping['default'] = 1;
  1950. } else if ($mapping['type'] == 'datetime') {
  1951. $mapping['default'] = 'CURRENT_TIMESTAMP';
  1952. } else {
  1953. throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
  1954. }
  1955. }
  1956. }
  1957. /**
  1958. * Sets whether this class is to be versioned for optimistic locking.
  1959. *
  1960. * @param boolean $bool
  1961. */
  1962. public function setVersioned($bool)
  1963. {
  1964. $this->isVersioned = $bool;
  1965. }
  1966. /**
  1967. * Sets the name of the field that is to be used for versioning if this class is
  1968. * versioned for optimistic locking.
  1969. *
  1970. * @param string $versionField
  1971. */
  1972. public function setVersionField($versionField)
  1973. {
  1974. $this->versionField = $versionField;
  1975. }
  1976. /**
  1977. * Mark this class as read only, no change tracking is applied to it.
  1978. *
  1979. * @return void
  1980. */
  1981. public function markReadOnly()
  1982. {
  1983. $this->isReadOnly = true;
  1984. }
  1985. /**
  1986. * A numerically indexed list of field names of this persistent class.
  1987. *
  1988. * This array includes identifier fields if present on this class.
  1989. *
  1990. * @return array
  1991. */
  1992. public function getFieldNames()
  1993. {
  1994. return array_keys($this->fieldMappings);
  1995. }
  1996. /**
  1997. * A numerically indexed list of association names of this persistent class.
  1998. *
  1999. * This array includes identifier associations if present on this class.
  2000. *
  2001. * @return array
  2002. */
  2003. public function getAssociationNames()
  2004. {
  2005. return array_keys($this->associationMappings);
  2006. }
  2007. /**
  2008. * Returns the target class name of the given association.
  2009. *
  2010. * @param string $assocName
  2011. * @return string
  2012. */
  2013. public function getAssociationTargetClass($assocName)
  2014. {
  2015. if ( ! isset($this->associationMappings[$assocName])) {
  2016. throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
  2017. }
  2018. return $this->associationMappings[$assocName]['targetEntity'];
  2019. }
  2020. /**
  2021. * Get fully-qualified class name of this persistent class.
  2022. *
  2023. * @return string
  2024. */
  2025. public function getName()
  2026. {
  2027. return $this->name;
  2028. }
  2029. /**
  2030. * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
  2031. *
  2032. * @param AbstractPlatform $platform
  2033. * @return array
  2034. */
  2035. public function getQuotedIdentifierColumnNames($platform)
  2036. {
  2037. $quotedColumnNames = array();
  2038. foreach ($this->identifier as $idProperty) {
  2039. if (isset($this->fieldMappings[$idProperty])) {
  2040. $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
  2041. ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
  2042. : $this->fieldMappings[$idProperty]['columnName'];
  2043. continue;
  2044. }
  2045. // Association defined as Id field
  2046. $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
  2047. $assocQuotedColumnNames = array_map(
  2048. function ($joinColumn) {
  2049. return isset($joinColumn['quoted'])
  2050. ? $platform->quoteIdentifier($joinColumn['name'])
  2051. : $joinColumn['name'];
  2052. },
  2053. $joinColumns
  2054. );
  2055. $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
  2056. }
  2057. return $quotedColumnNames;
  2058. }
  2059. /**
  2060. * Gets the (possibly quoted) column name of a mapped field for safe use
  2061. * in an SQL statement.
  2062. *
  2063. * @param string $field
  2064. * @param AbstractPlatform $platform
  2065. * @return string
  2066. */
  2067. public function getQuotedColumnName($field, $platform)
  2068. {
  2069. return isset($this->fieldMappings[$field]['quoted'])
  2070. ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
  2071. : $this->fieldMappings[$field]['columnName'];
  2072. }
  2073. /**
  2074. * Gets the (possibly quoted) primary table name of this class for safe use
  2075. * in an SQL statement.
  2076. *
  2077. * @param AbstractPlatform $platform
  2078. * @return string
  2079. */
  2080. public function getQuotedTableName($platform)
  2081. {
  2082. return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
  2083. }
  2084. /**
  2085. * Gets the (possibly quoted) name of the join table.
  2086. *
  2087. * @param AbstractPlatform $platform
  2088. * @return string
  2089. */
  2090. public function getQuotedJoinTableName(array $assoc, $platform)
  2091. {
  2092. return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
  2093. }
  2094. /**
  2095. * @param string $fieldName
  2096. * @return bool
  2097. */
  2098. public function isAssociationInverseSide($fieldName)
  2099. {
  2100. return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
  2101. }
  2102. /**
  2103. * @param string $fieldName
  2104. * @return string
  2105. */
  2106. public function getAssociationMappedByTargetField($fieldName)
  2107. {
  2108. return $this->associationMappings[$fieldName]['mappedBy'];
  2109. }
  2110. }