PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Doctrine/Doctrine/Import/Schema.php

https://github.com/ostric/e-learning
PHP | 748 lines | 440 code | 88 blank | 220 comment | 97 complexity | e3f7ee85c9a8262611320e543cf29815 MD5 | raw file
  1. <?php
  2. /*
  3. * $Id: Schema.php 1838 2007-06-26 00:58:21Z nicobn $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information, see
  19. * <http://www.phpdoctrine.org>.
  20. */
  21. /**
  22. * Doctrine_Import_Schema
  23. *
  24. * Class for importing Doctrine_Record classes from a yaml schema definition
  25. *
  26. * @package Doctrine
  27. * @subpackage Import
  28. * @link www.phpdoctrine.org
  29. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  30. * @version $Revision: 1838 $
  31. * @author Nicolas BĂ©rard-Nault <nicobn@gmail.com>
  32. * @author Jonathan H. Wage <jonwage@gmail.com>
  33. */
  34. class Doctrine_Import_Schema
  35. {
  36. /**
  37. * _relations
  38. *
  39. * Array of all relationships parsed from all schema files
  40. *
  41. * @var array
  42. */
  43. protected $_relations = array();
  44. /**
  45. * _options
  46. *
  47. * Array of options used to configure the model generation from the parsed schema files
  48. * This array is forwarded to Doctrine_Import_Builder
  49. *
  50. * @var array
  51. */
  52. protected $_options = array('packagesPrefix' => 'Package',
  53. 'packagesPath' => '',
  54. 'packagesFolderName' => 'packages',
  55. 'suffix' => '.php',
  56. 'generateBaseClasses' => true,
  57. 'generateTableClasses' => false,
  58. 'generateAccessors' => false,
  59. 'baseClassesPrefix' => 'Base',
  60. 'baseClassesDirectory' => 'generated',
  61. 'baseClassName' => 'Doctrine_Record');
  62. /**
  63. * _validation
  64. *
  65. * Array used to validate schema element.
  66. * See: _validateSchemaElement
  67. *
  68. * @var array
  69. */
  70. protected $_validation = array('root' => array('abstract',
  71. 'connection',
  72. 'className',
  73. 'tableName',
  74. 'connection',
  75. 'relations',
  76. 'columns',
  77. 'indexes',
  78. 'attributes',
  79. 'templates',
  80. 'actAs',
  81. 'options',
  82. 'package',
  83. 'package_custom_path',
  84. 'inheritance',
  85. 'detect_relations',
  86. 'listeners',
  87. 'checks'),
  88. 'column' => array('name',
  89. 'format',
  90. 'fixed',
  91. 'primary',
  92. 'autoincrement',
  93. 'type',
  94. 'length',
  95. 'size',
  96. 'default',
  97. 'scale',
  98. 'values',
  99. 'comment',
  100. 'sequence',
  101. 'protected',
  102. 'zerofill',
  103. 'owner',
  104. 'extra'),
  105. 'relation' => array('key',
  106. 'class',
  107. 'alias',
  108. 'type',
  109. 'refClass',
  110. 'local',
  111. 'foreign',
  112. 'foreignClass',
  113. 'foreignAlias',
  114. 'foreignType',
  115. 'autoComplete',
  116. 'cascade',
  117. 'onDelete',
  118. 'onUpdate',
  119. 'equal',
  120. 'owningSide',
  121. 'refClassRelationAlias'),
  122. 'inheritance'=> array('type',
  123. 'extends',
  124. 'keyField',
  125. 'keyValue'));
  126. /**
  127. * _validators
  128. *
  129. * Array of available validators
  130. *
  131. * @see getValidators()
  132. * @var array Array of available validators
  133. */
  134. protected $_validators = array();
  135. /**
  136. * getValidators
  137. *
  138. * Retrieve the array of available validators
  139. *
  140. * @return array
  141. */
  142. public function getValidators()
  143. {
  144. if (empty($this->_validators)) {
  145. $this->_validators = Doctrine_Lib::getValidators();
  146. }
  147. return $this->_validators;
  148. }
  149. /**
  150. * getOption
  151. *
  152. * @param string $name
  153. * @return void
  154. */
  155. public function getOption($name)
  156. {
  157. if (isset($this->_options[$name])) {
  158. return $this->_options[$name];
  159. }
  160. }
  161. /**
  162. * getOptions
  163. *
  164. * @return void
  165. */
  166. public function getOptions()
  167. {
  168. return $this->_options;
  169. }
  170. /**
  171. * setOption
  172. *
  173. * @param string $name
  174. * @param string $value
  175. * @return void
  176. */
  177. public function setOption($name, $value)
  178. {
  179. if (isset($this->_options[$name])) {
  180. $this->_options[$name] = $value;
  181. }
  182. }
  183. /**
  184. * setOptions
  185. *
  186. * @param string $options
  187. * @return void
  188. */
  189. public function setOptions($options)
  190. {
  191. if ( ! empty($options)) {
  192. $this->_options = $options;
  193. }
  194. }
  195. /**
  196. * buildSchema
  197. *
  198. * Loop throug directories of schema files and parse them all in to one complete array of schema information
  199. *
  200. * @param string $schema Array of schema files or single schema file. Array of directories with schema files or single directory
  201. * @param string $format Format of the files we are parsing and building from
  202. * @return array $array
  203. */
  204. public function buildSchema($schema, $format)
  205. {
  206. $array = array();
  207. foreach ((array) $schema AS $s) {
  208. if (is_file($s)) {
  209. $array = array_merge($array, $this->parseSchema($s, $format));
  210. } else if (is_dir($s)) {
  211. $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($s),
  212. RecursiveIteratorIterator::LEAVES_ONLY);
  213. foreach ($it as $file) {
  214. $e = explode('.', $file->getFileName());
  215. if (end($e) === $format) {
  216. $array = array_merge($array, $this->parseSchema($file->getPathName(), $format));
  217. }
  218. }
  219. } else {
  220. $array = array_merge($array, $this->parseSchema($s, $format));
  221. }
  222. }
  223. $array = $this->_buildRelationships($array);
  224. $array = $this->_processInheritance($array);
  225. return $array;
  226. }
  227. /**
  228. * importSchema
  229. *
  230. * A method to import a Schema and translate it into a Doctrine_Record object
  231. *
  232. * @param string $schema The file containing the XML schema
  233. * @param string $format Format of the schema file
  234. * @param string $directory The directory where the Doctrine_Record class will be written
  235. * @param array $models Optional array of models to import
  236. *
  237. * @return void
  238. */
  239. public function importSchema($schema, $format = 'yml', $directory = null, $models = array())
  240. {
  241. $builder = new Doctrine_Import_Builder();
  242. $builder->setTargetPath($directory);
  243. $builder->setOptions($this->getOptions());
  244. $array = $this->buildSchema($schema, $format);
  245. if (count($array) == 0) {
  246. throw new Doctrine_Import_Exception(
  247. sprintf('No ' . $format . ' schema found in ' . implode(", ", $schema))
  248. );
  249. }
  250. foreach ($array as $name => $definition) {
  251. if ( ! empty($models) && !in_array($definition['className'], $models)) {
  252. continue;
  253. }
  254. $builder->buildRecord($definition);
  255. }
  256. }
  257. /**
  258. * parseSchema
  259. *
  260. * A method to parse a Schema and translate it into a property array.
  261. * The function returns that property array.
  262. *
  263. * @param string $schema Path to the file containing the schema
  264. * @param string $type Format type of the schema we are parsing
  265. * @return array $build Built array of schema information
  266. */
  267. public function parseSchema($schema, $type)
  268. {
  269. $defaults = array('abstract' => false,
  270. 'className' => null,
  271. 'tableName' => null,
  272. 'connection' => null,
  273. 'relations' => array(),
  274. 'indexes' => array(),
  275. 'attributes' => array(),
  276. 'templates' => array(),
  277. 'actAs' => array(),
  278. 'options' => array(),
  279. 'package' => null,
  280. 'inheritance' => array(),
  281. 'detect_relations' => false);
  282. $array = Doctrine_Parser::load($schema, $type);
  283. // Go through the schema and look for global values so we can assign them to each table/class
  284. $globals = array();
  285. $globalKeys = array('connection',
  286. 'attributes',
  287. 'templates',
  288. 'actAs',
  289. 'options',
  290. 'package',
  291. 'package_custom_path',
  292. 'inheritance',
  293. 'detect_relations');
  294. // Loop over and build up all the global values and remove them from the array
  295. foreach ($array as $key => $value) {
  296. if (in_array($key, $globalKeys)) {
  297. unset($array[$key]);
  298. $globals[$key] = $value;
  299. }
  300. }
  301. // Apply the globals to each table if it does not have a custom value set already
  302. foreach ($array as $className => $table) {
  303. foreach ($globals as $key => $value) {
  304. if (!isset($array[$className][$key])) {
  305. $array[$className][$key] = $value;
  306. }
  307. }
  308. }
  309. $build = array();
  310. foreach ($array as $className => $table) {
  311. $table = (array) $table;
  312. $this->_validateSchemaElement('root', array_keys($table), $className);
  313. $columns = array();
  314. $className = isset($table['className']) ? (string) $table['className']:(string) $className;
  315. if (isset($table['inheritance']['keyField']) || isset($table['inheritance']['keyValue'])) {
  316. $table['inheritance']['type'] = 'column_aggregation';
  317. }
  318. if (isset($table['tableName']) && $table['tableName']) {
  319. $tableName = $table['tableName'];
  320. } else {
  321. if (isset($table['inheritance']['type']) && ($table['inheritance']['type'] == 'column_aggregation')) {
  322. $tableName = null;
  323. } else {
  324. $tableName = Doctrine_Inflector::tableize($className);
  325. }
  326. }
  327. $connection = isset($table['connection']) ? $table['connection']:'current';
  328. $columns = isset($table['columns']) ? $table['columns']:array();
  329. if ( ! empty($columns)) {
  330. foreach ($columns as $columnName => $field) {
  331. // Support short syntax: my_column: integer(4)
  332. if ( ! is_array($field)) {
  333. $original = $field;
  334. $field = array();
  335. $field['type'] = $original;
  336. }
  337. $colDesc = array();
  338. if (isset($field['name'])) {
  339. $colDesc['name'] = $field['name'];
  340. } else {
  341. $colDesc['name'] = $columnName;
  342. }
  343. $this->_validateSchemaElement('column', array_keys($field), $className . '->columns->' . $colDesc['name']);
  344. // Support short type(length) syntax: my_column: { type: integer(4) }
  345. $e = explode('(', $field['type']);
  346. if (isset($e[0]) && isset($e[1])) {
  347. $colDesc['type'] = $e[0];
  348. $value = substr($e[1], 0, strlen($e[1]) - 1);
  349. $e = explode(',', $value);
  350. $colDesc['length'] = $e[0];
  351. if (isset($e[1]) && $e[1]) {
  352. $colDesc['scale'] = $e[1];
  353. }
  354. } else {
  355. $colDesc['type'] = isset($field['type']) ? (string) $field['type']:null;
  356. $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null;
  357. $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length'];
  358. }
  359. $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null;
  360. $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null;
  361. $colDesc['default'] = isset($field['default']) ? $field['default']:null;
  362. $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null;
  363. $colDesc['sequence'] = isset($field['sequence']) ? (string) $field['sequence']:null;
  364. $colDesc['values'] = isset($field['values']) ? (array) $field['values']:null;
  365. // Include all the specified and valid validators in the colDesc
  366. $validators = $this->getValidators();
  367. foreach ($validators as $validator) {
  368. if (isset($field[$validator])) {
  369. $colDesc[$validator] = $field[$validator];
  370. }
  371. }
  372. $columns[(string) $columnName] = $colDesc;
  373. }
  374. }
  375. // Apply the default values
  376. foreach ($defaults as $key => $defaultValue) {
  377. if (isset($table[$key]) && ! isset($build[$className][$key])) {
  378. $build[$className][$key] = $table[$key];
  379. } else {
  380. $build[$className][$key] = isset($build[$className][$key]) ? $build[$className][$key]:$defaultValue;
  381. }
  382. }
  383. $build[$className]['className'] = $className;
  384. $build[$className]['tableName'] = $tableName;
  385. $build[$className]['columns'] = $columns;
  386. // Make sure that anything else that is specified in the schema makes it to the final array
  387. $build[$className] = Doctrine_Lib::arrayDeepMerge($table, $build[$className]);
  388. // We need to keep track of the className for the connection
  389. $build[$className]['connectionClassName'] = $build[$className]['className'];
  390. }
  391. return $build;
  392. }
  393. /**
  394. * _processInheritance
  395. *
  396. * Perform some processing on inheritance.
  397. * Sets the default type and sets some default values for certain types
  398. *
  399. * @param string $array
  400. * @return void
  401. */
  402. protected function _processInheritance($array)
  403. {
  404. // Apply default inheritance configuration
  405. foreach ($array as $className => $definition) {
  406. if ( ! empty($array[$className]['inheritance'])) {
  407. $this->_validateSchemaElement('inheritance', array_keys($definition['inheritance']), $className . '->inheritance');
  408. // Default inheritance to concrete inheritance
  409. if ( ! isset($array[$className]['inheritance']['type'])) {
  410. $array[$className]['inheritance']['type'] = 'concrete';
  411. }
  412. // Some magic for setting up the keyField and keyValue column aggregation options
  413. // Adds keyField to the parent class automatically
  414. if ($array[$className]['inheritance']['type'] == 'column_aggregation') {
  415. // Set the keyField to 'type' by default
  416. if ( ! isset($array[$className]['inheritance']['keyField'])) {
  417. $array[$className]['inheritance']['keyField'] = 'type';
  418. }
  419. // Set the keyValue to the name of the child class if it does not exist
  420. if ( ! isset($array[$className]['inheritance']['keyValue'])) {
  421. $array[$className]['inheritance']['keyValue'] = $className;
  422. }
  423. $parent = $this->_findBaseSuperClass($array, $definition['className']);
  424. // Add the keyType column to the parent if a definition does not already exist
  425. if ( ! isset($array[$parent]['columns'][$array[$className]['inheritance']['keyField']])) {
  426. $array[$parent]['columns'][$array[$className]['inheritance']['keyField']] = array('name' => $array[$className]['inheritance']['keyField'], 'type' => 'string', 'length' => 255);
  427. }
  428. }
  429. }
  430. }
  431. // Array of the array keys to move to the parent, and the value to default the child definition to
  432. // after moving it. Will also populate the subclasses array for the inheritance parent
  433. $moves = array('columns' => array(),
  434. 'indexes' => array(),
  435. 'attributes' => array(),
  436. 'options' => array(),
  437. 'checks' => array());
  438. foreach ($array as $className => $definition) {
  439. // Move any definitions on the schema to the parent
  440. if (isset($definition['inheritance']['extends']) && isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
  441. $parent = $this->_findBaseSuperClass($array, $definition['className']);
  442. foreach ($moves as $move => $resetValue) {
  443. if (isset($array[$parent][$move]) && isset($definition[$move])) {
  444. $array[$parent][$move] = Doctrine_Lib::arrayDeepMerge($array[$parent][$move], $definition[$move]);
  445. $array[$definition['className']][$move] = $resetValue;
  446. }
  447. }
  448. // Populate the parents subclasses
  449. if ($definition['inheritance']['type'] == 'column_aggregation') {
  450. $array[$parent]['inheritance']['subclasses'][$definition['className']] = array($definition['inheritance']['keyField'] => $definition['inheritance']['keyValue']);
  451. }
  452. }
  453. }
  454. return $array;
  455. }
  456. /**
  457. * Find the base super class for this inheritance child. We need to move all levels of children to the
  458. * top most parent.
  459. *
  460. * @param array $array Array of schema information
  461. * @return string $class Class to get find the parent for
  462. */
  463. protected function _findBaseSuperClass($array, $class)
  464. {
  465. if (isset($array[$class]['inheritance']['extends']) && isset($array[$class]['inheritance']['type']) && ($array[$class]['inheritance']['type'] == 'simple' || $array[$class]['inheritance']['type'] == 'column_aggregation')) {
  466. return $this->_findBaseSuperClass($array, $array[$class]['inheritance']['extends']);
  467. } else {
  468. return $class;
  469. }
  470. }
  471. /**
  472. * buildRelationships
  473. *
  474. * Loop through an array of schema information and build all the necessary relationship information
  475. * Will attempt to auto complete relationships and simplify the amount of information required
  476. * for defining a relationship
  477. *
  478. * @param string $array
  479. * @return void
  480. */
  481. protected function _buildRelationships($array)
  482. {
  483. // Handle auto detecting relations by the names of columns
  484. // User.contact_id will automatically create User hasOne Contact local => contact_id, foreign => id
  485. foreach ($array as $className => $properties) {
  486. if (isset($properties['columns']) && ! empty($properties['columns']) && isset($properties['detect_relations']) && $properties['detect_relations']) {
  487. foreach ($properties['columns'] as $column) {
  488. // Check if the column we are inflecting has a _id on the end of it before trying to inflect it and find
  489. // the class name for the column
  490. if (strpos($column['name'], '_id')) {
  491. $columnClassName = Doctrine_Inflector::classify(str_replace('_id', '', $column['name']));
  492. if (isset($array[$columnClassName]) && !isset($array[$className]['relations'][$columnClassName])) {
  493. $array[$className]['relations'][$columnClassName] = array();
  494. // Set the detected foreign key type and length to the same as the primary key
  495. // of the related table
  496. $type = isset($array[$columnClassName]['columns']['id']['type']) ? $array[$columnClassName]['columns']['id']['type']:'integer';
  497. $length = isset($array[$columnClassName]['columns']['id']['length']) ? $array[$columnClassName]['columns']['id']['length']:8;
  498. $array[$className]['columns'][$column['name']]['type'] = $type;
  499. $array[$className]['columns'][$column['name']]['length'] = $length;
  500. }
  501. }
  502. }
  503. }
  504. }
  505. foreach ($array as $name => $properties) {
  506. if ( ! isset($properties['relations'])) {
  507. continue;
  508. }
  509. $className = $properties['className'];
  510. $relations = $properties['relations'];
  511. foreach ($relations as $alias => $relation) {
  512. $class = isset($relation['class']) ? $relation['class']:$alias;
  513. if ( ! isset($array[$class])) {
  514. continue;
  515. }
  516. $relation['class'] = $class;
  517. $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias;
  518. // Attempt to guess the local and foreign
  519. if (isset($relation['refClass'])) {
  520. $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine_Inflector::tableize($name) . '_id';
  521. $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:Doctrine_Inflector::tableize($class) . '_id';
  522. } else {
  523. $relation['local'] = isset($relation['local']) ? $relation['local']:Doctrine_Inflector::tableize($relation['class']) . '_id';
  524. $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:'id';
  525. }
  526. if (isset($relation['refClass'])) {
  527. $relation['type'] = 'many';
  528. }
  529. if (isset($relation['type']) && $relation['type']) {
  530. $relation['type'] = $relation['type'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
  531. } else {
  532. $relation['type'] = Doctrine_Relation::ONE;
  533. }
  534. if (isset($relation['foreignType']) && $relation['foreignType']) {
  535. $relation['foreignType'] = $relation['foreignType'] === 'one' ? Doctrine_Relation::ONE:Doctrine_Relation::MANY;
  536. }
  537. $relation['key'] = $this->_buildUniqueRelationKey($relation);
  538. $this->_validateSchemaElement('relation', array_keys($relation), $className . '->relation->' . $relation['alias']);
  539. $this->_relations[$className][$alias] = $relation;
  540. }
  541. }
  542. // Now we auto-complete opposite ends of relationships
  543. $this->_autoCompleteOppositeRelations();
  544. // Make sure we do not have any duplicate relations
  545. $this->_fixDuplicateRelations();
  546. // Set the full array of relationships for each class to the final array
  547. foreach ($this->_relations as $className => $relations) {
  548. $array[$className]['relations'] = $relations;
  549. }
  550. return $array;
  551. }
  552. /**
  553. * fixRelationships
  554. *
  555. * Loop through all relationships building the opposite ends of each relationship
  556. * and make sure no duplicate relations exist
  557. *
  558. * @return void
  559. */
  560. protected function _autoCompleteOppositeRelations()
  561. {
  562. foreach($this->_relations as $className => $relations) {
  563. foreach ($relations AS $alias => $relation) {
  564. if ((isset($relation['equal']) && $relation['equal']) || (isset($relation['autoComplete']) && $relation['autoComplete'] === false)) {
  565. continue;
  566. }
  567. $newRelation = array();
  568. $newRelation['foreign'] = $relation['local'];
  569. $newRelation['local'] = $relation['foreign'];
  570. $newRelation['class'] = isset($relation['foreignClass']) ? $relation['foreignClass']:$className;
  571. $newRelation['alias'] = isset($relation['foreignAlias']) ? $relation['foreignAlias']:$className;
  572. // this is so that we know that this relation was autogenerated and
  573. // that we do not need to include it if it is explicitly declared in the schema by the users.
  574. $newRelation['autogenerated'] = true;
  575. if (isset($relation['refClass'])) {
  576. $newRelation['refClass'] = $relation['refClass'];
  577. $newRelation['type'] = isset($relation['foreignType']) ? $relation['foreignType']:$relation['type'];
  578. } else {
  579. if(isset($relation['foreignType'])) {
  580. $newRelation['type'] = $relation['foreignType'];
  581. } else {
  582. $newRelation['type'] = $relation['type'] === Doctrine_Relation::ONE ? Doctrine_Relation::MANY:Doctrine_Relation::ONE;
  583. }
  584. }
  585. // Make sure it doesn't already exist
  586. if ( ! isset($this->_relations[$relation['class']][$newRelation['alias']])) {
  587. $newRelation['key'] = $this->_buildUniqueRelationKey($newRelation);
  588. $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation;
  589. }
  590. }
  591. }
  592. }
  593. /**
  594. * _fixDuplicateRelations
  595. *
  596. * Ensure the relations for each class are unique and that no duplicated relations exist from the auto generated relations
  597. * and the user explicitely defining the opposite end
  598. *
  599. * @return void
  600. */
  601. protected function _fixDuplicateRelations()
  602. {
  603. foreach($this->_relations as $className => $relations) {
  604. // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations
  605. $existingRelations = array();
  606. $uniqueRelations = array();
  607. foreach ($relations as $relation) {
  608. if ( ! in_array($relation['key'], $existingRelations)) {
  609. $existingRelations[] = $relation['key'];
  610. $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation));
  611. } else {
  612. // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it
  613. if ( ! isset($relation['autogenerated']) || $relation['autogenerated'] != true) {
  614. $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation));
  615. }
  616. }
  617. }
  618. $this->_relations[$className] = $uniqueRelations;
  619. }
  620. }
  621. /**
  622. * _buildUniqueRelationKey
  623. *
  624. * Build a unique key to identify a relationship by
  625. * Md5 hash of all the relationship parameters
  626. *
  627. * @param string $relation
  628. * @return void
  629. */
  630. protected function _buildUniqueRelationKey($relation)
  631. {
  632. return md5($relation['local'].$relation['foreign'].$relation['class'].(isset($relation['refClass']) ? $relation['refClass']:null));
  633. }
  634. /**
  635. * _validateSchemaElement
  636. *
  637. * @param string $name
  638. * @param string $value
  639. * @return void
  640. */
  641. protected function _validateSchemaElement($name, $element, $path)
  642. {
  643. $element = (array) $element;
  644. $validation = $this->_validation[$name];
  645. // Validators are a part of the column validation
  646. // This should be fixed, made cleaner
  647. if ($name == 'column') {
  648. $validators = $this->getValidators();
  649. $validation = array_merge($validation, $validators);
  650. }
  651. $validation = array_flip($validation);
  652. foreach ($element as $key => $value) {
  653. if ( ! isset($validation[$value])) {
  654. throw new Doctrine_Import_Exception(
  655. sprintf('Invalid schema element named "' . $value . '" at path "' . $path . '"')
  656. );
  657. }
  658. }
  659. }
  660. }