2008-05-26 00:10:41 +04:00
|
|
|
<?php
|
2008-02-11 20:08:22 +03:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* This software consists of voluntary contributions made by many individuals
|
|
|
|
* and is licensed under the LGPL. For more information, see
|
|
|
|
* <http://www.phpdoctrine.org>.
|
|
|
|
*/
|
2008-02-04 00:29:57 +03:00
|
|
|
|
2008-07-21 00:13:24 +04:00
|
|
|
#namespace Doctrine::ORM::Internal;
|
|
|
|
|
2008-02-04 00:29:57 +03:00
|
|
|
/**
|
2008-02-11 20:08:22 +03:00
|
|
|
* The metadata factory is used to create ClassMetadata objects that contain all the
|
|
|
|
* metadata of a class.
|
2008-02-04 00:29:57 +03:00
|
|
|
*
|
2008-05-26 00:10:41 +04:00
|
|
|
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
|
|
|
* @author Roman Borschel <roman@code-factory.org>
|
|
|
|
* @package Doctrine
|
|
|
|
* @subpackage ClassMetadata
|
|
|
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
|
|
* @version $Revision$
|
|
|
|
* @link www.phpdoctrine.org
|
|
|
|
* @since 2.0
|
2008-07-21 00:13:24 +04:00
|
|
|
* @todo Rename to ClassMetadataFactory.
|
2008-02-04 00:29:57 +03:00
|
|
|
*/
|
|
|
|
class Doctrine_ClassMetadata_Factory
|
|
|
|
{
|
2008-07-21 00:13:24 +04:00
|
|
|
protected $_em;
|
2008-02-04 00:29:57 +03:00
|
|
|
protected $_driver;
|
|
|
|
|
|
|
|
/**
|
2008-02-11 20:08:22 +03:00
|
|
|
* The already loaded metadata objects.
|
2008-02-04 00:29:57 +03:00
|
|
|
*/
|
|
|
|
protected $_loadedMetadata = array();
|
|
|
|
|
2008-02-11 20:08:22 +03:00
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
* Creates a new factory instance that uses the given connection and metadata driver
|
|
|
|
* implementations.
|
|
|
|
*
|
|
|
|
* @param $conn The connection to use.
|
|
|
|
* @param $driver The metadata driver to use.
|
|
|
|
*/
|
2008-05-17 16:22:24 +04:00
|
|
|
public function __construct(Doctrine_EntityManager $em, $driver)
|
2008-02-04 00:29:57 +03:00
|
|
|
{
|
2008-07-21 00:13:24 +04:00
|
|
|
$this->_em = $em;
|
2008-02-04 00:29:57 +03:00
|
|
|
$this->_driver = $driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the metadata object for a class.
|
|
|
|
*
|
|
|
|
* @param string $className The name of the class.
|
|
|
|
* @return Doctrine_Metadata
|
|
|
|
*/
|
|
|
|
public function getMetadataFor($className)
|
2008-05-24 23:56:35 +04:00
|
|
|
{
|
2008-02-04 00:29:57 +03:00
|
|
|
if (isset($this->_loadedMetadata[$className])) {
|
|
|
|
return $this->_loadedMetadata[$className];
|
|
|
|
}
|
|
|
|
$this->_loadClasses($className, $this->_loadedMetadata);
|
|
|
|
|
|
|
|
return $this->_loadedMetadata[$className];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the metadata of the class in question and all it's ancestors whose metadata
|
|
|
|
* is still not loaded.
|
|
|
|
*
|
|
|
|
* @param string $name The name of the class for which the metadata should get loaded.
|
|
|
|
* @param array $tables The metadata collection to which the loaded metadata is added.
|
|
|
|
*/
|
|
|
|
protected function _loadClasses($name, array &$classes)
|
|
|
|
{
|
|
|
|
$parentClass = $name;
|
|
|
|
$parentClasses = array();
|
|
|
|
$loadedParentClass = false;
|
|
|
|
while ($parentClass = get_parent_class($parentClass)) {
|
2008-05-14 01:20:34 +04:00
|
|
|
if ($parentClass == 'Doctrine_Entity') {
|
2008-02-04 00:29:57 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (isset($classes[$parentClass])) {
|
|
|
|
$loadedParentClass = $parentClass;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$parentClasses[] = $parentClass;
|
|
|
|
}
|
|
|
|
$parentClasses = array_reverse($parentClasses);
|
|
|
|
$parentClasses[] = $name;
|
|
|
|
|
|
|
|
if ($loadedParentClass) {
|
|
|
|
$class = $classes[$loadedParentClass];
|
|
|
|
} else {
|
|
|
|
$rootClassOfHierarchy = count($parentClasses) > 0 ? array_shift($parentClasses) : $name;
|
2008-07-21 00:13:24 +04:00
|
|
|
$class = new Doctrine_ClassMetadata($rootClassOfHierarchy, $this->_em);
|
2008-02-04 00:29:57 +03:00
|
|
|
$this->_loadMetadata($class, $rootClassOfHierarchy);
|
|
|
|
$classes[$rootClassOfHierarchy] = $class;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($parentClasses) == 0) {
|
|
|
|
return $class;
|
|
|
|
}
|
|
|
|
|
|
|
|
// load metadata of subclasses
|
|
|
|
// -> child1 -> child2 -> $name
|
|
|
|
|
|
|
|
$parent = $class;
|
|
|
|
foreach ($parentClasses as $subclassName) {
|
2008-07-21 00:13:24 +04:00
|
|
|
$subClass = new Doctrine_ClassMetadata($subclassName, $this->_em);
|
2008-02-04 00:29:57 +03:00
|
|
|
$subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions());
|
|
|
|
$this->_addInheritedFields($subClass, $parent);
|
|
|
|
$this->_addInheritedRelations($subClass, $parent);
|
|
|
|
$this->_loadMetadata($subClass, $subclassName);
|
2008-03-23 14:30:29 +03:00
|
|
|
if ($parent->getInheritanceType() == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) {
|
2008-02-04 00:29:57 +03:00
|
|
|
$subClass->setTableName($parent->getTableName());
|
|
|
|
}
|
|
|
|
$classes[$subclassName] = $subClass;
|
|
|
|
$parent = $subClass;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function _addInheritedFields($subClass, $parentClass)
|
|
|
|
{
|
2008-07-21 00:13:24 +04:00
|
|
|
foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) {
|
2008-02-04 00:29:57 +03:00
|
|
|
$fullName = "$name as " . $parentClass->getFieldName($name);
|
2008-07-21 00:13:24 +04:00
|
|
|
$mapping['inherited'] = true;
|
|
|
|
$subClass->mapField($mapping);
|
2008-02-04 00:29:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-26 00:10:41 +04:00
|
|
|
protected function _addInheritedRelations($subClass, $parentClass)
|
|
|
|
{
|
2008-03-26 14:10:45 +03:00
|
|
|
foreach ($parentClass->getRelationParser()->getRelationDefinitions() as $name => $definition) {
|
|
|
|
$subClass->getRelationParser()->addRelationDefinition($name, $definition);
|
2008-02-04 00:29:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-03-26 14:10:45 +03:00
|
|
|
* Loads the metadata of a specified class.
|
|
|
|
*
|
|
|
|
* @param Doctrine_ClassMetadata $class The container for the metadata.
|
|
|
|
* @param string $name The name of the class for which the metadata will be loaded.
|
2008-02-04 00:29:57 +03:00
|
|
|
*/
|
|
|
|
protected function _loadMetadata(Doctrine_ClassMetadata $class, $name)
|
|
|
|
{
|
|
|
|
if ( ! class_exists($name) || empty($name)) {
|
|
|
|
throw new Doctrine_Exception("Couldn't find class " . $name . ".");
|
|
|
|
}
|
|
|
|
|
|
|
|
$names = array();
|
|
|
|
$className = $name;
|
|
|
|
// get parent classes
|
2008-07-21 00:13:24 +04:00
|
|
|
//TODO: Skip Entity types MappedSuperclass/Transient
|
2008-02-04 00:29:57 +03:00
|
|
|
do {
|
2008-05-14 01:20:34 +04:00
|
|
|
if ($className === 'Doctrine_Entity') {
|
2008-02-04 00:29:57 +03:00
|
|
|
break;
|
|
|
|
} else if ($className == $name) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$names[] = $className;
|
|
|
|
} while ($className = get_parent_class($className));
|
|
|
|
|
|
|
|
if ($className === false) {
|
|
|
|
throw new Doctrine_ClassMetadata_Factory_Exception("Unknown component '$className'.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// save parents
|
2008-02-11 20:08:22 +03:00
|
|
|
$class->setParentClasses($names);
|
2008-02-04 00:29:57 +03:00
|
|
|
|
|
|
|
// load further metadata
|
|
|
|
$this->_driver->loadMetadataForClass($name, $class);
|
|
|
|
|
2008-07-21 00:13:24 +04:00
|
|
|
// set default table name, if necessary
|
2008-02-04 00:29:57 +03:00
|
|
|
$tableName = $class->getTableName();
|
|
|
|
if ( ! isset($tableName)) {
|
|
|
|
$class->setTableName(Doctrine::tableize($class->getClassName()));
|
|
|
|
}
|
|
|
|
|
2008-07-21 00:13:24 +04:00
|
|
|
// complete identifier mapping
|
2008-02-04 00:29:57 +03:00
|
|
|
$this->_initIdentifier($class);
|
|
|
|
|
|
|
|
return $class;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes the class identifier(s)/primary key(s).
|
|
|
|
*
|
|
|
|
* @param Doctrine_Metadata The metadata container of the class in question.
|
|
|
|
*/
|
|
|
|
protected function _initIdentifier(Doctrine_ClassMetadata $class)
|
|
|
|
{
|
2008-07-21 00:13:24 +04:00
|
|
|
/*switch (count($class->getIdentifier())) {
|
2008-07-10 21:17:58 +04:00
|
|
|
case 0: // No identifier in the class mapping yet
|
|
|
|
|
|
|
|
// If its a subclass, inherit the identifier from the parent.
|
2008-03-23 14:30:29 +03:00
|
|
|
if ($class->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED &&
|
2008-07-10 21:17:58 +04:00
|
|
|
count($class->getParentClasses()) > 0) {
|
2008-03-26 14:10:45 +03:00
|
|
|
$parents = $class->getParentClasses();
|
2008-02-04 00:29:57 +03:00
|
|
|
$root = end($parents);
|
|
|
|
$rootClass = $class->getConnection()->getMetadata($root);
|
|
|
|
$class->setIdentifier($rootClass->getIdentifier());
|
|
|
|
|
|
|
|
if ($class->getIdentifierType() !== Doctrine::IDENTIFIER_AUTOINC) {
|
|
|
|
$class->setIdentifierType($rootClass->getIdentifierType());
|
|
|
|
} else {
|
|
|
|
$class->setIdentifierType(Doctrine::IDENTIFIER_NATURAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add all inherited primary keys
|
2008-07-21 00:13:24 +04:00
|
|
|
foreach ($class->getIdentifier() as $id) {
|
2008-02-04 00:29:57 +03:00
|
|
|
$definition = $rootClass->getDefinitionOf($id);
|
|
|
|
|
|
|
|
// inherited primary keys shouldn't contain autoinc
|
|
|
|
// and sequence definitions
|
|
|
|
unset($definition['autoincrement']);
|
|
|
|
unset($definition['sequence']);
|
|
|
|
|
|
|
|
// add the inherited primary key column
|
|
|
|
$fullName = $rootClass->getColumnName($id) . ' as ' . $id;
|
|
|
|
$class->setColumn($fullName, $definition['type'], $definition['length'],
|
|
|
|
$definition, true);
|
|
|
|
}
|
|
|
|
} else {
|
2008-07-21 00:13:24 +04:00
|
|
|
throw Doctrine_MappingException::identifierRequired($class->getClassName());
|
2008-02-04 00:29:57 +03:00
|
|
|
}
|
|
|
|
break;
|
2008-07-10 21:17:58 +04:00
|
|
|
case 1: // A single identifier is in the mapping
|
|
|
|
foreach ($class->getIdentifier() as $pk) {
|
2008-02-04 00:29:57 +03:00
|
|
|
$columnName = $class->getColumnName($pk);
|
2008-07-11 14:48:04 +04:00
|
|
|
$thisColumns = $class->getFieldMappings();
|
2008-02-04 00:29:57 +03:00
|
|
|
$e = $thisColumns[$columnName];
|
|
|
|
|
|
|
|
$found = false;
|
|
|
|
|
|
|
|
foreach ($e as $option => $value) {
|
|
|
|
if ($found) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$e2 = explode(':', $option);
|
|
|
|
|
|
|
|
switch (strtolower($e2[0])) {
|
|
|
|
case 'autoincrement':
|
|
|
|
case 'autoinc':
|
|
|
|
$class->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC);
|
|
|
|
$found = true;
|
|
|
|
break;
|
|
|
|
case 'seq':
|
|
|
|
case 'sequence':
|
|
|
|
$class->setIdentifierType(Doctrine::IDENTIFIER_SEQUENCE);
|
|
|
|
$found = true;
|
|
|
|
|
|
|
|
if ($value) {
|
2008-02-11 20:08:22 +03:00
|
|
|
$class->setTableOption('sequenceName', $value);
|
2008-02-04 00:29:57 +03:00
|
|
|
} else {
|
|
|
|
if (($sequence = $class->getAttribute(Doctrine::ATTR_DEFAULT_SEQUENCE)) !== null) {
|
2008-02-11 20:08:22 +03:00
|
|
|
$class->setTableOption('sequenceName', $sequence);
|
2008-02-04 00:29:57 +03:00
|
|
|
} else {
|
2008-02-11 20:08:22 +03:00
|
|
|
$class->setTableOption('sequenceName', $class->getConnection()
|
|
|
|
->getSequenceName($class->getTableName()));
|
2008-02-04 00:29:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$identifierType = $class->getIdentifierType();
|
|
|
|
if ( ! isset($identifierType)) {
|
|
|
|
$class->setIdentifierType(Doctrine::IDENTIFIER_NATURAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-24 23:31:49 +03:00
|
|
|
$class->setIdentifier(array($pk));
|
2008-02-04 00:29:57 +03:00
|
|
|
|
|
|
|
break;
|
2008-07-10 21:17:58 +04:00
|
|
|
default: // Multiple identifiers are in the mapping so its a composite id
|
2008-02-04 00:29:57 +03:00
|
|
|
$class->setIdentifierType(Doctrine::IDENTIFIER_COMPOSITE);
|
2008-07-21 00:13:24 +04:00
|
|
|
}*/
|
|
|
|
|
|
|
|
// If the chosen generator type is "auto", then pick the one appropriate for
|
|
|
|
// the database.
|
|
|
|
|
|
|
|
// FIXME: This is very ugly here. Such switch()es on the database driver
|
|
|
|
// are unnecessary as we can easily replace them with polymorphic calls on
|
|
|
|
// the connection (or another) object. We just need to decide where to put
|
|
|
|
// the id generation types.
|
|
|
|
if ($class->getIdGeneratorType() == Doctrine_ClassMetadata::GENERATOR_TYPE_AUTO) {
|
|
|
|
switch (strtolower($this->_em->getConnection()->getDriverName())) {
|
|
|
|
case 'mysql':
|
|
|
|
// pick IDENTITY
|
|
|
|
$class->setIdGeneratorType(Doctrine_ClassMetadata::GENERATOR_TYPE_IDENTITY);
|
|
|
|
break;
|
|
|
|
case 'oracle':
|
|
|
|
//pick SEQUENCE
|
|
|
|
break;
|
|
|
|
case 'postgres':
|
|
|
|
//pick SEQUENCE
|
|
|
|
break;
|
|
|
|
case 'firebird':
|
|
|
|
//pick what?
|
|
|
|
break;
|
|
|
|
case 'mssql':
|
|
|
|
//pick what?
|
|
|
|
default:
|
|
|
|
throw new Doctrine_Exception("Encountered unknown database driver: "
|
|
|
|
. $this->_em->getConnection()->getDriverName());
|
|
|
|
}
|
2008-02-04 00:29:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|