diff --git a/lib/Doctrine/Relation/Parser.php b/lib/Doctrine/Relation/Parser.php new file mode 100644 index 000000000..0dd71bf3e --- /dev/null +++ b/lib/Doctrine/Relation/Parser.php @@ -0,0 +1,282 @@ +. + */ +/** + * Doctrine_Relation_Parser + * + * @author Konsta Vesterinen + * @package Doctrine + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version $Revision: 1397 $ + * @category Object Relational Mapping + * @link www.phpdoctrine.com + * @since 1.0 + */ +class Doctrine_Relation_Parser +{ + /** + * @var Doctrine_Table $_table the table object this parser belongs to + */ + protected $_table; + /** + * @var array $_relations an array containing all the Doctrine_Relation objects for this table + */ + protected $_relations = array(); + /** + * @var array $_bound bound relations + */ + protected $_bound = array(); + /** + * @var array $_boundAliases bound relation aliases + */ + protected $_boundAliases = array(); + /** + * constructor + * + * @param Doctrine_Table $table the table object this parser belongs to + */ + public function __construct(Doctrine_Table $table) + { + $this->_table = $table; + } + /** + * getTable + * + * @return Doctrine_Table the table object this parser belongs to + */ + public function getTable() + { + return $this->_table; + } + + /** + * binds a relation + * + * @param string $name + * @param string $field + * @return void + */ + public function bind($name, $field, $type, $options = null) + { + if (isset($this->relations[$name])) { + unset($this->relations[$name]); + } + + $lower = strtolower($name); + + if (isset($this->columns[$lower])) { + throw new Doctrine_Table_Exception("Couldn't bind relation. Column with name " . $lower . ' already exists!'); + } + + $e = explode(' as ', $name); + $name = $e[0]; + + if (isset($e[1])) { + $alias = $e[1]; + $this->boundAliases[$name] = $alias; + } else { + $alias = $name; + } + + $this->bound[$alias] = array('field' => $field, + 'type' => $type, + 'class' => $name, + 'alias' => $alias); + if ($options !== null) { + $opt = array(); + if (is_string($options)) { + $opt['local'] = $options; + } else { + $opt = (array) $options; + } + + $this->bound[$alias] = array_merge($this->bound[$alias], $opt); + } + } + /** + * getRelation + * + * @param string $name component name of which a foreign key object is bound + * @return Doctrine_Relation + */ + public function getRelation($name, $recursive = true) + { + if (isset($this->relations[$name])) { + return $this->relations[$name]; + } + + if ( ! $this->conn->hasTable($this->options['name'])) { + $allowExport = true; + } else { + $allowExport = false; + } + + if (isset($this->bound[$name])) { + + $definition = $this->bound[$name]; + + list($component, $tmp) = explode('.', $definition['field']); + + if ( ! isset($definition['foreign'])) { + $definition['foreign'] = $tmp; + } + + unset($definition['field']); + + $definition['table'] = $this->conn->getTable($definition['class'], $allowExport); + $definition['constraint'] = false; + + if ($component == $this->options['name'] || in_array($component, $this->options['parents'])) { + + // ONE-TO-ONE + if ($definition['type'] == Doctrine_Relation::ONE_COMPOSITE || + $definition['type'] == Doctrine_Relation::ONE_AGGREGATE) { + // tree structure parent relation found + + if ( ! isset($definition['local'])) { + $definition['local'] = $definition['foreign']; + $definition['foreign'] = $definition['table']->getIdentifier(); + } + + $relation = new Doctrine_Relation_LocalKey($definition); + + } else { + // tree structure children relation found + + if ( ! isset($definition['local'])) { + $tmp = $definition['table']->getIdentifier(); + + $definition['local'] = $tmp; + } + + //$definition['foreign'] = $tmp; + $definition['constraint'] = true; + + $relation = new Doctrine_Relation_ForeignKey($definition); + } + + } elseif ($component == $definition['class'] || + ($component == $definition['alias'])) { // && ($name == $this->options['name'] || in_array($name,$this->parents)) + + if ( ! isset($defintion['local'])) { + $definition['local'] = $this->identifier; + } + + $definition['constraint'] = true; + + // ONE-TO-MANY or ONE-TO-ONE + $relation = new Doctrine_Relation_ForeignKey($definition); + + } else { + // MANY-TO-MANY + // only aggregate relations allowed + + if ($definition['type'] != Doctrine_Relation::MANY_AGGREGATE) { + throw new Doctrine_Table_Exception("Only aggregate relations are allowed for many-to-many relations"); + } + + $classes = array_merge($this->options['parents'], array($this->options['name'])); + + foreach (array_reverse($classes) as $class) { + try { + $bound = $definition['table']->getBoundForName($class, $component); + break; + } catch(Doctrine_Table_Exception $exc) { } + } + if ( ! isset($bound)) { + throw new Doctrine_Table_Exception("Couldn't map many-to-many relation for " + . $this->options['name'] . " and $name. Components use different join tables."); + } + if ( ! isset($definition['local'])) { + $definition['local'] = $this->identifier; + } + $e2 = explode('.', $bound['field']); + $fields = explode('-', $e2[1]); + + if ($e2[0] != $component) { + throw new Doctrine_Table_Exception($e2[0] . ' doesn\'t match ' . $component); + } + $associationTable = $this->conn->getTable($e2[0], $allowExport); + + if (count($fields) > 1) { + // SELF-REFERENCING THROUGH JOIN TABLE + + $def['table'] = $associationTable; + $def['local'] = $this->identifier; + $def['foreign'] = $fields[0]; + $def['alias'] = $e2[0]; + $def['class'] = $e2[0]; + $def['type'] = Doctrine_Relation::MANY_COMPOSITE; + + $this->relations[$e2[0]] = new Doctrine_Relation_ForeignKey($def); + + $definition['assocTable'] = $associationTable; + $definition['local'] = $fields[0]; + $definition['foreign'] = $fields[1]; + $relation = new Doctrine_Relation_Association_Self($definition); + } else { + if($definition['table'] === $this) { + + } else { + // auto initialize a new one-to-one relationships for association table + $associationTable->bind($this->getComponentName(), + $associationTable->getComponentName(). '.' . $e2[1], + Doctrine_Relation::ONE_AGGREGATE + ); + + $associationTable->bind($definition['table']->getComponentName(), + $associationTable->getComponentName(). '.' . $definition['foreign'], + Doctrine_Relation::ONE_AGGREGATE + ); + + // NORMAL MANY-TO-MANY RELATIONSHIP + + $def['table'] = $associationTable; + $def['foreign'] = $e2[1]; + $def['local'] = $definition['local']; + $def['alias'] = $e2[0]; + $def['class'] = $e2[0]; + $def['type'] = Doctrine_Relation::MANY_COMPOSITE; + $this->relations[$e2[0]] = new Doctrine_Relation_ForeignKey($def); + + $definition['local'] = $e2[1]; + $definition['assocTable'] = $associationTable; + $relation = new Doctrine_Relation_Association($definition); + } + } + } + + $this->relations[$name] = $relation; + + return $this->relations[$name]; + } + + + // load all relations + $this->getRelations(); + + if ($recursive) { + return $this->getRelation($name, false); + } else { + throw new Doctrine_Table_Exception($this->options['name'] . " doesn't have a relation to " . $name); + } + + } +}