From 180d435e3d6405fd53cccf6a0dc235162db3c458 Mon Sep 17 00:00:00 2001 From: zYne Date: Fri, 16 Feb 2007 22:54:59 +0000 Subject: [PATCH] Relation model rewrite, first draft --- lib/Doctrine/Collection.php | 17 +-- lib/Doctrine/Connection.php | 6 +- lib/Doctrine/Connection/Pgsql.php | 14 ++ lib/Doctrine/Export.php | 16 ++- lib/Doctrine/Export/Mysql.php | 35 +++-- lib/Doctrine/Lib.php | 32 ++--- lib/Doctrine/Manager.php | 2 +- lib/Doctrine/Record.php | 15 ++- lib/Doctrine/Relation.php | 125 ++++++++++------- lib/Doctrine/Relation/Association.php | 43 ++---- lib/Doctrine/Relation/Association/Self.php | 35 +++-- lib/Doctrine/Relation/ForeignKey.php | 19 +-- lib/Doctrine/Relation/LocalKey.php | 10 +- lib/Doctrine/Table.php | 150 ++++++++++++++------- 14 files changed, 304 insertions(+), 215 deletions(-) diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php index ec33c4e81..f5c24a125 100644 --- a/lib/Doctrine/Collection.php +++ b/lib/Doctrine/Collection.php @@ -261,7 +261,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator * * @return void */ - public function setReference(Doctrine_Record $record,Doctrine_Relation $relation) + public function setReference(Doctrine_Record $record, Doctrine_Relation $relation) { $this->reference = $record; $this->relation = $relation; @@ -607,30 +607,27 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $list[] = $value; } }; - $query->from($this->table->getComponentName()."(".implode(", ",$this->table->getPrimaryKeys()).")"); - $query->where($this->table->getComponentName().".id IN (".substr(str_repeat("?, ", count($list)),0,-2).")"); + $query->from($this->table->getComponentName() . '(' . implode(", ",$this->table->getPrimaryKeys()) . ')'); + $query->where($this->table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')'); return $query; } $rel = $this->table->getRelation($name); - $table = $rel->getTable(); - $foreign = $rel->getForeign(); - $local = $rel->getLocal(); if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) { foreach ($this->data as $record) { - $list[] = $record[$local]; - }; + $list[] = $record[$rel->getLocal()]; + } } else { foreach ($this->data as $record) { $value = $record->getIncremented(); if ($value !== null) { $list[] = $value; } - }; + } } - $this->table->getRelation($name); + $dql = $rel->getRelationDql(count($list), 'collection'); $coll = $query->query($dql, $list); diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 884640da0..adff7c71c 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -18,6 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ +Doctrine::autoload('Doctrine_Configurable'); /** * Doctrine_Connection * @@ -481,8 +482,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun . 'VALUES (' . substr(str_repeat('?, ', count($values)), 0, -2) . ')'; // prepare and execute the statement - $stmt = $this->dbh->prepare($query); - $stmt->execute(array_values($values)); + $this->execute($query, array_values($values)); return true; } @@ -684,7 +684,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun } } catch(Doctrine_Adapter_Exception $e) { } catch(PDOException $e) { } - + print Doctrine_Lib::formatSql($query); $this->rethrowException($e); } /** diff --git a/lib/Doctrine/Connection/Pgsql.php b/lib/Doctrine/Connection/Pgsql.php index e8a3dc4d6..28ac9bcdc 100644 --- a/lib/Doctrine/Connection/Pgsql.php +++ b/lib/Doctrine/Connection/Pgsql.php @@ -89,6 +89,20 @@ class Doctrine_Connection_Pgsql extends Doctrine_Connection_Common $query = 'SET NAMES '.$this->dbh->quote($charset); $this->exec($query); } + /** + * convertBoolean + * some drivers need the boolean values to be converted into integers + * when using DQL API + * + * This method takes care of that conversion + * + * @param array $item + * @return void + */ + public function convertBooleans(array $items) + { + return $items; + } /** * Changes a query string for various DBMS specific reasons * diff --git a/lib/Doctrine/Export.php b/lib/Doctrine/Export.php index 6351c8db7..9927abcec 100644 --- a/lib/Doctrine/Export.php +++ b/lib/Doctrine/Export.php @@ -615,7 +615,7 @@ class Doctrine_Export extends Doctrine_Connection_Module * The onDelete and onUpdate keys accept the following values: * * CASCADE: Delete or update the row from the parent table and automatically delete or - * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. + * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column * in the parent table or in the child table. * @@ -624,7 +624,7 @@ class Doctrine_Export extends Doctrine_Connection_Module * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported. * * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary - * key value is not allowed to proceed if there is a related foreign key value in the referenced table. + * key value is not allowed to proceed if there is a related foreign key value in the referenced table. * * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as * omitting the ON DELETE or ON UPDATE clause. @@ -634,9 +634,9 @@ class Doctrine_Export extends Doctrine_Connection_Module * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint * of a field declaration. */ - public function getForeignKeyDeclaration($definition) + public function getForeignKeyDeclaration(array $definition) { - $sql = $this->getForeignKeyBaseDeclaration(); + $sql = $this->getForeignKeyBaseDeclaration($definition); if (isset($definition['deferred'])) { $sql .= ' ' . $this->getForeignKeyDeferredDeclaration(); @@ -673,15 +673,19 @@ class Doctrine_Export extends Doctrine_Connection_Module } /** * getForeignKeyBaseDeclaration + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a field declaration to be used in statements like CREATE TABLE. * + * @param array $definition * @return string */ - public function getForeignKeyBaseDeclaration() + public function getForeignKeyBaseDeclaration(array $definition) { $sql = ''; if (isset($definition['name'])) { - $sql .= 'CONSTRAINT ' . $definition['name']; + $sql .= 'CONSTRAINT ' . $definition['name'] . ' '; } + $sql .= 'FOREIGN KEY '; if ( ! isset($definition['local'])) { throw new Doctrine_Export_Exception('Local reference field missing from definition.'); } diff --git a/lib/Doctrine/Export/Mysql.php b/lib/Doctrine/Export/Mysql.php index 1edc1319c..ed374bc43 100644 --- a/lib/Doctrine/Export/Mysql.php +++ b/lib/Doctrine/Export/Mysql.php @@ -86,7 +86,6 @@ class Doctrine_Export_Mysql extends Doctrine_Export * 'comment' => 'Foo', * 'charset' => 'utf8', * 'collate' => 'utf8_unicode_ci', - * 'collate' => 'utf8_unicode_ci', * 'type' => 'innodb', * ); * @@ -106,6 +105,13 @@ class Doctrine_Export_Mysql extends Doctrine_Export $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition); } } + + if (isset($options['foreignKeys']) && ! empty($options['foreignKeys'])) { + foreach($options['foreignKeys'] as $definition) { + $queryFields .= ', ' . $this->getForeignKeyDeclaration($definition); + } + } + if (isset($options['primary']) && ! empty($options['primary'])) { $queryFields .= ', PRIMARY KEY(' . implode(', ', array_values($options['primary'])) . ')'; } @@ -333,13 +339,17 @@ class Doctrine_Export_Mysql extends Doctrine_Export $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($sequenceName), true); $seqcolName = $this->conn->quoteIdentifier($this->conn->getAttribute(Doctrine::ATTR_SEQCOL_NAME), true); - $query = 'CREATE TABLE ' . $sequenceName - . ' (' . $seqcolName . ' INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (' - . $seqcolName . '))' - . strlen($this->conn->default_table_type) ? ' TYPE = ' - . $this->conn->default_table_type : ''; - - $res = $this->conn->exec($query); + try { + $query = 'CREATE TABLE ' . $sequenceName + . ' (' . $seqcolName . ' INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (' + . $seqcolName . '))' + . strlen($this->conn->default_table_type) ? ' TYPE = ' + . $this->conn->default_table_type : ''; + + $res = $this->conn->exec($query); + } catch(Doctrine_Connection_Exception $e) { + throw new Doctrine_Export_Exception('could not create sequence table'); + } if ($start == 1) return true; @@ -352,11 +362,11 @@ class Doctrine_Export_Mysql extends Doctrine_Export // Handle error try { $result = $this->conn->exec('DROP TABLE ' . $sequenceName); - } catch(Exception $e) { + } catch(Doctrine_Connection_Exception $e) { throw new Doctrine_Export_Exception('could not drop inconsistent sequence table'); } - throw new Doctrine_Export_Exception('could not create sequence table'); + } /** * Get the stucture of a field into an array @@ -462,9 +472,12 @@ class Doctrine_Export_Mysql extends Doctrine_Export } } - if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) { + if ( ! isset($definition['fields'])) { throw new Doctrine_Export_Exception('No index columns given.'); } + if ( ! is_array($definition['fields'])) { + $definition['fields'] = array($definition['fields']); + } $query = $type . 'INDEX ' . $name; diff --git a/lib/Doctrine/Lib.php b/lib/Doctrine/Lib.php index 467e98d09..322ff562e 100644 --- a/lib/Doctrine/Lib.php +++ b/lib/Doctrine/Lib.php @@ -54,7 +54,7 @@ class Doctrine_Lib case Doctrine_Record::STATE_TCLEAN: return "transient clean"; break; - }; + } } /** * returns a string representation of Doctrine_Record object @@ -89,7 +89,7 @@ class Doctrine_Lib case Doctrine_Transaction::STATE_ACTIVE: return "active"; break; - }; + } } /** * returns a string representation of Doctrine_Connection object @@ -98,28 +98,12 @@ class Doctrine_Lib */ public static function getConnectionAsString(Doctrine_Connection $connection) { - $r[] = "
";
-        $r[] = "Doctrine_Connection object";
-        $r[] = "State               : ".Doctrine_Lib::getConnectionStateAsString($connection->getTransaction()->getState());
-        $r[] = "Open Transactions   : ".$connection->getTransaction()->getTransactionLevel();
-        $r[] = "Table in memory     : ".$connection->count();
-
-        $queries = false;
-        if ($connection->getDBH() instanceof Doctrine_Db) {
-            $handler = "Doctrine Database Handler";
-            $queries = count($connection->getDBH()->getQueries());
-            $sum     = array_sum($connection->getDBH()->getExecTimes());
-        } elseif ($connection->getDBH() instanceof PDO) {
-            $handler = "PHP Native PDO Driver";
-        } else {
-            $handler = "Unknown Database Handler";
-        }
-
-        $r[] = "DB Handler          : ".$handler;
-        if ($queries) {
-            $r[] = "Executed Queries    : ".$queries;
-            $r[] = "Sum of Exec Times   : ".$sum;
-        }
+        $r[] = '
';
+        $r[] = 'Doctrine_Connection object';
+        $r[] = 'State               : ' . Doctrine_Lib::getConnectionStateAsString($connection->transaction->getState());
+        $r[] = 'Open Transactions   : ' . $connection->transaction->getTransactionLevel();
+        $r[] = 'Table in memory     : ' . $connection->count();
+        $r[] = 'Driver name         : ' . $connection->getDbh()->getAttribute(Doctrine::ATTR_DRIVER_NAME);
 
         $r[] = "
"; return implode("\n",$r)."
"; diff --git a/lib/Doctrine/Manager.php b/lib/Doctrine/Manager.php index 4ac6098dd..ca67d81a2 100644 --- a/lib/Doctrine/Manager.php +++ b/lib/Doctrine/Manager.php @@ -231,7 +231,7 @@ class Doctrine_Manager extends Doctrine_Configurable implements Countable, Itera $this->connections[$name] = new Doctrine_Connection_Mock($this, $adapter); break; default: - throw new Doctrine_Manager_Exception('Unknown connection driver '. $adapter->getAttribute(PDO::ATTR_DRIVER_NAME)); + throw new Doctrine_Manager_Exception('Unknown connection driver '. $adapter->getAttribute(Doctrine::ATTR_DRIVER_NAME)); }; if ($setCurrent) { diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index 43e15a8d7..82915950a 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -775,7 +775,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite if ( ! isset($this->references[$name])) { $this->loadReference($name); } - } catch(Doctrine_Table_Exception $e) { + } catch(Doctrine_Table_Exception $e) { + print $e; throw new Doctrine_Record_Exception("Unknown property / related component '$name'."); } @@ -1525,6 +1526,17 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->_table->setOption($name, $value); } } + /** + * index + * defines a foreignKey + * + * @param array $definition the definition array + * @return void + */ + public function foreignKey(array $definition = array()) + { + return $this->_table->addForeignKey($definition); + } /** * index * defines or retrieves an index @@ -1533,7 +1545,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * * @param string $name the name of the index * @param array $definition the definition array - * @param array $options an array of options * @return mixed */ public function index($name, array $definition = array()) diff --git a/lib/Doctrine/Relation.php b/lib/Doctrine/Relation.php index 34cb00a91..ad3f9d00c 100644 --- a/lib/Doctrine/Relation.php +++ b/lib/Doctrine/Relation.php @@ -53,41 +53,66 @@ abstract class Doctrine_Relation */ const MANY_COMPOSITE = 3; + const ONE = 0; + const MANY = 1; + + protected $definition = array('alias' => true, + 'foreign' => true, + 'local' => true, + 'table' => true, + ); /** - * @var Doctrine_Table $table foreign factory + * constructor + * + * @param array $definition an associative array with the following structure: + * name related class name + * + * local the local field(s) + * + * foreign the foreign reference field(s) + * + * table the foreign table object + * + * assocTable the association table object (if any) + * + * onDelete referential delete action + * + * onUpdate referential update action + * + * deferred deferred constraint checking + * + * alias relation alias + * + * type the relation type, either Doctrine_Relation::ONE or Doctrine_Relation::MANY + * + * The onDelete and onUpdate keys accept the following values: + * + * CASCADE: Delete or update the row from the parent table and automatically delete or + * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. + * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column + * in the parent table or in the child table. + * + * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the + * child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier + * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported. + * + * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary + * key value is not allowed to proceed if there is a related foreign key value in the referenced table. + * + * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as + * omitting the ON DELETE or ON UPDATE clause. + * + * SET DEFAULT */ - protected $table; - /** - * @var string $local local field - */ - protected $local; - /** - * @var string $foreign foreign field - */ - protected $foreign; - /** - * @var integer $type bind type - */ - protected $type; - /** - * @var string $alias relation alias - */ - protected $alias; - - /** - * @param Doctrine_Table $table - * @param string $local - * @param string $foreign - * @param integer $type - * @param string $alias - */ - public function __construct(Doctrine_Table $table, $local, $foreign, $type, $alias) + public function __construct(array $definition) { - $this->table = $table; - $this->local = $local; - $this->foreign = $foreign; - $this->type = $type; - $this->alias = $alias; + foreach (array_keys($this->definition) as $key) { + if ( ! isset($definition[$key])) { + throw new Doctrine_Exception($key . ' is required!'); + } + } + + $this->definition = $definition; } /** * getAlias @@ -97,7 +122,7 @@ abstract class Doctrine_Relation */ final public function getAlias() { - return $this->alias; + return $this->definition['alias']; } /** * getType @@ -108,7 +133,7 @@ abstract class Doctrine_Relation */ final public function getType() { - return $this->type; + return $this->definition['type']; } /** * getTable @@ -118,7 +143,7 @@ abstract class Doctrine_Relation */ final public function getTable() { - return $this->table; + return $this->definition['table']; } /** * getLocal @@ -128,7 +153,7 @@ abstract class Doctrine_Relation */ final public function getLocal() { - return $this->local; + return $this->definition['local']; } /** * getForeign @@ -139,7 +164,7 @@ abstract class Doctrine_Relation */ final public function getForeign() { - return $this->foreign; + return $this->definition['foreign']; } /** * isComposite @@ -149,8 +174,8 @@ abstract class Doctrine_Relation */ final public function isComposite() { - return ($this->type == Doctrine_Relation::ONE_COMPOSITE || - $this->type == Doctrine_Relation::MANY_COMPOSITE); + return ($this->definition['type'] == Doctrine_Relation::ONE_COMPOSITE || + $this->definition['type'] == Doctrine_Relation::MANY_COMPOSITE); } /** * isOneToOne @@ -160,8 +185,8 @@ abstract class Doctrine_Relation */ final public function isOneToOne() { - return ($this->type == Doctrine_Relation::ONE_AGGREGATE || - $this->type == Doctrine_Relation::ONE_COMPOSITE); + return ($this->definition['type'] == Doctrine_Relation::ONE_AGGREGATE || + $this->definition['type'] == Doctrine_Relation::ONE_COMPOSITE); } /** * getRelationDql @@ -171,8 +196,10 @@ abstract class Doctrine_Relation */ public function getRelationDql($count) { - $dql = 'FROM ' . $this->table->getComponentName() - . ' WHERE ' . $this->table->getComponentName() . '.' . $this->foreign + $component = $this->definition['table']->getComponentName(); + + $dql = 'FROM ' . $component + . ' WHERE ' . $component . '.' . $this->definition['foreign'] . ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')'; return $dql; @@ -278,12 +305,12 @@ abstract class Doctrine_Relation public function __toString() { $r[] = "
";
-        $r[] = "Class       : ".get_class($this);
-        $r[] = "Component   : ".$this->table->getComponentName();
-        $r[] = "Table       : ".$this->table->getTableName();
-        $r[] = "Local key   : ".$this->local;
-        $r[] = "Foreign key : ".$this->foreign;
-        $r[] = "Type        : ".$this->type;
+        foreach ($this->definition as $k => $v) {
+            if(is_object($v)) {
+                $v = 'Object(' . get_class($v) . ')';
+            }
+            $r[] = $k . ' : ' . $v;
+        }
         $r[] = "
"; return implode("\n", $r); } diff --git a/lib/Doctrine/Relation/Association.php b/lib/Doctrine/Relation/Association.php index ecaca74fe..cdcfe7f4c 100644 --- a/lib/Doctrine/Relation/Association.php +++ b/lib/Doctrine/Relation/Association.php @@ -35,30 +35,12 @@ Doctrine::autoload('Doctrine_Relation'); */ class Doctrine_Relation_Association extends Doctrine_Relation { - /** - * @var Doctrine_Table $associationTable - */ - protected $associationTable; - /** - * the constructor - * @param Doctrine_Table $table foreign factory object - * @param Doctrine_Table $associationTable factory which handles the association - * @param string $local local field name - * @param string $foreign foreign field name - * @param integer $type type of relation - * @see Doctrine_Table constants - */ - public function __construct(Doctrine_Table $table, Doctrine_Table $associationTable, $local, $foreign, $type, $alias) - { - parent::__construct($table, $local, $foreign, $type, $alias); - $this->associationTable = $associationTable; - } /** * @return Doctrine_Table */ public function getAssociationFactory() { - return $this->associationTable; + return $this->definition['assocTable']; } /** * processDiff @@ -106,23 +88,26 @@ class Doctrine_Relation_Association extends Doctrine_Relation */ public function getRelationDql($count, $context = 'record') { + $component = $this->definition['assocTable']->getComponentName(); switch ($context) { case "record": - $sub = 'SQL:SELECT ' . $this->foreign. - ' FROM ' . $this->associationTable->getTableName(). - ' WHERE ' . $this->local. + $sub = 'SQL:SELECT ' . $this->definition['foreign']. + ' FROM ' . $this->definition['assocTable']->getTableName(). + ' WHERE ' . $this->definition['local'] . ' IN (' . substr(str_repeat("?, ", $count),0,-2) . ')'; - $dql = "FROM ".$this->table->getComponentName(); - $dql .= ".".$this->associationTable->getComponentName(); - $dql .= " WHERE ".$this->table->getComponentName().".".$this->table->getIdentifier()." IN ($sub)"; + $dql = 'FROM ' . $this->definition['table']->getComponentName(); + $dql .= '.' . $component; + $dql .= ' WHERE ' . $this->definition['table']->getComponentName() + . '.' . $this->definition['table']->getIdentifier() . ' IN (' . $sub . ')'; break; case "collection": $sub = substr(str_repeat("?, ", $count),0,-2); - $dql = "FROM ".$this->associationTable->getComponentName().".".$this->table->getComponentName(); - $dql .= " WHERE ".$this->associationTable->getComponentName().".".$this->local." IN ($sub)"; - }; + $dql = 'FROM ' . $component . '.' . $this->definition['table']->getComponentName(); + $dql .= ' WHERE ' . $component . '.' . $this->definition['local'] . ' IN (' . $sub . ')'; + break; + } return $dql; } @@ -138,7 +123,7 @@ class Doctrine_Relation_Association extends Doctrine_Relation { $id = $record->getIncremented(); if (empty($id)) { - $coll = new Doctrine_Collection($this->table); + $coll = new Doctrine_Collection($this->definition['table']); } else { $coll = Doctrine_Query::create()->parseQuery($this->getRelationDql(1))->execute(array($id)); } diff --git a/lib/Doctrine/Relation/Association/Self.php b/lib/Doctrine/Relation/Association/Self.php index 92baf6169..850959ced 100644 --- a/lib/Doctrine/Relation/Association/Self.php +++ b/lib/Doctrine/Relation/Association/Self.php @@ -42,24 +42,31 @@ class Doctrine_Relation_Association_Self extends Doctrine_Relation_Association { switch ($context) { case 'record': - $sub = 'SELECT '.$this->foreign. - ' FROM '.$this->associationTable->getTableName(). - ' WHERE '.$this->local. - ' = ?'; - $sub2 = 'SELECT '.$this->local. - ' FROM '.$this->associationTable->getTableName(). - ' WHERE '.$this->foreign. - ' = ?'; + $sub = 'SELECT '.$this->definition['foreign'] + . ' FROM '.$this->definition['assocTable']->getTableName() + . ' WHERE '.$this->definition['local'] + . ' = ?'; - $dql = 'FROM ' . $this->table->getComponentName(); - $dql .= '.' . $this->associationTable->getComponentName(); - $dql .= ' WHERE ' . $this->table->getComponentName() . '.' . $this->table->getIdentifier() . ' IN (' . $sub . ')'; - $dql .= ' || ' . $this->table->getComponentName() . '.' . $this->table->getIdentifier() . ' IN (' . $sub2 . ')'; + $sub2 = 'SELECT '.$this->definition['local'] + . ' FROM '.$this->definition['assocTable']->getTableName() + . ' WHERE '.$this->definition['foreign'] + . ' = ?'; + + $dql = 'FROM ' . $this->definition['table']->getComponentName() + . '.' . $this->definition['assocTable']->getComponentName() + . ' WHERE ' . $this->definition['table']->getComponentName() + . '.' . $this->definition['table']->getIdentifier() + . ' IN (' . $sub . ')' + . ' || ' . $this->definition['table']->getComponentName() + . '.' . $this->definition['table']->getIdentifier() + . ' IN (' . $sub2 . ')'; break; case 'collection': $sub = substr(str_repeat('?, ', $count),0,-2); - $dql = 'FROM '.$this->associationTable->getComponentName().'.'.$this->table->getComponentName(); - $dql .= ' WHERE '.$this->associationTable->getComponentName().'.'.$this->local.' IN ('.$sub.')'; + $dql = 'FROM '.$this->definition['assocTable']->getComponentName() + . '.' . $this->definition['table']->getComponentName() + . ' WHERE '.$this->definition['assocTable']->getComponentName() + . '.' . $this->definition['local'] . ' IN (' . $sub . ')'; }; return $dql; diff --git a/lib/Doctrine/Relation/ForeignKey.php b/lib/Doctrine/Relation/ForeignKey.php index 1d2394b1f..4f456d384 100644 --- a/lib/Doctrine/Relation/ForeignKey.php +++ b/lib/Doctrine/Relation/ForeignKey.php @@ -37,6 +37,7 @@ class Doctrine_Relation_ForeignKey extends Doctrine_Relation * processDiff * * @param Doctrine_Record $record + * @return void */ public function processDiff(Doctrine_Record $record) { @@ -75,29 +76,29 @@ class Doctrine_Relation_ForeignKey extends Doctrine_Relation */ public function fetchRelatedFor(Doctrine_Record $record) { - $id = $record->get($this->local); + $id = $record->get($this->definition['local']); if ($this->isOneToOne()) { if (empty($id)) { - $related = $this->table->create(); + $related = $this->definition['table']->create(); } else { - $dql = 'FROM ' . $this->table->getComponentName() - . ' WHERE ' . $this->table->getComponentName() - . '.' . $this->foreign . ' = ?'; + $dql = 'FROM ' . $this->definition['table']->getComponentName() + . ' WHERE ' . $this->definition['table']->getComponentName() + . '.' . $this->definition['foreign'] . ' = ?'; - $coll = $this->table->getConnection()->query($dql, array($id)); + $coll = $this->definition['table']->getConnection()->query($dql, array($id)); $related = $coll[0]; } - $related->set($this->foreign, $record, false); + $related->set($this->definition['foreign'], $record, false); } else { if (empty($id)) { - $related = new Doctrine_Collection($this->table); + $related = new Doctrine_Collection($this->definition['table']); } else { $query = $this->getRelationDql(1); - $related = $this->table->getConnection()->query($query, array($id)); + $related = $this->definition['table']->getConnection()->query($query, array($id)); } $related->setReference($record, $this); } diff --git a/lib/Doctrine/Relation/LocalKey.php b/lib/Doctrine/Relation/LocalKey.php index 0b93465c5..cfbd6b3d0 100644 --- a/lib/Doctrine/Relation/LocalKey.php +++ b/lib/Doctrine/Relation/LocalKey.php @@ -58,17 +58,17 @@ class Doctrine_Relation_LocalKey extends Doctrine_Relation */ public function fetchRelatedFor(Doctrine_Record $record) { - $id = $record->get($this->local); + $id = $record->get($this->definition['local']); if (empty($id)) { - $related = $this->table->create(); + $related = $this->definition['table']->create(); } else { - if ( ! ($related = $this->table->find($id))) { - $related = $this->table->create(); + if ( ! ($related = $this->definition['table']->find($id))) { + $related = $this->definition['table']->create(); } } - $record->set($this->local, $related, false); + $record->set($this->definition['local'], $related, false); return $related; } diff --git a/lib/Doctrine/Table.php b/lib/Doctrine/Table.php index c5d5db34e..adaeb4649 100644 --- a/lib/Doctrine/Table.php +++ b/lib/Doctrine/Table.php @@ -140,6 +140,8 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable * * -- charset character set * + * -- foreignKeys the foreign keys of this table + * * -- collation * * -- indexes the index definitions of this table @@ -412,6 +414,17 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable { return isset($this->options[$option]); } + /** + * addForeignKey + * + * adds a foreignKey to this table + * + * @return void + */ + public function addForeignKey(array $definition) + { + $this->options['foreignKeys'][] = $definition; + } /** * addIndex * @@ -664,9 +677,9 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable { foreach ($this->bound as $k => $bound) { - $e = explode('.', $bound[0]); + $e = explode('.', $bound['field']); - if ($bound[3] == $name && $e[0] == $component) { + if ($bound['name'] == $name && $e[0] == $component) { return $this->bound[$k]; } } @@ -738,7 +751,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable * @param string $field * @return void */ - public function bind($name, $field, $type, $localKey) + public function bind($name, $field, $type, $local = null, $options = array()) { if (isset($this->relations[$name])) { unset($this->relations[$name]); @@ -750,8 +763,8 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable throw new Doctrine_Table_Exception("Couldn't bind relation. Column with name " . $lower . ' already exists!'); } - $e = explode(' as ', $name); - $name = $e[0]; + $e = explode(' as ', $name); + $name = $e[0]; if (isset($e[1])) { $alias = $e[1]; @@ -760,7 +773,12 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable $alias = $name; } - $this->bound[$alias] = array($field, $type, $localKey, $name); + $this->bound[$alias] = array('field' => $field, + 'type' => $type, + 'local' => $local, + 'name' => $name, + 'options' => $options, + 'alias' => $alias); } /** * @return Doctrine_Connection @@ -775,7 +793,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable */ final public function hasRelatedComponent($name, $component) { - return (strpos($this->bound[$name][0], $component.'.') !== false); + return (strpos($this->bound[$name]['field'], $component . '.') !== false); } /** * @param string $name component name of which a foreign key object is bound @@ -799,59 +817,60 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable * @param string $name component name of which a foreign key object is bound * @return Doctrine_Relation */ - final public function getRelation($name, $recursive = true) + public function getRelation($name, $recursive = true) { - $original = $name; - if (isset($this->relations[$name])) { return $this->relations[$name]; } if (isset($this->bound[$name])) { - $type = $this->bound[$name][1]; - $local = $this->bound[$name][2]; - list($component, $foreign) = explode(".", $this->bound[$name][0]); - $alias = $name; - $name = $this->bound[$alias][3]; - $table = $this->conn->getTable($name); + $definition = $this->bound[$name]; - if ($component == $this->options['name'] || in_array($component, $this->parents)) { + list($component, $definition['foreign']) = explode('.', $definition['field']); + unset($definition['field']); + + $definition['table'] = $this->conn->getTable($definition['name']); + + if ($component == $this->options['name'] || in_array($component, $this->options['parents'])) { // ONE-TO-ONE - if ($type == Doctrine_Relation::ONE_COMPOSITE || - $type == Doctrine_Relation::ONE_AGGREGATE) { + if ($definition['type'] == Doctrine_Relation::ONE_COMPOSITE || + $definition['type'] == Doctrine_Relation::ONE_AGGREGATE) { // tree structure parent relation found - if ( ! isset($local)) { - $local = $table->getIdentifier(); + if ( ! isset($definition['local'])) { + $definition['local'] = $definition['foreign']; + $definition['foreign'] = $definition['table']->getIdentifier(); } - $relation = new Doctrine_Relation_LocalKey($table, $foreign, $local, $type, $alias); + + $relation = new Doctrine_Relation_LocalKey($definition); + } else { // tree structure children relation found - if ( ! isset($local)) { - $tmp = $table->getIdentifier(); + if ( ! isset($definition['local'])) { + $tmp = $definition['table']->getIdentifier(); } - $local = $foreign; - $foreign = $tmp; + $definition['local'] = $tmp; + //$definition['foreign'] = $tmp; - $relation = new Doctrine_Relation_ForeignKey($table, $foreign, $local, $type, $alias); + $relation = new Doctrine_Relation_ForeignKey($definition); } - } elseif ($component == $name || - ($component == $alias)) { // && ($name == $this->options['name'] || in_array($name,$this->parents)) + } elseif ($component == $definition['name'] || + ($component == $definition['alias'])) { // && ($name == $this->options['name'] || in_array($name,$this->parents)) - if ( ! isset($local)) { - $local = $this->identifier; + if ( ! isset($defintion['local'])) { + $definition['local'] = $this->identifier; } // ONE-TO-MANY or ONE-TO-ONE - $relation = new Doctrine_Relation_ForeignKey($table, $local, $foreign, $type, $alias); + $relation = new Doctrine_Relation_ForeignKey($definition); } else { // MANY-TO-MANY // only aggregate relations allowed - if ($type != Doctrine_Relation::MANY_AGGREGATE) { + if ($definition['type'] != Doctrine_Relation::MANY_AGGREGATE) { throw new Doctrine_Table_Exception("Only aggregate relations are allowed for many-to-many relations"); } @@ -859,7 +878,7 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable foreach (array_reverse($classes) as $class) { try { - $bound = $table->getBoundForName($class, $component); + $bound = $definition['table']->getBoundForName($class, $component); break; } catch(Doctrine_Table_Exception $exc) { } } @@ -867,50 +886,77 @@ class Doctrine_Table extends Doctrine_Configurable implements Countable 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($local)) { - $local = $this->identifier; + if ( ! isset($definition['local'])) { + $definition['local'] = $this->identifier; } - $e2 = explode('.', $bound[0]); + $e2 = explode('.', $bound['field']); $fields = explode('-', $e2[1]); - if ($e2[0] != $component) + if ($e2[0] != $component) { throw new Doctrine_Table_Exception($e2[0] . ' doesn\'t match ' . $component); - + } $associationTable = $this->conn->getTable($e2[0]); if (count($fields) > 1) { // SELF-REFERENCING THROUGH JOIN TABLE - $this->relations[$e2[0]] = new Doctrine_Relation_ForeignKey($associationTable, $local, $fields[0],Doctrine_Relation::MANY_COMPOSITE, $e2[0]); - $relation = new Doctrine_Relation_Association_Self($table, $associationTable, $fields[0], $fields[1], $type, $alias); + $def['table'] = $associationTable; + $def['local'] = $this->identifier; + $def['foreign'] = $fields[0]; + $def['alias'] = $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($table === $this) { - + if($definition['table'] === $this) { + } else { - // auto initialize a new one-to-one relationship for association table - $associationTable->bind($this->getComponentName(), $associationTable->getComponentName(). '.' .$e2[1], Doctrine_Relation::ONE_AGGREGATE, $this->getIdentifier()); - $associationTable->bind($table->getComponentName(), $associationTable->getComponentName(). '.' .$foreign, Doctrine_Relation::ONE_AGGREGATE, $table->getIdentifier()); + // 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 - $this->relations[$e2[0]] = new Doctrine_Relation_ForeignKey($associationTable, $local, $e2[1], Doctrine_Relation::MANY_COMPOSITE, $e2[0]); + + $def['table'] = $associationTable; + $def['foreign'] = $e2[1]; + $def['local'] = $definition['local']; + $def['alias'] = $e2[0]; + $def['type'] = Doctrine_Relation::MANY_COMPOSITE; + $this->relations[$e2[0]] = new Doctrine_Relation_ForeignKey($def); - $relation = new Doctrine_Relation_Association($table, $associationTable, $e2[1], $foreign, $type, $alias); + $definition['local'] = $e2[1]; + $definition['assocTable'] = $associationTable; + $relation = new Doctrine_Relation_Association($definition); } } } - $this->relations[$alias] = $relation; - return $this->relations[$alias]; + $this->relations[$name] = $relation; + + return $this->relations[$name]; } // load all relations $this->getRelations(); if ($recursive) { - return $this->getRelation($original, false); + return $this->getRelation($name, false); } else { - throw new Doctrine_Table_Exception($this->options['name'] . " doesn't have a relation to " . $original); + throw new Doctrine_Table_Exception($this->options['name'] . " doesn't have a relation to " . $name); } + } /** * returns an array containing all foreign key objects