ClassMetadataInfo.php 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343
  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. if ($this->_prototype === false) {
  670. $this->_prototype = @unserialize(sprintf('C:%d:"%s":0:{}', strlen($this->name), $this->name));
  671. }
  672. }
  673. return clone $this->_prototype;
  674. }
  675. /**
  676. * Restores some state that can not be serialized/unserialized.
  677. *
  678. * @param ReflectionService $reflService
  679. * @return void
  680. */
  681. public function wakeupReflection($reflService)
  682. {
  683. // Restore ReflectionClass and properties
  684. $this->reflClass = $reflService->getClass($this->name);
  685. foreach ($this->fieldMappings as $field => $mapping) {
  686. $this->reflFields[$field] = isset($mapping['declared'])
  687. ? $reflService->getAccessibleProperty($mapping['declared'], $field)
  688. : $reflService->getAccessibleProperty($this->name, $field);
  689. }
  690. foreach ($this->associationMappings as $field => $mapping) {
  691. $this->reflFields[$field] = isset($mapping['declared'])
  692. ? $reflService->getAccessibleProperty($mapping['declared'], $field)
  693. : $reflService->getAccessibleProperty($this->name, $field);
  694. }
  695. }
  696. /**
  697. * Initializes a new ClassMetadata instance that will hold the object-relational mapping
  698. * metadata of the class with the given name.
  699. *
  700. * @param string $entityName The name of the entity class the new instance is used for.
  701. */
  702. public function initializeReflection($reflService)
  703. {
  704. $this->reflClass = $reflService->getClass($this->name);
  705. $this->namespace = $reflService->getClassNamespace($this->name);
  706. $this->table['name'] = $reflService->getClassShortName($this->name);
  707. if ($this->reflClass) {
  708. $this->name = $this->rootEntityName = $this->reflClass->getName();
  709. }
  710. }
  711. /**
  712. * Validate Identifier
  713. *
  714. * @return void
  715. */
  716. public function validateIdentifier()
  717. {
  718. // Verify & complete identifier mapping
  719. if ( ! $this->identifier && ! $this->isMappedSuperclass) {
  720. throw MappingException::identifierRequired($this->name);
  721. }
  722. if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
  723. throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
  724. }
  725. }
  726. /**
  727. * Validate association targets actually exist.
  728. *
  729. * @return void
  730. */
  731. public function validateAssocations()
  732. {
  733. foreach ($this->associationMappings as $field => $mapping) {
  734. if ( ! \Doctrine\Common\ClassLoader::classExists($mapping['targetEntity']) ) {
  735. throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
  736. }
  737. }
  738. }
  739. /**
  740. * Validate lifecycle callbacks
  741. *
  742. * @param ReflectionService $reflService
  743. * @return void
  744. */
  745. public function validateLifecycleCallbacks($reflService)
  746. {
  747. foreach ($this->lifecycleCallbacks as $event => $callbacks) {
  748. foreach ($callbacks as $callbackFuncName) {
  749. if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
  750. throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
  751. }
  752. }
  753. }
  754. }
  755. /**
  756. * Gets the ReflectionClass instance of the mapped class.
  757. *
  758. * @return ReflectionClass
  759. */
  760. public function getReflectionClass()
  761. {
  762. return $this->reflClass;
  763. }
  764. /**
  765. * Sets the change tracking policy used by this class.
  766. *
  767. * @param integer $policy
  768. */
  769. public function setChangeTrackingPolicy($policy)
  770. {
  771. $this->changeTrackingPolicy = $policy;
  772. }
  773. /**
  774. * Whether the change tracking policy of this class is "deferred explicit".
  775. *
  776. * @return boolean
  777. */
  778. public function isChangeTrackingDeferredExplicit()
  779. {
  780. return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
  781. }
  782. /**
  783. * Whether the change tracking policy of this class is "deferred implicit".
  784. *
  785. * @return boolean
  786. */
  787. public function isChangeTrackingDeferredImplicit()
  788. {
  789. return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
  790. }
  791. /**
  792. * Whether the change tracking policy of this class is "notify".
  793. *
  794. * @return boolean
  795. */
  796. public function isChangeTrackingNotify()
  797. {
  798. return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
  799. }
  800. /**
  801. * Checks whether a field is part of the identifier/primary key field(s).
  802. *
  803. * @param string $fieldName The field name
  804. * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
  805. * FALSE otherwise.
  806. */
  807. public function isIdentifier($fieldName)
  808. {
  809. if ( ! $this->isIdentifierComposite) {
  810. return $fieldName === $this->identifier[0];
  811. }
  812. return in_array($fieldName, $this->identifier);
  813. }
  814. /**
  815. * Check if the field is unique.
  816. *
  817. * @param string $fieldName The field name
  818. * @return boolean TRUE if the field is unique, FALSE otherwise.
  819. */
  820. public function isUniqueField($fieldName)
  821. {
  822. $mapping = $this->getFieldMapping($fieldName);
  823. if ($mapping !== false) {
  824. return isset($mapping['unique']) && $mapping['unique'] == true;
  825. }
  826. return false;
  827. }
  828. /**
  829. * Check if the field is not null.
  830. *
  831. * @param string $fieldName The field name
  832. * @return boolean TRUE if the field is not null, FALSE otherwise.
  833. */
  834. public function isNullable($fieldName)
  835. {
  836. $mapping = $this->getFieldMapping($fieldName);
  837. if ($mapping !== false) {
  838. return isset($mapping['nullable']) && $mapping['nullable'] == true;
  839. }
  840. return false;
  841. }
  842. /**
  843. * Gets a column name for a field name.
  844. * If the column name for the field cannot be found, the given field name
  845. * is returned.
  846. *
  847. * @param string $fieldName The field name.
  848. * @return string The column name.
  849. */
  850. public function getColumnName($fieldName)
  851. {
  852. return isset($this->columnNames[$fieldName]) ?
  853. $this->columnNames[$fieldName] : $fieldName;
  854. }
  855. /**
  856. * Gets the mapping of a (regular) field that holds some data but not a
  857. * reference to another object.
  858. *
  859. * @param string $fieldName The field name.
  860. * @return array The field mapping.
  861. */
  862. public function getFieldMapping($fieldName)
  863. {
  864. if ( ! isset($this->fieldMappings[$fieldName])) {
  865. throw MappingException::mappingNotFound($this->name, $fieldName);
  866. }
  867. return $this->fieldMappings[$fieldName];
  868. }
  869. /**
  870. * Gets the mapping of an association.
  871. *
  872. * @see ClassMetadataInfo::$associationMappings
  873. * @param string $fieldName The field name that represents the association in
  874. * the object model.
  875. * @return array The mapping.
  876. */
  877. public function getAssociationMapping($fieldName)
  878. {
  879. if ( ! isset($this->associationMappings[$fieldName])) {
  880. throw MappingException::mappingNotFound($this->name, $fieldName);
  881. }
  882. return $this->associationMappings[$fieldName];
  883. }
  884. /**
  885. * Gets all association mappings of the class.
  886. *
  887. * @return array
  888. */
  889. public function getAssociationMappings()
  890. {
  891. return $this->associationMappings;
  892. }
  893. /**
  894. * Gets the field name for a column name.
  895. * If no field name can be found the column name is returned.
  896. *
  897. * @param string $columnName column name
  898. * @return string column alias
  899. */
  900. public function getFieldName($columnName)
  901. {
  902. return isset($this->fieldNames[$columnName]) ?
  903. $this->fieldNames[$columnName] : $columnName;
  904. }
  905. /**
  906. * Gets the named query.
  907. *
  908. * @see ClassMetadataInfo::$namedQueries
  909. * @throws MappingException
  910. * @param string $queryName The query name
  911. * @return string
  912. */
  913. public function getNamedQuery($queryName)
  914. {
  915. if ( ! isset($this->namedQueries[$queryName])) {
  916. throw MappingException::queryNotFound($this->name, $queryName);
  917. }
  918. return $this->namedQueries[$queryName]['dql'];
  919. }
  920. /**
  921. * Gets all named queries of the class.
  922. *
  923. * @return array
  924. */
  925. public function getNamedQueries()
  926. {
  927. return $this->namedQueries;
  928. }
  929. /**
  930. * Validates & completes the given field mapping.
  931. *
  932. * @param array $mapping The field mapping to validated & complete.
  933. * @return array The validated and completed field mapping.
  934. */
  935. protected function _validateAndCompleteFieldMapping(array &$mapping)
  936. {
  937. // Check mandatory fields
  938. if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
  939. throw MappingException::missingFieldName($this->name);
  940. }
  941. if ( ! isset($mapping['type'])) {
  942. // Default to string
  943. $mapping['type'] = 'string';
  944. }
  945. // Complete fieldName and columnName mapping
  946. if ( ! isset($mapping['columnName'])) {
  947. $mapping['columnName'] = $mapping['fieldName'];
  948. } else {
  949. if ($mapping['columnName'][0] == '`') {
  950. $mapping['columnName'] = trim($mapping['columnName'], '`');
  951. $mapping['quoted'] = true;
  952. }
  953. }
  954. $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
  955. if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
  956. throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
  957. }
  958. $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
  959. // Complete id mapping
  960. if (isset($mapping['id']) && $mapping['id'] === true) {
  961. if ($this->versionField == $mapping['fieldName']) {
  962. throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
  963. }
  964. if ( ! in_array($mapping['fieldName'], $this->identifier)) {
  965. $this->identifier[] = $mapping['fieldName'];
  966. }
  967. // Check for composite key
  968. if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
  969. $this->isIdentifierComposite = true;
  970. }
  971. }
  972. if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
  973. if (isset($mapping['id']) && $mapping['id'] === true) {
  974. throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
  975. }
  976. $mapping['requireSQLConversion'] = true;
  977. }
  978. }
  979. /**
  980. * Validates & completes the basic mapping information that is common to all
  981. * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
  982. *
  983. * @param array $mapping The mapping.
  984. * @return array The updated mapping.
  985. * @throws MappingException If something is wrong with the mapping.
  986. */
  987. protected function _validateAndCompleteAssociationMapping(array $mapping)
  988. {
  989. if ( ! isset($mapping['mappedBy'])) {
  990. $mapping['mappedBy'] = null;
  991. }
  992. if ( ! isset($mapping['inversedBy'])) {
  993. $mapping['inversedBy'] = null;
  994. }
  995. $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
  996. // unset optional indexBy attribute if its empty
  997. if (!isset($mapping['indexBy']) || !$mapping['indexBy']) {
  998. unset($mapping['indexBy']);
  999. }
  1000. // If targetEntity is unqualified, assume it is in the same namespace as
  1001. // the sourceEntity.
  1002. $mapping['sourceEntity'] = $this->name;
  1003. if (isset($mapping['targetEntity'])) {
  1004. if (strlen($this->namespace) > 0 && strpos($mapping['targetEntity'], '\\') === false) {
  1005. $mapping['targetEntity'] = $this->namespace . '\\' . $mapping['targetEntity'];
  1006. }
  1007. $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
  1008. }
  1009. if ( ($mapping['type'] & self::MANY_TO_ONE) > 0 &&
  1010. isset($mapping['orphanRemoval']) &&
  1011. $mapping['orphanRemoval'] == true) {
  1012. throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
  1013. }
  1014. // Complete id mapping
  1015. if (isset($mapping['id']) && $mapping['id'] === true) {
  1016. if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
  1017. throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
  1018. }
  1019. if ( ! in_array($mapping['fieldName'], $this->identifier)) {
  1020. if (count($mapping['joinColumns']) >= 2) {
  1021. throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
  1022. $mapping['targetEntity'], $this->name, $mapping['fieldName']
  1023. );
  1024. }
  1025. $this->identifier[] = $mapping['fieldName'];
  1026. $this->containsForeignIdentifier = true;
  1027. }
  1028. // Check for composite key
  1029. if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
  1030. $this->isIdentifierComposite = true;
  1031. }
  1032. }
  1033. // Mandatory attributes for both sides
  1034. // Mandatory: fieldName, targetEntity
  1035. if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
  1036. throw MappingException::missingFieldName($this->name);
  1037. }
  1038. if ( ! isset($mapping['targetEntity'])) {
  1039. throw MappingException::missingTargetEntity($mapping['fieldName']);
  1040. }
  1041. // Mandatory and optional attributes for either side
  1042. if ( ! $mapping['mappedBy']) {
  1043. if (isset($mapping['joinTable']) && $mapping['joinTable']) {
  1044. if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] == '`') {
  1045. $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`');
  1046. $mapping['joinTable']['quoted'] = true;
  1047. }
  1048. }
  1049. } else {
  1050. $mapping['isOwningSide'] = false;
  1051. }
  1052. if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
  1053. throw MappingException::illegalToManyIdentifierAssoaction($this->name, $mapping['fieldName']);
  1054. }
  1055. // Fetch mode. Default fetch mode to LAZY, if not set.
  1056. if ( ! isset($mapping['fetch'])) {
  1057. $mapping['fetch'] = self::FETCH_LAZY;
  1058. }
  1059. // Cascades
  1060. $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
  1061. if (in_array('all', $cascades)) {
  1062. $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
  1063. }
  1064. $mapping['cascade'] = $cascades;
  1065. $mapping['isCascadeRemove'] = in_array('remove', $cascades);
  1066. $mapping['isCascadePersist'] = in_array('persist', $cascades);
  1067. $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
  1068. $mapping['isCascadeMerge'] = in_array('merge', $cascades);
  1069. $mapping['isCascadeDetach'] = in_array('detach', $cascades);
  1070. return $mapping;
  1071. }
  1072. /**
  1073. * Validates & completes a one-to-one association mapping.
  1074. *
  1075. * @param array $mapping The mapping to validate & complete.
  1076. * @return array The validated & completed mapping.
  1077. * @override
  1078. */
  1079. protected function _validateAndCompleteOneToOneMapping(array $mapping)
  1080. {
  1081. $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
  1082. if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
  1083. $mapping['isOwningSide'] = true;
  1084. }
  1085. if ($mapping['isOwningSide']) {
  1086. if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
  1087. // Apply default join column
  1088. $mapping['joinColumns'] = array(array(
  1089. 'name' => $mapping['fieldName'] . '_id',
  1090. 'referencedColumnName' => 'id'
  1091. ));
  1092. }
  1093. $uniqueContraintColumns = array();
  1094. foreach ($mapping['joinColumns'] as $key => &$joinColumn) {
  1095. if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
  1096. if (count($mapping['joinColumns']) == 1) {
  1097. if (! isset($mapping['id']) || ! $mapping['id']) {
  1098. $joinColumn['unique'] = true;
  1099. }
  1100. } else {
  1101. $uniqueContraintColumns[] = $joinColumn['name'];
  1102. }
  1103. }
  1104. if (empty($joinColumn['name'])) {
  1105. $joinColumn['name'] = $mapping['fieldName'] . '_id';
  1106. }
  1107. if (empty($joinColumn['referencedColumnName'])) {
  1108. $joinColumn['referencedColumnName'] = 'id';
  1109. }
  1110. $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
  1111. $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
  1112. ? $joinColumn['fieldName'] : $joinColumn['name'];
  1113. }
  1114. if ($uniqueContraintColumns) {
  1115. if (!$this->table) {
  1116. throw new \RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
  1117. }
  1118. $this->table['uniqueConstraints'][$mapping['fieldName']."_uniq"] = array(
  1119. 'columns' => $uniqueContraintColumns
  1120. );
  1121. }
  1122. $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
  1123. }
  1124. $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
  1125. $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
  1126. if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
  1127. throw MappingException::illegalInverseIdentifierAssocation($this->name, $mapping['fieldName']);
  1128. }
  1129. return $mapping;
  1130. }
  1131. /**
  1132. * Validates and completes the mapping.
  1133. *
  1134. * @param array $mapping The mapping to validate and complete.
  1135. * @return array The validated and completed mapping.
  1136. * @override
  1137. */
  1138. protected function _validateAndCompleteOneToManyMapping(array $mapping)
  1139. {
  1140. $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
  1141. // OneToMany-side MUST be inverse (must have mappedBy)
  1142. if ( ! isset($mapping['mappedBy'])) {
  1143. throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
  1144. }
  1145. $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
  1146. $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
  1147. if (isset($mapping['orderBy'])) {
  1148. if ( ! is_array($mapping['orderBy'])) {
  1149. throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
  1150. }
  1151. }
  1152. return $mapping;
  1153. }
  1154. protected function _validateAndCompleteManyToManyMapping(array $mapping)
  1155. {
  1156. $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
  1157. if ($mapping['isOwningSide']) {
  1158. if (strpos($mapping['sourceEntity'], '\\') !== false) {
  1159. $sourceShortName = strtolower(substr($mapping['sourceEntity'], strrpos($mapping['sourceEntity'], '\\') + 1));
  1160. } else {
  1161. $sourceShortName = strtolower($mapping['sourceEntity']);
  1162. }
  1163. if (strpos($mapping['targetEntity'], '\\') !== false) {
  1164. $targetShortName = strtolower(substr($mapping['targetEntity'], strrpos($mapping['targetEntity'], '\\') + 1));
  1165. } else {
  1166. $targetShortName = strtolower($mapping['targetEntity']);
  1167. }
  1168. // owning side MUST have a join table
  1169. if ( ! isset($mapping['joinTable']['name'])) {
  1170. $mapping['joinTable']['name'] = $sourceShortName .'_' . $targetShortName;
  1171. }
  1172. if ( ! isset($mapping['joinTable']['joinColumns'])) {
  1173. $mapping['joinTable']['joinColumns'] = array(array(
  1174. 'name' => $sourceShortName . '_id',
  1175. 'referencedColumnName' => 'id',
  1176. 'onDelete' => 'CASCADE'));
  1177. }
  1178. if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
  1179. $mapping['joinTable']['inverseJoinColumns'] = array(array(
  1180. 'name' => $targetShortName . '_id',
  1181. 'referencedColumnName' => 'id',
  1182. 'onDelete' => 'CASCADE'));
  1183. }
  1184. foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
  1185. if (empty($joinColumn['name'])) {
  1186. $joinColumn['name'] = $sourceShortName . '_id';
  1187. }
  1188. if (empty($joinColumn['referencedColumnName'])) {
  1189. $joinColumn['referencedColumnName'] = 'id';
  1190. }
  1191. if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
  1192. $mapping['isOnDeleteCascade'] = true;
  1193. }
  1194. $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
  1195. $mapping['joinTableColumns'][] = $joinColumn['name'];
  1196. }
  1197. foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
  1198. if (empty($inverseJoinColumn['name'])) {
  1199. $inverseJoinColumn['name'] = $targetShortName . '_id';
  1200. }
  1201. if (empty($inverseJoinColumn['referencedColumnName'])) {
  1202. $inverseJoinColumn['referencedColumnName'] = 'id';
  1203. }
  1204. if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
  1205. $mapping['isOnDeleteCascade'] = true;
  1206. }
  1207. $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
  1208. $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
  1209. }
  1210. }
  1211. $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
  1212. if (isset($mapping['orderBy'])) {
  1213. if ( ! is_array($mapping['orderBy'])) {
  1214. throw new \InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
  1215. }
  1216. }
  1217. return $mapping;
  1218. }
  1219. /**
  1220. * Gets the identifier (primary key) field names of the class.
  1221. *
  1222. * @return mixed
  1223. */
  1224. public function getIdentifierFieldNames()
  1225. {
  1226. return $this->identifier;
  1227. }
  1228. /**
  1229. * Gets the name of the single id field. Note that this only works on
  1230. * entity classes that have a single-field pk.
  1231. *
  1232. * @return string
  1233. * @throws MappingException If the class has a composite primary key.
  1234. */
  1235. public function getSingleIdentifierFieldName()
  1236. {
  1237. if ($this->isIdentifierComposite) {
  1238. throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
  1239. }
  1240. return $this->identifier[0];
  1241. }
  1242. /**
  1243. * Gets the column name of the single id column. Note that this only works on
  1244. * entity classes that have a single-field pk.
  1245. *
  1246. * @return string
  1247. * @throws MappingException If the class has a composite primary key.
  1248. */
  1249. public function getSingleIdentifierColumnName()
  1250. {
  1251. return $this->getColumnName($this->getSingleIdentifierFieldName());
  1252. }
  1253. /**
  1254. * INTERNAL:
  1255. * Sets the mapped identifier/primary key fields of this class.
  1256. * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
  1257. *
  1258. * @param array $identifier
  1259. */
  1260. public function setIdentifier(array $identifier)
  1261. {
  1262. $this->identifier = $identifier;
  1263. $this->isIdentifierComposite = (count($this->identifier) > 1);
  1264. }
  1265. /**
  1266. * Gets the mapped identifier field of this class.
  1267. *
  1268. * @return string $identifier
  1269. */
  1270. public function getIdentifier()
  1271. {
  1272. return $this->identifier;
  1273. }
  1274. /**
  1275. * Checks whether the class has a (mapped) field with a certain name.
  1276. *
  1277. * @return boolean
  1278. */
  1279. public function hasField($fieldName)
  1280. {
  1281. return isset($this->fieldMappings[$fieldName]);
  1282. }
  1283. /**
  1284. * Gets an array containing all the column names.
  1285. *
  1286. * @return array
  1287. */
  1288. public function getColumnNames(array $fieldNames = null)
  1289. {
  1290. if ($fieldNames === null) {
  1291. return array_keys($this->fieldNames);
  1292. } else {
  1293. $columnNames = array();
  1294. foreach ($fieldNames as $fieldName) {
  1295. $columnNames[] = $this->getColumnName($fieldName);
  1296. }
  1297. return $columnNames;
  1298. }
  1299. }
  1300. /**
  1301. * Returns an array with all the identifier column names.
  1302. *
  1303. * @return array
  1304. */
  1305. public function getIdentifierColumnNames()
  1306. {
  1307. $columnNames = array();
  1308. foreach ($this->identifier as $idProperty) {
  1309. if (isset($this->fieldMappings[$idProperty])) {
  1310. $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
  1311. continue;
  1312. }
  1313. // Association defined as Id field
  1314. $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
  1315. $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
  1316. $columnNames = array_merge($columnNames, $assocColumnNames);
  1317. }
  1318. return $columnNames;
  1319. }
  1320. /**
  1321. * Sets the type of Id generator to use for the mapped class.
  1322. */
  1323. public function setIdGeneratorType($generatorType)
  1324. {
  1325. $this->generatorType = $generatorType;
  1326. }
  1327. /**
  1328. * Checks whether the mapped class uses an Id generator.
  1329. *
  1330. * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
  1331. */
  1332. public function usesIdGenerator()
  1333. {
  1334. return $this->generatorType != self::GENERATOR_TYPE_NONE;
  1335. }
  1336. /**
  1337. * @return boolean
  1338. */
  1339. public function isInheritanceTypeNone()
  1340. {
  1341. return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
  1342. }
  1343. /**
  1344. * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
  1345. *
  1346. * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
  1347. * FALSE otherwise.
  1348. */
  1349. public function isInheritanceTypeJoined()
  1350. {
  1351. return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
  1352. }
  1353. /**
  1354. * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
  1355. *
  1356. * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
  1357. * FALSE otherwise.
  1358. */
  1359. public function isInheritanceTypeSingleTable()
  1360. {
  1361. return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
  1362. }
  1363. /**
  1364. * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
  1365. *
  1366. * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
  1367. * FALSE otherwise.
  1368. */
  1369. public function isInheritanceTypeTablePerClass()
  1370. {
  1371. return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
  1372. }
  1373. /**
  1374. * Checks whether the class uses an identity column for the Id generation.
  1375. *
  1376. * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
  1377. */
  1378. public function isIdGeneratorIdentity()
  1379. {
  1380. return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
  1381. }
  1382. /**
  1383. * Checks whether the class uses a sequence for id generation.
  1384. *
  1385. * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
  1386. */
  1387. public function isIdGeneratorSequence()
  1388. {
  1389. return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
  1390. }
  1391. /**
  1392. * Checks whether the class uses a table for id generation.
  1393. *
  1394. * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
  1395. */
  1396. public function isIdGeneratorTable()
  1397. {
  1398. $this->generatorType == self::GENERATOR_TYPE_TABLE;
  1399. }
  1400. /**
  1401. * Checks whether the class has a natural identifier/pk (which means it does
  1402. * not use any Id generator.
  1403. *
  1404. * @return boolean
  1405. */
  1406. public function isIdentifierNatural()
  1407. {
  1408. return $this->generatorType == self::GENERATOR_TYPE_NONE;
  1409. }
  1410. /**
  1411. * Gets the type of a field.
  1412. *
  1413. * @param string $fieldName
  1414. * @return \Doctrine\DBAL\Types\Type
  1415. */
  1416. public function getTypeOfField($fieldName)
  1417. {
  1418. return isset($this->fieldMappings[$fieldName]) ?
  1419. $this->fieldMappings[$fieldName]['type'] : null;
  1420. }
  1421. /**
  1422. * Gets the type of a column.
  1423. *
  1424. * @return \Doctrine\DBAL\Types\Type
  1425. */
  1426. public function getTypeOfColumn($columnName)
  1427. {
  1428. return $this->getTypeOfField($this->getFieldName($columnName));
  1429. }
  1430. /**
  1431. * Gets the name of the primary table.
  1432. *
  1433. * @return string
  1434. */
  1435. public function getTableName()
  1436. {
  1437. return $this->table['name'];
  1438. }
  1439. /**
  1440. * Gets the table name to use for temporary identifier tables of this class.
  1441. *
  1442. * @return string
  1443. */
  1444. public function getTemporaryIdTableName()
  1445. {
  1446. // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
  1447. return str_replace('.', '_', $this->getTableName() . '_id_tmp');
  1448. }
  1449. /**
  1450. * Sets the mapped subclasses of this class.
  1451. *
  1452. * @param array $subclasses The names of all mapped subclasses.
  1453. */
  1454. public function setSubclasses(array $subclasses)
  1455. {
  1456. foreach ($subclasses as $subclass) {
  1457. if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
  1458. $this->subClasses[] = $this->namespace . '\\' . $subclass;
  1459. } else {
  1460. $this->subClasses[] = $subclass;
  1461. }
  1462. }
  1463. }
  1464. /**
  1465. * Sets the parent class names.
  1466. * Assumes that the class names in the passed array are in the order:
  1467. * directParent -> directParentParent -> directParentParentParent ... -> root.
  1468. */
  1469. public function setParentClasses(array $classNames)
  1470. {
  1471. $this->parentClasses = $classNames;
  1472. if (count($classNames) > 0) {
  1473. $this->rootEntityName = array_pop($classNames);
  1474. }
  1475. }
  1476. /**
  1477. * Sets the inheritance type used by the class and it's subclasses.
  1478. *
  1479. * @param integer $type
  1480. */
  1481. public function setInheritanceType($type)
  1482. {
  1483. if ( ! $this->_isInheritanceType($type)) {
  1484. throw MappingException::invalidInheritanceType($this->name, $type);
  1485. }
  1486. $this->inheritanceType = $type;
  1487. }
  1488. /**
  1489. * Checks whether a mapped field is inherited from an entity superclass.
  1490. *
  1491. * @return boolean TRUE if the field is inherited, FALSE otherwise.
  1492. */
  1493. public function isInheritedField($fieldName)
  1494. {
  1495. return isset($this->fieldMappings[$fieldName]['inherited']);
  1496. }
  1497. /**
  1498. * Checks whether a mapped association field is inherited from a superclass.
  1499. *
  1500. * @param string $fieldName
  1501. * @return boolean TRUE if the field is inherited, FALSE otherwise.
  1502. */
  1503. public function isInheritedAssociation($fieldName)
  1504. {
  1505. return isset($this->associationMappings[$fieldName]['inherited']);
  1506. }
  1507. /**
  1508. * Sets the name of the primary table the class is mapped to.
  1509. *
  1510. * @param string $tableName The table name.
  1511. * @deprecated Use {@link setPrimaryTable}.
  1512. */
  1513. public function setTableName($tableName)
  1514. {
  1515. $this->table['name'] = $tableName;
  1516. }
  1517. /**
  1518. * Sets the primary table definition. The provided array supports the
  1519. * following structure:
  1520. *
  1521. * name => <tableName> (optional, defaults to class name)
  1522. * indexes => array of indexes (optional)
  1523. * uniqueConstraints => array of constraints (optional)
  1524. *
  1525. * If a key is omitted, the current value is kept.
  1526. *
  1527. * @param array $table The table description.
  1528. */
  1529. public function setPrimaryTable(array $table)
  1530. {
  1531. if (isset($table['name'])) {
  1532. if ($table['name'][0] == '`') {
  1533. $this->table['name'] = str_replace("`", "", $table['name']);
  1534. $this->table['quoted'] = true;
  1535. } else {
  1536. $this->table['name'] = $table['name'];
  1537. }
  1538. }
  1539. if (isset($table['indexes'])) {
  1540. $this->table['indexes'] = $table['indexes'];
  1541. }
  1542. if (isset($table['uniqueConstraints'])) {
  1543. $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
  1544. }
  1545. }
  1546. /**
  1547. * Checks whether the given type identifies an inheritance type.
  1548. *
  1549. * @param integer $type
  1550. * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
  1551. */
  1552. private function _isInheritanceType($type)
  1553. {
  1554. return $type == self::INHERITANCE_TYPE_NONE ||
  1555. $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
  1556. $type == self::INHERITANCE_TYPE_JOINED ||
  1557. $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
  1558. }
  1559. /**
  1560. * Adds a mapped field to the class.
  1561. *
  1562. * @param array $mapping The field mapping.
  1563. */
  1564. public function mapField(array $mapping)
  1565. {
  1566. $this->_validateAndCompleteFieldMapping($mapping);
  1567. if (isset($this->fieldMappings[$mapping['fieldName']]) || isset($this->associationMappings[$mapping['fieldName']])) {
  1568. throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
  1569. }
  1570. $this->fieldMappings[$mapping['fieldName']] = $mapping;
  1571. }
  1572. /**
  1573. * INTERNAL:
  1574. * Adds an association mapping without completing/validating it.
  1575. * This is mainly used to add inherited association mappings to derived classes.
  1576. *
  1577. * @param array $mapping
  1578. */
  1579. public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
  1580. {
  1581. if (isset($this->associationMappings[$mapping['fieldName']])) {
  1582. throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
  1583. }
  1584. $this->associationMappings[$mapping['fieldName']] = $mapping;
  1585. }
  1586. /**
  1587. * INTERNAL:
  1588. * Adds a field mapping without completing/validating it.
  1589. * This is mainly used to add inherited field mappings to derived classes.
  1590. *
  1591. * @param array $mapping
  1592. */
  1593. public function addInheritedFieldMapping(array $fieldMapping)
  1594. {
  1595. $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
  1596. $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
  1597. $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
  1598. }
  1599. /**
  1600. * INTERNAL:
  1601. * Adds a named query to this class.
  1602. *
  1603. * @throws MappingException
  1604. * @param array $queryMapping
  1605. */
  1606. public function addNamedQuery(array $queryMapping)
  1607. {
  1608. if (isset($this->namedQueries[$queryMapping['name']])) {
  1609. throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
  1610. }
  1611. $name = $queryMapping['name'];
  1612. $query = $queryMapping['query'];
  1613. $dql = str_replace('__CLASS__', $this->name, $query);
  1614. $this->namedQueries[$name] = array(
  1615. 'name' => $name,
  1616. 'query' => $query,
  1617. 'dql' => $dql
  1618. );
  1619. }
  1620. /**
  1621. * Adds a one-to-one mapping.
  1622. *
  1623. * @param array $mapping The mapping.
  1624. */
  1625. public function mapOneToOne(array $mapping)
  1626. {
  1627. $mapping['type'] = self::ONE_TO_ONE;
  1628. $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
  1629. $this->_storeAssociationMapping($mapping);
  1630. }
  1631. /**
  1632. * Adds a one-to-many mapping.
  1633. *
  1634. * @param array $mapping The mapping.
  1635. */
  1636. public function mapOneToMany(array $mapping)
  1637. {
  1638. $mapping['type'] = self::ONE_TO_MANY;
  1639. $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
  1640. $this->_storeAssociationMapping($mapping);
  1641. }
  1642. /**
  1643. * Adds a many-to-one mapping.
  1644. *
  1645. * @param array $mapping The mapping.
  1646. */
  1647. public function mapManyToOne(array $mapping)
  1648. {
  1649. $mapping['type'] = self::MANY_TO_ONE;
  1650. // A many-to-one mapping is essentially a one-one backreference
  1651. $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
  1652. $this->_storeAssociationMapping($mapping);
  1653. }
  1654. /**
  1655. * Adds a many-to-many mapping.
  1656. *
  1657. * @param array $mapping The mapping.
  1658. */
  1659. public function mapManyToMany(array $mapping)
  1660. {
  1661. $mapping['type'] = self::MANY_TO_MANY;
  1662. $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
  1663. $this->_storeAssociationMapping($mapping);
  1664. }
  1665. /**
  1666. * Stores the association mapping.
  1667. *
  1668. * @param array $assocMapping
  1669. */
  1670. protected function _storeAssociationMapping(array $assocMapping)
  1671. {
  1672. $sourceFieldName = $assocMapping['fieldName'];
  1673. if (isset($this->fieldMappings[$sourceFieldName]) || isset($this->associationMappings[$sourceFieldName])) {
  1674. throw MappingException::duplicateFieldMapping($this->name, $sourceFieldName);
  1675. }
  1676. $this->associationMappings[$sourceFieldName] = $assocMapping;
  1677. }
  1678. /**
  1679. * Registers a custom repository class for the entity class.
  1680. *
  1681. * @param string $mapperClassName The class name of the custom mapper.
  1682. */
  1683. public function setCustomRepositoryClass($repositoryClassName)
  1684. {
  1685. if ($repositoryClassName !== null && strpos($repositoryClassName, '\\') === false
  1686. && strlen($this->namespace) > 0) {
  1687. $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
  1688. }
  1689. $this->customRepositoryClassName = $repositoryClassName;
  1690. }
  1691. /**
  1692. * Dispatches the lifecycle event of the given entity to the registered
  1693. * lifecycle callbacks and lifecycle listeners.
  1694. *
  1695. * @param string $event The lifecycle event.
  1696. * @param Entity $entity The Entity on which the event occured.
  1697. */
  1698. public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
  1699. {
  1700. foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
  1701. $entity->$callback();
  1702. }
  1703. }
  1704. /**
  1705. * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
  1706. *
  1707. * @param string $lifecycleEvent
  1708. * @return boolean
  1709. */
  1710. public function hasLifecycleCallbacks($lifecycleEvent)
  1711. {
  1712. return isset($this->lifecycleCallbacks[$lifecycleEvent]);
  1713. }
  1714. /**
  1715. * Gets the registered lifecycle callbacks for an event.
  1716. *
  1717. * @param string $event
  1718. * @return array
  1719. */
  1720. public function getLifecycleCallbacks($event)
  1721. {
  1722. return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
  1723. }
  1724. /**
  1725. * Adds a lifecycle callback for entities of this class.
  1726. *
  1727. * @param string $callback
  1728. * @param string $event
  1729. */
  1730. public function addLifecycleCallback($callback, $event)
  1731. {
  1732. $this->lifecycleCallbacks[$event][] = $callback;
  1733. }
  1734. /**
  1735. * Sets the lifecycle callbacks for entities of this class.
  1736. * Any previously registered callbacks are overwritten.
  1737. *
  1738. * @param array $callbacks
  1739. */
  1740. public function setLifecycleCallbacks(array $callbacks)
  1741. {
  1742. $this->lifecycleCallbacks = $callbacks;
  1743. }
  1744. /**
  1745. * Sets the discriminator column definition.
  1746. *
  1747. * @param array $columnDef
  1748. * @see getDiscriminatorColumn()
  1749. */
  1750. public function setDiscriminatorColumn($columnDef)
  1751. {
  1752. if ($columnDef !== null) {
  1753. if (isset($this->fieldNames[$columnDef['name']])) {
  1754. throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
  1755. }
  1756. if ( ! isset($columnDef['name'])) {
  1757. throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name, $columnDef);
  1758. }
  1759. if ( ! isset($columnDef['fieldName'])) {
  1760. $columnDef['fieldName'] = $columnDef['name'];
  1761. }
  1762. if ( ! isset($columnDef['type'])) {
  1763. $columnDef['type'] = "string";
  1764. }
  1765. if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
  1766. throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
  1767. }
  1768. $this->discriminatorColumn = $columnDef;
  1769. }
  1770. }
  1771. /**
  1772. * Sets the discriminator values used by this class.
  1773. * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
  1774. *
  1775. * @param array $map
  1776. */
  1777. public function setDiscriminatorMap(array $map)
  1778. {
  1779. foreach ($map as $value => $className) {
  1780. $this->addDiscriminatorMapClass($value, $className);
  1781. }
  1782. }
  1783. /**
  1784. * Add one entry of the discriminator map with a new class and corresponding name.
  1785. *
  1786. * @param string $name
  1787. * @param string $className
  1788. */
  1789. public function addDiscriminatorMapClass($name, $className)
  1790. {
  1791. if (strlen($this->namespace) > 0 && strpos($className, '\\') === false) {
  1792. $className = $this->namespace . '\\' . $className;
  1793. }
  1794. $className = ltrim($className, '\\');
  1795. $this->discriminatorMap[$name] = $className;
  1796. if ($this->name == $className) {
  1797. $this->discriminatorValue = $name;
  1798. } else {
  1799. if ( ! class_exists($className)) {
  1800. throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
  1801. }
  1802. if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
  1803. $this->subClasses[] = $className;
  1804. }
  1805. }
  1806. }
  1807. /**
  1808. * Checks whether the class has a named query with the given query name.
  1809. *
  1810. * @param string $fieldName
  1811. * @return boolean
  1812. */
  1813. public function hasNamedQuery($queryName)
  1814. {
  1815. return isset($this->namedQueries[$queryName]);
  1816. }
  1817. /**
  1818. * Checks whether the class has a mapped association with the given field name.
  1819. *
  1820. * @param string $fieldName
  1821. * @return boolean
  1822. */
  1823. public function hasAssociation($fieldName)
  1824. {
  1825. return isset($this->associationMappings[$fieldName]);
  1826. }
  1827. /**
  1828. * Checks whether the class has a mapped association for the specified field
  1829. * and if yes, checks whether it is a single-valued association (to-one).
  1830. *
  1831. * @param string $fieldName
  1832. * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
  1833. */
  1834. public function isSingleValuedAssociation($fieldName)
  1835. {
  1836. return isset($this->associationMappings[$fieldName]) &&
  1837. ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
  1838. }
  1839. /**
  1840. * Checks whether the class has a mapped association for the specified field
  1841. * and if yes, checks whether it is a collection-valued association (to-many).
  1842. *
  1843. * @param string $fieldName
  1844. * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
  1845. */
  1846. public function isCollectionValuedAssociation($fieldName)
  1847. {
  1848. return isset($this->associationMappings[$fieldName]) &&
  1849. ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
  1850. }
  1851. /**
  1852. * Is this an association that only has a single join column?
  1853. *
  1854. * @param string $fieldName
  1855. * @return bool
  1856. */
  1857. public function isAssociationWithSingleJoinColumn($fieldName)
  1858. {
  1859. return (
  1860. isset($this->associationMappings[$fieldName]) &&
  1861. isset($this->associationMappings[$fieldName]['joinColumns'][0]) &&
  1862. !isset($this->associationMappings[$fieldName]['joinColumns'][1])
  1863. );
  1864. }
  1865. /**
  1866. * Return the single association join column (if any).
  1867. *
  1868. * @param string $fieldName
  1869. * @return string
  1870. */
  1871. public function getSingleAssociationJoinColumnName($fieldName)
  1872. {
  1873. if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
  1874. throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
  1875. }
  1876. return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
  1877. }
  1878. /**
  1879. * Return the single association referenced join column name (if any).
  1880. *
  1881. * @param string $fieldName
  1882. * @return string
  1883. */
  1884. public function getSingleAssociationReferencedJoinColumnName($fieldName)
  1885. {
  1886. if (!$this->isAssociationWithSingleJoinColumn($fieldName)) {
  1887. throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
  1888. }
  1889. return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
  1890. }
  1891. /**
  1892. * Used to retrieve a fieldname for either field or association from a given column,
  1893. *
  1894. * This method is used in foreign-key as primary-key contexts.
  1895. *
  1896. * @param string $columnName
  1897. * @return string
  1898. */
  1899. public function getFieldForColumn($columnName)
  1900. {
  1901. if (isset($this->fieldNames[$columnName])) {
  1902. return $this->fieldNames[$columnName];
  1903. } else {
  1904. foreach ($this->associationMappings AS $assocName => $mapping) {
  1905. if ($this->isAssociationWithSingleJoinColumn($assocName) &&
  1906. $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
  1907. return $assocName;
  1908. }
  1909. }
  1910. throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
  1911. }
  1912. }
  1913. /**
  1914. * Sets the ID generator used to generate IDs for instances of this class.
  1915. *
  1916. * @param AbstractIdGenerator $generator
  1917. */
  1918. public function setIdGenerator($generator)
  1919. {
  1920. $this->idGenerator = $generator;
  1921. }
  1922. /**
  1923. * Sets the definition of the sequence ID generator for this class.
  1924. *
  1925. * The definition must have the following structure:
  1926. * <code>
  1927. * array(
  1928. * 'sequenceName' => 'name',
  1929. * 'allocationSize' => 20,
  1930. * 'initialValue' => 1
  1931. * )
  1932. * </code>
  1933. *
  1934. * @param array $definition
  1935. */
  1936. public function setSequenceGeneratorDefinition(array $definition)
  1937. {
  1938. $this->sequenceGeneratorDefinition = $definition;
  1939. }
  1940. /**
  1941. * Sets the version field mapping used for versioning. Sets the default
  1942. * value to use depending on the column type.
  1943. *
  1944. * @param array $mapping The version field mapping array
  1945. */
  1946. public function setVersionMapping(array &$mapping)
  1947. {
  1948. $this->isVersioned = true;
  1949. $this->versionField = $mapping['fieldName'];
  1950. if ( ! isset($mapping['default'])) {
  1951. if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
  1952. $mapping['default'] = 1;
  1953. } else if ($mapping['type'] == 'datetime') {
  1954. $mapping['default'] = 'CURRENT_TIMESTAMP';
  1955. } else {
  1956. throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
  1957. }
  1958. }
  1959. }
  1960. /**
  1961. * Sets whether this class is to be versioned for optimistic locking.
  1962. *
  1963. * @param boolean $bool
  1964. */
  1965. public function setVersioned($bool)
  1966. {
  1967. $this->isVersioned = $bool;
  1968. }
  1969. /**
  1970. * Sets the name of the field that is to be used for versioning if this class is
  1971. * versioned for optimistic locking.
  1972. *
  1973. * @param string $versionField
  1974. */
  1975. public function setVersionField($versionField)
  1976. {
  1977. $this->versionField = $versionField;
  1978. }
  1979. /**
  1980. * Mark this class as read only, no change tracking is applied to it.
  1981. *
  1982. * @return void
  1983. */
  1984. public function markReadOnly()
  1985. {
  1986. $this->isReadOnly = true;
  1987. }
  1988. /**
  1989. * A numerically indexed list of field names of this persistent class.
  1990. *
  1991. * This array includes identifier fields if present on this class.
  1992. *
  1993. * @return array
  1994. */
  1995. public function getFieldNames()
  1996. {
  1997. return array_keys($this->fieldMappings);
  1998. }
  1999. /**
  2000. * A numerically indexed list of association names of this persistent class.
  2001. *
  2002. * This array includes identifier associations if present on this class.
  2003. *
  2004. * @return array
  2005. */
  2006. public function getAssociationNames()
  2007. {
  2008. return array_keys($this->associationMappings);
  2009. }
  2010. /**
  2011. * Returns the target class name of the given association.
  2012. *
  2013. * @param string $assocName
  2014. * @return string
  2015. */
  2016. public function getAssociationTargetClass($assocName)
  2017. {
  2018. if ( ! isset($this->associationMappings[$assocName])) {
  2019. throw new \InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
  2020. }
  2021. return $this->associationMappings[$assocName]['targetEntity'];
  2022. }
  2023. /**
  2024. * Get fully-qualified class name of this persistent class.
  2025. *
  2026. * @return string
  2027. */
  2028. public function getName()
  2029. {
  2030. return $this->name;
  2031. }
  2032. /**
  2033. * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
  2034. *
  2035. * @param AbstractPlatform $platform
  2036. * @return array
  2037. */
  2038. public function getQuotedIdentifierColumnNames($platform)
  2039. {
  2040. $quotedColumnNames = array();
  2041. foreach ($this->identifier as $idProperty) {
  2042. if (isset($this->fieldMappings[$idProperty])) {
  2043. $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
  2044. ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
  2045. : $this->fieldMappings[$idProperty]['columnName'];
  2046. continue;
  2047. }
  2048. // Association defined as Id field
  2049. $joinColumns = $this->associationMappings[$idProperty]['joinColumns'];
  2050. $assocQuotedColumnNames = array_map(
  2051. function ($joinColumn) {
  2052. return isset($joinColumn['quoted'])
  2053. ? $platform->quoteIdentifier($joinColumn['name'])
  2054. : $joinColumn['name'];
  2055. },
  2056. $joinColumns
  2057. );
  2058. $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
  2059. }
  2060. return $quotedColumnNames;
  2061. }
  2062. /**
  2063. * Gets the (possibly quoted) column name of a mapped field for safe use
  2064. * in an SQL statement.
  2065. *
  2066. * @param string $field
  2067. * @param AbstractPlatform $platform
  2068. * @return string
  2069. */
  2070. public function getQuotedColumnName($field, $platform)
  2071. {
  2072. return isset($this->fieldMappings[$field]['quoted'])
  2073. ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
  2074. : $this->fieldMappings[$field]['columnName'];
  2075. }
  2076. /**
  2077. * Gets the (possibly quoted) primary table name of this class for safe use
  2078. * in an SQL statement.
  2079. *
  2080. * @param AbstractPlatform $platform
  2081. * @return string
  2082. */
  2083. public function getQuotedTableName($platform)
  2084. {
  2085. return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name'];
  2086. }
  2087. /**
  2088. * Gets the (possibly quoted) name of the join table.
  2089. *
  2090. * @param AbstractPlatform $platform
  2091. * @return string
  2092. */
  2093. public function getQuotedJoinTableName(array $assoc, $platform)
  2094. {
  2095. return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name'];
  2096. }
  2097. /**
  2098. * @param string $fieldName
  2099. * @return bool
  2100. */
  2101. public function isAssociationInverseSide($fieldName)
  2102. {
  2103. return isset($this->associationMappings[$fieldName]) && ! $this->associationMappings[$fieldName]['isOwningSide'];
  2104. }
  2105. /**
  2106. * @param string $fieldName
  2107. * @return string
  2108. */
  2109. public function getAssociationMappedByTargetField($fieldName)
  2110. {
  2111. return $this->associationMappings[$fieldName]['mappedBy'];
  2112. }
  2113. }