refactorings. merged hydration bugfix from 0.11.
This commit is contained in:
parent
74ce82bd50
commit
7b711ae70e
@ -867,7 +867,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab
|
|||||||
{
|
{
|
||||||
$columnName = $this->getColumnName($fieldName);
|
$columnName = $this->getColumnName($fieldName);
|
||||||
return isset($this->_mappedColumns[$columnName]['accessor']) ?
|
return isset($this->_mappedColumns[$columnName]['accessor']) ?
|
||||||
$this->_mappedColumns[$columnName]['accessor'] : null;
|
$this->_mappedColumns[$columnName]['accessor'] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -994,26 +994,7 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Countable, Ite
|
|||||||
*/
|
*/
|
||||||
public function get($fieldName, $load = false)
|
public function get($fieldName, $load = false)
|
||||||
{
|
{
|
||||||
/*// check for custom accessor, if not done yet.
|
$this->_invokeCustomAccessor($fieldName);
|
||||||
if ( ! isset(self::$_accessorCache[$this->_entityName][$fieldName])) {
|
|
||||||
if (self::$_useAutoAccessorOverride) {
|
|
||||||
$getterMethod = 'get' . Doctrine::classify($fieldName);
|
|
||||||
if (method_exists($this, $getterMethod)) {
|
|
||||||
self::$_accessorCache[$this->_entityName][$fieldName] = $getterMethod;
|
|
||||||
} else {
|
|
||||||
self::$_accessorCache[$this->_entityName][$fieldName] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($getter = $this->_class->getCustomAccessor($fieldName)) {
|
|
||||||
self::$_accessorCache[$this->_entityName][$fieldName] = $getter;
|
|
||||||
} else if ( ! isset(self::$_accessorCache[$this->_entityName][$fieldName])) {
|
|
||||||
self::$_accessorCache[$this->_entityName][$fieldName] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// invoke custom accessor, if it exists.
|
|
||||||
if ($getter = self::$_accessorCache[$this->_entityName][$fieldName]) {
|
|
||||||
return $this->$getter();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Use built-in accessor functionality
|
// Use built-in accessor functionality
|
||||||
$nullObj = Doctrine_Null::$INSTANCE;
|
$nullObj = Doctrine_Null::$INSTANCE;
|
||||||
@ -1046,6 +1027,29 @@ abstract class Doctrine_Entity extends Doctrine_Access implements Countable, Ite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function _invokeCustomAccessor($fieldName)
|
||||||
|
{
|
||||||
|
if ( ! isset(self::$_accessorCache[$this->_entityName][$fieldName])) {
|
||||||
|
if (self::$_useAutoAccessorOverride) {
|
||||||
|
$getterMethod = 'get' . Doctrine::classify($fieldName);
|
||||||
|
if (method_exists($this, $getterMethod)) {
|
||||||
|
self::$_accessorCache[$this->_entityName][$fieldName] = $getterMethod;
|
||||||
|
} else {
|
||||||
|
self::$_accessorCache[$this->_entityName][$fieldName] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($getter = $this->_class->getCustomAccessor($fieldName)) {
|
||||||
|
self::$_accessorCache[$this->_entityName][$fieldName] = $getter;
|
||||||
|
} else if ( ! isset(self::$_accessorCache[$this->_entityName][$fieldName])) {
|
||||||
|
self::$_accessorCache[$this->_entityName][$fieldName] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// invoke custom accessor, if it exists.
|
||||||
|
if ($getter = self::$_accessorCache[$this->_entityName][$fieldName]) {
|
||||||
|
return $this->$getter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getClassName()
|
public function getClassName()
|
||||||
{
|
{
|
||||||
return $this->_entityName;
|
return $this->_entityName;
|
||||||
|
@ -59,7 +59,7 @@ class Doctrine_EntityRepository
|
|||||||
if ( ! empty($alias)) {
|
if ( ! empty($alias)) {
|
||||||
$alias = ' ' . trim($alias);
|
$alias = ' ' . trim($alias);
|
||||||
}
|
}
|
||||||
return Doctrine_Query::create($this->_em)->from($this->_entityName . $alias);
|
return $this->_em->createQuery()->from($this->_entityName . $alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,11 +101,10 @@ class Doctrine_EntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds all entities of the mapper's class.
|
* Finds all entities in the repository.
|
||||||
* Use with care.
|
|
||||||
*
|
*
|
||||||
* @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
|
* @param int $hydrationMode
|
||||||
* @return Doctrine_Collection
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function findAll($hydrationMode = null)
|
public function findAll($hydrationMode = null)
|
||||||
{
|
{
|
||||||
|
@ -102,8 +102,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
|||||||
$driver = new Doctrine_Hydrator_RecordDriver($this->_em);
|
$driver = new Doctrine_Hydrator_RecordDriver($this->_em);
|
||||||
}
|
}
|
||||||
|
|
||||||
$event = new Doctrine_Event(null, Doctrine_Event::HYDRATE, null);
|
|
||||||
|
|
||||||
$s = microtime(true);
|
$s = microtime(true);
|
||||||
|
|
||||||
// Used variables during hydration
|
// Used variables during hydration
|
||||||
@ -112,8 +110,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
|||||||
$rootComponentName = $this->_queryComponents[$rootAlias]['table']->getClassName();
|
$rootComponentName = $this->_queryComponents[$rootAlias]['table']->getClassName();
|
||||||
// if only one class is involved we can make our lives easier
|
// if only one class is involved we can make our lives easier
|
||||||
$isSimpleQuery = count($this->_queryComponents) <= 1;
|
$isSimpleQuery = count($this->_queryComponents) <= 1;
|
||||||
// Holds hydration listeners that get called during hydration
|
|
||||||
$listeners = array();
|
|
||||||
// Lookup map to quickly discover/lookup existing records in the result
|
// Lookup map to quickly discover/lookup existing records in the result
|
||||||
// It's the identifier "memory"
|
// It's the identifier "memory"
|
||||||
$identifierMap = array();
|
$identifierMap = array();
|
||||||
@ -142,13 +138,11 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
|||||||
// disable lazy-loading of related elements during hydration
|
// disable lazy-loading of related elements during hydration
|
||||||
$component['table']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false);
|
$component['table']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false);
|
||||||
$componentName = $component['table']->getClassName();
|
$componentName = $component['table']->getClassName();
|
||||||
//$listeners[$componentName] = $component['table']->getRecordListener();
|
|
||||||
$identifierMap[$dqlAlias] = array();
|
$identifierMap[$dqlAlias] = array();
|
||||||
$resultPointers[$dqlAlias] = array();
|
$resultPointers[$dqlAlias] = array();
|
||||||
$idTemplate[$dqlAlias] = '';
|
$idTemplate[$dqlAlias] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$cache = array();
|
$cache = array();
|
||||||
// Evaluate HYDRATE_SINGLE_SCALAR
|
// Evaluate HYDRATE_SINGLE_SCALAR
|
||||||
if ($hydrationMode == Doctrine::HYDRATE_SINGLE_SCALAR) {
|
if ($hydrationMode == Doctrine::HYDRATE_SINGLE_SCALAR) {
|
||||||
@ -177,20 +171,10 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
|||||||
$class = $this->_queryComponents[$rootAlias]['table'];
|
$class = $this->_queryComponents[$rootAlias]['table'];
|
||||||
$componentName = $class->getComponentName();
|
$componentName = $class->getComponentName();
|
||||||
|
|
||||||
// just event stuff
|
|
||||||
//$event->set('data', $rowData[$rootAlias]);
|
|
||||||
//$listeners[$componentName]->preHydrate($event);
|
|
||||||
//--
|
|
||||||
|
|
||||||
// Check for an existing element
|
// Check for an existing element
|
||||||
$index = false;
|
$index = false;
|
||||||
if ($isSimpleQuery || ! isset($identifierMap[$rootAlias][$id[$rootAlias]])) {
|
if ($isSimpleQuery || ! isset($identifierMap[$rootAlias][$id[$rootAlias]])) {
|
||||||
$element = $driver->getElement($rowData[$rootAlias], $componentName);
|
$element = $driver->getElement($rowData[$rootAlias], $componentName);
|
||||||
|
|
||||||
// just event stuff
|
|
||||||
//$event->set('data', $element);
|
|
||||||
//$listeners[$componentName]->postHydrate($event);
|
|
||||||
//--
|
|
||||||
|
|
||||||
// do we need to index by a custom field?
|
// do we need to index by a custom field?
|
||||||
if ($field = $this->_getCustomIndexField($rootAlias)) {
|
if ($field = $this->_getCustomIndexField($rootAlias)) {
|
||||||
@ -230,17 +214,11 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now hydrate the rest of the data found in the current row, that belongs to other
|
// now hydrate the rest of the data found in the current row, that belongs to other
|
||||||
// (related) components.
|
// (related) classes.
|
||||||
foreach ($rowData as $dqlAlias => $data) {
|
foreach ($rowData as $dqlAlias => $data) {
|
||||||
$index = false;
|
$index = false;
|
||||||
$map = $this->_queryComponents[$dqlAlias];
|
$map = $this->_queryComponents[$dqlAlias];
|
||||||
$componentName = $map['table']->getComponentName();
|
$componentName = $map['table']->getClassName();
|
||||||
|
|
||||||
// just event stuff
|
|
||||||
//$event->set('data', $data);
|
|
||||||
//$listeners[$componentName]->preHydrate($event);
|
|
||||||
//--
|
|
||||||
|
|
||||||
$parent = $map['parent'];
|
$parent = $map['parent'];
|
||||||
$relation = $map['relation'];
|
$relation = $map['relation'];
|
||||||
$relationAlias = $relation->getAlias();
|
$relationAlias = $relation->getAlias();
|
||||||
@ -263,30 +241,18 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
|
|||||||
$oneToOne = false;
|
$oneToOne = false;
|
||||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||||
$driver->initRelatedCollection($baseElement, $relationAlias);
|
$driver->initRelatedCollection($baseElement, $relationAlias);
|
||||||
if ( ! isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) {
|
$indexExists = isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
|
||||||
|
$index = $indexExists ? $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
|
||||||
|
$indexIsValid = $index !== false ? isset($baseElement[$relationAlias][$index]) : false;
|
||||||
|
if ( ! $indexExists || ! $indexIsValid) {
|
||||||
$element = $driver->getElement($data, $componentName);
|
$element = $driver->getElement($data, $componentName);
|
||||||
|
|
||||||
// just event stuff
|
|
||||||
//$event->set('data', $element);
|
|
||||||
//$listeners[$componentName]->postHydrate($event);
|
|
||||||
//--
|
|
||||||
|
|
||||||
if ($field = $this->_getCustomIndexField($dqlAlias)) {
|
if ($field = $this->_getCustomIndexField($dqlAlias)) {
|
||||||
// TODO: must be checked in the parser. fields used in INDEXBY
|
|
||||||
// must be a) the primary key or b) unique & notnull
|
|
||||||
/*if ($driver->isIndexKeyInUse($baseElement, $relationAlias, $field)) {
|
|
||||||
throw Doctrine_Hydrator_Exception::nonUniqueKeyMapping();
|
|
||||||
} else if ( ! $driver->isFieldSet($element, $field)) {
|
|
||||||
throw Doctrine_Hydrator_Exception::nonExistantFieldUsedAsIndex($field);
|
|
||||||
}*/
|
|
||||||
$driver->addRelatedIndexedElement($baseElement, $relationAlias, $element, $field);
|
$driver->addRelatedIndexedElement($baseElement, $relationAlias, $element, $field);
|
||||||
} else {
|
} else {
|
||||||
$driver->addRelatedElement($baseElement, $relationAlias, $element);
|
$driver->addRelatedElement($baseElement, $relationAlias, $element);
|
||||||
}
|
}
|
||||||
$identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey(
|
$identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey(
|
||||||
$driver->getReferenceValue($baseElement, $relationAlias));
|
$driver->getReferenceValue($baseElement, $relationAlias));
|
||||||
} else {
|
|
||||||
$index = $identifierMap[$path][$id[$parent]][$id[$dqlAlias]];
|
|
||||||
}
|
}
|
||||||
} else if ( ! isset($baseElement[$relationAlias])) {
|
} else if ( ! isset($baseElement[$relationAlias])) {
|
||||||
$driver->setRelatedElement($baseElement, $relationAlias,
|
$driver->setRelatedElement($baseElement, $relationAlias,
|
||||||
|
@ -1,747 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* $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>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Mapper is responsible for mapping between the domain model and the database
|
|
||||||
* back and forth. Each entity in the domain model has a corresponding mapper.
|
|
||||||
*
|
|
||||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
|
||||||
* @package Doctrine
|
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
||||||
* @version $Revision: 3406 $
|
|
||||||
* @link www.phpdoctrine.org
|
|
||||||
* @since 2.0
|
|
||||||
* @todo Rename to "EntityPersister" or similar.
|
|
||||||
*/
|
|
||||||
class Doctrine_Mapper
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Metadata object that descibes the mapping of the mapped entity class.
|
|
||||||
*
|
|
||||||
* @var Doctrine_ClassMetadata
|
|
||||||
*/
|
|
||||||
protected $_classMetadata;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the domain class this mapper is used for.
|
|
||||||
*/
|
|
||||||
protected $_domainClassName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Doctrine_Connection object that the database connection of this mapper.
|
|
||||||
*
|
|
||||||
* @var Doctrine_Connection $conn
|
|
||||||
*/
|
|
||||||
protected $_conn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The concrete mapping strategy that is used.
|
|
||||||
*/
|
|
||||||
protected $_mappingStrategy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Null object.
|
|
||||||
*/
|
|
||||||
private $_nullObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of registered entity listeners.
|
|
||||||
*/
|
|
||||||
private $_entityListeners = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enter description here...
|
|
||||||
*
|
|
||||||
* @var unknown_type
|
|
||||||
* @todo To EntityManager.
|
|
||||||
*/
|
|
||||||
private $_dataTemplate = array();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new mapper.
|
|
||||||
*
|
|
||||||
* @param string $name The name of the domain class this mapper is used for.
|
|
||||||
* @param Doctrine_Table $table The table object used for the mapping procedure.
|
|
||||||
* @throws Doctrine_Connection_Exception if there are no opened connections
|
|
||||||
*/
|
|
||||||
public function __construct($name, Doctrine_ClassMetadata $classMetadata)
|
|
||||||
{
|
|
||||||
$this->_domainClassName = $name;
|
|
||||||
$this->_conn = $classMetadata->getConnection();
|
|
||||||
$this->_classMetadata = $classMetadata;
|
|
||||||
$this->_nullObject = Doctrine_Null::$INSTANCE;
|
|
||||||
if ($classMetadata->getInheritanceType() == Doctrine::INHERITANCE_TYPE_JOINED) {
|
|
||||||
$this->_mappingStrategy = new Doctrine_Mapper_JoinedStrategy($this);
|
|
||||||
} else {
|
|
||||||
$this->_mappingStrategy = new Doctrine_Mapper_DefaultStrategy($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets the connection for this class
|
|
||||||
*
|
|
||||||
* @params Doctrine_Connection a connection object
|
|
||||||
* @return Doctrine_Table this object
|
|
||||||
* @todo refactor
|
|
||||||
*/
|
|
||||||
public function setConnection(Doctrine_Connection $conn)
|
|
||||||
{
|
|
||||||
$this->_conn = $conn;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the connection the mapper is currently using.
|
|
||||||
*
|
|
||||||
* @return Doctrine_Connection|null The connection object.
|
|
||||||
*/
|
|
||||||
public function getConnection()
|
|
||||||
{
|
|
||||||
return $this->_conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates a new record
|
|
||||||
*
|
|
||||||
* @param $array an array where keys are field names and
|
|
||||||
* values representing field values
|
|
||||||
* @return Doctrine_Entity the created record object
|
|
||||||
* @todo To EntityManager.
|
|
||||||
*/
|
|
||||||
public function create(array $array = array())
|
|
||||||
{
|
|
||||||
$record = new $this->_domainClassName();
|
|
||||||
$record->fromArray($array);
|
|
||||||
|
|
||||||
return $record;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addEntityListener(Doctrine_Record_Listener $listener)
|
|
||||||
{
|
|
||||||
if ( ! in_array($listener, $this->_entityListeners)) {
|
|
||||||
$this->_entityListeners[] = $listener;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeEntityListener(Doctrine_Record_Listener $listener)
|
|
||||||
{
|
|
||||||
if ($key = array_search($listener, $this->_entityListeners, true)) {
|
|
||||||
unset($this->_entityListeners[$key]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function notifyEntityListeners(Doctrine_Entity $entity, $callback, $eventType)
|
|
||||||
{
|
|
||||||
if ($this->_entityListeners) {
|
|
||||||
$event = new Doctrine_Event($entity, $eventType);
|
|
||||||
foreach ($this->_entityListeners as $listener) {
|
|
||||||
$listener->$callback($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enter description here...
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $entity
|
|
||||||
* @return unknown
|
|
||||||
* @todo To EntityManager
|
|
||||||
*/
|
|
||||||
public function detach(Doctrine_Entity $entity)
|
|
||||||
{
|
|
||||||
return $this->_conn->unitOfWork->detach($entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clear
|
|
||||||
* clears the first level cache (identityMap)
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @todo what about a more descriptive name? clearIdentityMap?
|
|
||||||
* @todo To EntityManager
|
|
||||||
*/
|
|
||||||
public function clear()
|
|
||||||
{
|
|
||||||
$this->_conn->unitOfWork->clearIdentitiesForEntity($this->_classMetadata->getRootClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* addRecord
|
|
||||||
* adds a record to identity map
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record record to be added
|
|
||||||
* @return boolean
|
|
||||||
* @todo Better name? registerRecord? Move elsewhere to the new location of the identity maps.
|
|
||||||
* @todo Remove.
|
|
||||||
*/
|
|
||||||
public function addRecord(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
if ($this->_conn->unitOfWork->contains($record)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->_conn->unitOfWork->registerIdentity($record);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the mapper to manage the entity if it's not already managed.
|
|
||||||
*
|
|
||||||
* @return boolean TRUE if the entity was previously not managed and is now managed,
|
|
||||||
* FALSE otherwise (the entity is already managed).
|
|
||||||
* @todo Remove.
|
|
||||||
*/
|
|
||||||
public function manage(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
return $this->_conn->unitOfWork->manage($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* removeRecord
|
|
||||||
* removes a record from the identity map, returning true if the record
|
|
||||||
* was found and removed and false if the record wasn't found.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record record to be removed
|
|
||||||
* @return boolean
|
|
||||||
* @todo Move elsewhere to the new location of the identity maps.
|
|
||||||
*/
|
|
||||||
public function removeRecord(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
if ($this->_conn->unitOfWork->contains($record)) {
|
|
||||||
$this->_conn->unitOfWork->unregisterIdentity($record);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getRecord
|
|
||||||
* First checks if record exists in identityMap, if not
|
|
||||||
* returns a new record.
|
|
||||||
*
|
|
||||||
* @return Doctrine_Entity
|
|
||||||
* @todo To EntityManager.
|
|
||||||
*/
|
|
||||||
public function getRecord(array $data)
|
|
||||||
{
|
|
||||||
if ( ! empty($data)) {
|
|
||||||
$identifierFieldNames = $this->_classMetadata->getIdentifier();
|
|
||||||
|
|
||||||
$found = false;
|
|
||||||
foreach ($identifierFieldNames as $fieldName) {
|
|
||||||
if ( ! isset($data[$fieldName])) {
|
|
||||||
// primary key column not found return new record
|
|
||||||
$found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$id[] = $data[$fieldName];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($found) {
|
|
||||||
return new $this->_domainClassName(true, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
$idHash = $this->_conn->unitOfWork->getIdentifierHash($id);
|
|
||||||
|
|
||||||
if ($record = $this->_conn->unitOfWork->tryGetByIdHash($idHash,
|
|
||||||
$this->_classMetadata->getRootClassName())) {
|
|
||||||
$record->hydrate($data);
|
|
||||||
} else {
|
|
||||||
$record = new $this->_domainClassName(false, $data);
|
|
||||||
$this->_conn->unitOfWork->registerIdentity($record);
|
|
||||||
}
|
|
||||||
$data = array();
|
|
||||||
} else {
|
|
||||||
$record = new $this->_domainClassName(true, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $record;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $id database row id
|
|
||||||
* @todo Looks broken. Figure out an implementation and decide whether its needed.
|
|
||||||
*/
|
|
||||||
final public function getProxy($id = null)
|
|
||||||
{
|
|
||||||
if ($id !== null) {
|
|
||||||
$identifierColumnNames = $this->_classMetadata->getIdentifierColumnNames();
|
|
||||||
$query = 'SELECT ' . implode(', ', $identifierColumnNames)
|
|
||||||
. ' FROM ' . $this->_classMetadata->getTableName()
|
|
||||||
. ' WHERE ' . implode(' = ? && ', $identifierColumnNames) . ' = ?';
|
|
||||||
$query = $this->applyInheritance($query);
|
|
||||||
|
|
||||||
$params = array_merge(array($id),array());
|
|
||||||
|
|
||||||
$data = $this->_conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if ($data === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->getRecord($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* applyInheritance
|
|
||||||
* @param $where query where part to be modified
|
|
||||||
* @return string query where part with column aggregation inheritance added
|
|
||||||
* @todo What to do with this? Remove if possible.
|
|
||||||
*/
|
|
||||||
final public function applyInheritance($where)
|
|
||||||
{
|
|
||||||
$discCol = $this->_classMetadata->getInheritanceOption('discriminatorColumn');
|
|
||||||
if ( ! $discCol) {
|
|
||||||
return $where;
|
|
||||||
}
|
|
||||||
|
|
||||||
$discMap = $this->_classMetadata->getInheritanceOption('discriminatorMap');
|
|
||||||
$inheritanceMap = array($discCol => array_search($this->_domainClassName, $discMap));
|
|
||||||
if ( ! empty($inheritanceMap)) {
|
|
||||||
$a = array();
|
|
||||||
foreach ($inheritanceMap as $column => $value) {
|
|
||||||
$a[] = $column . ' = ?';
|
|
||||||
}
|
|
||||||
$i = implode(' AND ', $a);
|
|
||||||
$where .= ' AND ' . $i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $where;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prepareValue
|
|
||||||
* this method performs special data preparation depending on
|
|
||||||
* the type of the given column
|
|
||||||
*
|
|
||||||
* 1. It unserializes array and object typed columns
|
|
||||||
* 2. Uncompresses gzip typed columns
|
|
||||||
* 3. Gets the appropriate enum values for enum typed columns
|
|
||||||
* 4. Initializes special null object pointer for null values (for fast column existence checking purposes)
|
|
||||||
*
|
|
||||||
* example:
|
|
||||||
* <code type='php'>
|
|
||||||
* $field = 'name';
|
|
||||||
* $value = null;
|
|
||||||
* $table->prepareValue($field, $value); // Doctrine_Null
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* @throws Doctrine_Table_Exception if unserialization of array/object typed column fails or
|
|
||||||
* @throws Doctrine_Table_Exception if uncompression of gzip typed column fails *
|
|
||||||
* @param string $field the name of the field
|
|
||||||
* @param string $value field value
|
|
||||||
* @param string $typeHint A hint on the type of the value. If provided, the type lookup
|
|
||||||
* for the field can be skipped. Used i.e. during hydration to
|
|
||||||
* improve performance on large and/or complex results.
|
|
||||||
* @return mixed prepared value
|
|
||||||
* @todo To EntityManager. Make private and use in createEntity().
|
|
||||||
* .. Or, maybe better: Move to hydrator for performance reasons.
|
|
||||||
*/
|
|
||||||
public function prepareValue($fieldName, $value, $typeHint = null)
|
|
||||||
{
|
|
||||||
if ($value === $this->_nullObject) {
|
|
||||||
return $this->_nullObject;
|
|
||||||
} else if ($value === null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
$type = is_null($typeHint) ? $this->_classMetadata->getTypeOf($fieldName) : $typeHint;
|
|
||||||
switch ($type) {
|
|
||||||
case 'integer':
|
|
||||||
case 'string';
|
|
||||||
// don't do any casting here PHP INT_MAX is smaller than what the databases support
|
|
||||||
break;
|
|
||||||
case 'enum':
|
|
||||||
return $this->_classMetadata->enumValue($fieldName, $value);
|
|
||||||
break;
|
|
||||||
case 'boolean':
|
|
||||||
return (boolean) $value;
|
|
||||||
break;
|
|
||||||
case 'array':
|
|
||||||
case 'object':
|
|
||||||
if (is_string($value)) {
|
|
||||||
$value = unserialize($value);
|
|
||||||
if ($value === false) {
|
|
||||||
throw new Doctrine_Mapper_Exception('Unserialization of ' . $fieldName . ' failed.');
|
|
||||||
}
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'gzip':
|
|
||||||
$value = gzuncompress($value);
|
|
||||||
if ($value === false) {
|
|
||||||
throw new Doctrine_Mapper_Exception('Uncompressing of ' . $fieldName . ' failed.');
|
|
||||||
}
|
|
||||||
return $value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getComponentName
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @deprecated Use getMappedClassName()
|
|
||||||
*/
|
|
||||||
public function getComponentName()
|
|
||||||
{
|
|
||||||
return $this->_domainClassName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the class the mapper is used for.
|
|
||||||
*/
|
|
||||||
public function getMappedClassName()
|
|
||||||
{
|
|
||||||
return $this->_domainClassName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves an entity and all it's related entities.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record The entity to save.
|
|
||||||
* @param Doctrine_Connection $conn The connection to use. Will default to the mapper's
|
|
||||||
* connection.
|
|
||||||
* @throws Doctrine_Mapper_Exception If the mapper is unable to save the given entity.
|
|
||||||
*/
|
|
||||||
public function save(Doctrine_Entity $record, Doctrine_Connection $conn = null)
|
|
||||||
{
|
|
||||||
if ( ! ($record instanceof $this->_domainClassName)) {
|
|
||||||
throw new Doctrine_Mapper_Exception("Mapper of type " . $this->_domainClassName . "
|
|
||||||
can't save instances of type" . get_class($record) . ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($conn === null) {
|
|
||||||
$conn = $this->_conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
$state = $record->state();
|
|
||||||
if ($state === Doctrine_Entity::STATE_LOCKED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->state(Doctrine_Entity::STATE_LOCKED);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$conn->beginInternalTransaction();
|
|
||||||
$saveLater = $this->_saveRelated($record);
|
|
||||||
|
|
||||||
$record->state($state);
|
|
||||||
|
|
||||||
if ($record->isValid()) {
|
|
||||||
$this->_insertOrUpdate($record);
|
|
||||||
} else {
|
|
||||||
$conn->transaction->addInvalid($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
$state = $record->state();
|
|
||||||
$record->state(Doctrine_Entity::STATE_LOCKED);
|
|
||||||
|
|
||||||
foreach ($saveLater as $fk) {
|
|
||||||
$alias = $fk->getAlias();
|
|
||||||
if ($record->hasReference($alias)) {
|
|
||||||
$obj = $record->$alias;
|
|
||||||
// check that the related object is not an instance of Doctrine_Null
|
|
||||||
if ( ! ($obj instanceof Doctrine_Null)) {
|
|
||||||
$obj->save($conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the MANY-TO-MANY associations
|
|
||||||
$this->saveAssociations($record);
|
|
||||||
// reset state
|
|
||||||
$record->state($state);
|
|
||||||
$conn->commit();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$conn->rollback();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts or updates an entity, depending on it's state.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record The entity to insert/update.
|
|
||||||
*/
|
|
||||||
protected function _insertOrUpdate(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$record->preSave();
|
|
||||||
$this->notifyEntityListeners($record, 'preSave', Doctrine_Event::RECORD_SAVE);
|
|
||||||
|
|
||||||
switch ($record->state()) {
|
|
||||||
case Doctrine_Entity::STATE_TDIRTY:
|
|
||||||
$this->_insert($record);
|
|
||||||
break;
|
|
||||||
case Doctrine_Entity::STATE_DIRTY:
|
|
||||||
case Doctrine_Entity::STATE_PROXY:
|
|
||||||
$this->_update($record);
|
|
||||||
break;
|
|
||||||
case Doctrine_Entity::STATE_CLEAN:
|
|
||||||
case Doctrine_Entity::STATE_TCLEAN:
|
|
||||||
// do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->postSave();
|
|
||||||
$this->notifyEntityListeners($record, 'postSave', Doctrine_Event::RECORD_SAVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* saves the given record
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function saveSingleRecord(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$this->_insertOrUpdate($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* _saveRelated
|
|
||||||
* saves all related records to $record
|
|
||||||
*
|
|
||||||
* @throws PDOException if something went wrong at database level
|
|
||||||
* @param Doctrine_Entity $record
|
|
||||||
*/
|
|
||||||
protected function _saveRelated(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$saveLater = array();
|
|
||||||
foreach ($record->getReferences() as $k => $v) {
|
|
||||||
$rel = $record->getTable()->getRelation($k);
|
|
||||||
|
|
||||||
$local = $rel->getLocal();
|
|
||||||
$foreign = $rel->getForeign();
|
|
||||||
|
|
||||||
if ($rel instanceof Doctrine_Relation_ForeignKey) {
|
|
||||||
$saveLater[$k] = $rel;
|
|
||||||
} else if ($rel instanceof Doctrine_Relation_LocalKey) {
|
|
||||||
// ONE-TO-ONE relationship
|
|
||||||
$obj = $record->get($rel->getAlias());
|
|
||||||
|
|
||||||
// Protection against infinite function recursion before attempting to save
|
|
||||||
if ($obj instanceof Doctrine_Entity && $obj->isModified()) {
|
|
||||||
$obj->save($this->_conn);
|
|
||||||
|
|
||||||
/** Can this be removed?
|
|
||||||
$id = array_values($obj->identifier());
|
|
||||||
|
|
||||||
foreach ((array) $rel->getLocal() as $k => $field) {
|
|
||||||
$record->set($field, $id[$k]);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $saveLater;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* saveAssociations
|
|
||||||
*
|
|
||||||
* this method takes a diff of one-to-many / many-to-many original and
|
|
||||||
* current collections and applies the changes
|
|
||||||
*
|
|
||||||
* for example if original many-to-many related collection has records with
|
|
||||||
* primary keys 1,2 and 3 and the new collection has records with primary keys
|
|
||||||
* 3, 4 and 5, this method would first destroy the associations to 1 and 2 and then
|
|
||||||
* save new associations to 4 and 5
|
|
||||||
*
|
|
||||||
* @throws Doctrine_Connection_Exception if something went wrong at database level
|
|
||||||
* @param Doctrine_Entity $record
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function saveAssociations(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
foreach ($record->getReferences() as $relationName => $relatedObject) {
|
|
||||||
if ($relatedObject === Doctrine_Null::$INSTANCE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$rel = $record->getTable()->getRelation($relationName);
|
|
||||||
|
|
||||||
if ($rel instanceof Doctrine_Relation_Association) {
|
|
||||||
$relatedObject->save($this->_conn);
|
|
||||||
$assocTable = $rel->getAssociationTable();
|
|
||||||
|
|
||||||
foreach ($relatedObject->getDeleteDiff() as $r) {
|
|
||||||
$query = 'DELETE FROM ' . $assocTable->getTableName()
|
|
||||||
. ' WHERE ' . $rel->getForeign() . ' = ?'
|
|
||||||
. ' AND ' . $rel->getLocal() . ' = ?';
|
|
||||||
// FIXME: composite key support
|
|
||||||
$ids1 = $r->identifier();
|
|
||||||
$id1 = count($ids1) > 0 ? array_pop($ids1) : null;
|
|
||||||
$ids2 = $record->identifier();
|
|
||||||
$id2 = count($ids2) > 0 ? array_pop($ids2) : null;
|
|
||||||
$this->_conn->execute($query, array($id1, $id2));
|
|
||||||
}
|
|
||||||
|
|
||||||
$assocMapper = $this->_conn->getMapper($assocTable->getComponentName());
|
|
||||||
foreach ($relatedObject->getInsertDiff() as $r) {
|
|
||||||
$assocRecord = $assocMapper->create();
|
|
||||||
$assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r);
|
|
||||||
$assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record);
|
|
||||||
$assocMapper->save($assocRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an entity.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record record to be updated
|
|
||||||
* @return boolean whether or not the update was successful
|
|
||||||
* @todo Move to Doctrine_Table (which will become Doctrine_Mapper).
|
|
||||||
*/
|
|
||||||
protected function _update(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$record->preUpdate();
|
|
||||||
$this->notifyEntityListeners($record, 'preUpdate', Doctrine_Event::RECORD_UPDATE);
|
|
||||||
|
|
||||||
$table = $this->_classMetadata;
|
|
||||||
$this->_mappingStrategy->doUpdate($record);
|
|
||||||
|
|
||||||
$record->postUpdate();
|
|
||||||
$this->notifyEntityListeners($record, 'postUpdate', Doctrine_Event::RECORD_UPDATE);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts an entity.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record record to be inserted
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
protected function _insert(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$record->preInsert();
|
|
||||||
$this->notifyEntityListeners($record, 'preInsert', Doctrine_Event::RECORD_INSERT);
|
|
||||||
|
|
||||||
$this->_mappingStrategy->doInsert($record);
|
|
||||||
$this->addRecord($record);
|
|
||||||
|
|
||||||
$record->postInsert();
|
|
||||||
$this->notifyEntityListeners($record, 'postInsert', Doctrine_Event::RECORD_INSERT);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes given entity and all it's related entities.
|
|
||||||
*
|
|
||||||
* Triggered Events: onPreDelete, onDelete.
|
|
||||||
*
|
|
||||||
* @return boolean true on success, false on failure
|
|
||||||
* @throws Doctrine_Mapper_Exception
|
|
||||||
*/
|
|
||||||
public function delete(Doctrine_Entity $record, Doctrine_Connection $conn = null)
|
|
||||||
{
|
|
||||||
if ( ! $record->exists()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! ($record instanceof $this->_domainClassName)) {
|
|
||||||
throw new Doctrine_Mapper_Exception("Mapper of type " . $this->_domainClassName . "
|
|
||||||
can't save instances of type" . get_class($record) . ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($conn == null) {
|
|
||||||
$conn = $this->_conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->preDelete();
|
|
||||||
$this->notifyEntityListeners($record, 'preDelete', Doctrine_Event::RECORD_DELETE);
|
|
||||||
|
|
||||||
$table = $this->_classMetadata;
|
|
||||||
|
|
||||||
$state = $record->state();
|
|
||||||
$record->state(Doctrine_Entity::STATE_LOCKED);
|
|
||||||
|
|
||||||
$this->_mappingStrategy->doDelete($record);
|
|
||||||
|
|
||||||
$record->postDelete();
|
|
||||||
$this->notifyEntityListeners($record, 'postDelete', Doctrine_Event::RECORD_DELETE);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClassMetadata()
|
|
||||||
{
|
|
||||||
return $this->_classMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function free()
|
|
||||||
{
|
|
||||||
$this->_mappingStrategy = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMapping()
|
|
||||||
{
|
|
||||||
return $this->_mappingStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFieldName($columnName)
|
|
||||||
{
|
|
||||||
return $this->_mappingStrategy->getFieldName($columnName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFieldNames()
|
|
||||||
{
|
|
||||||
return $this->_mappingStrategy->getFieldNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOwningClass($fieldName)
|
|
||||||
{
|
|
||||||
return $this->_mappingStrategy->getOwningClass($fieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hooks used during SQL query construction to manipulate the query. */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback that is invoked during the SQL construction process.
|
|
||||||
*/
|
|
||||||
public function getCustomJoins()
|
|
||||||
{
|
|
||||||
return $this->_mappingStrategy->getCustomJoins();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback that is invoked during the SQL construction process.
|
|
||||||
*/
|
|
||||||
public function getCustomFields()
|
|
||||||
{
|
|
||||||
return $this->_mappingStrategy->getCustomFields();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* $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>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default mapping strategy maps a single entity instance to a single database table,
|
|
||||||
* as is the case in Single Table Inheritance & Concrete Table Inheritance.
|
|
||||||
*
|
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
|
||||||
* @package Doctrine
|
|
||||||
* @subpackage DefaultStrategy
|
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
||||||
* @version $Revision$
|
|
||||||
* @link www.phpdoctrine.org
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
class Doctrine_Mapper_DefaultStrategy extends Doctrine_Mapper_Strategy
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Deletes an entity.
|
|
||||||
*/
|
|
||||||
public function doDelete(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
$metadata = $this->_mapper->getClassMetadata();
|
|
||||||
try {
|
|
||||||
$conn->beginInternalTransaction();
|
|
||||||
$this->_deleteComposites($record);
|
|
||||||
|
|
||||||
$record->state(Doctrine_Entity::STATE_TDIRTY);
|
|
||||||
|
|
||||||
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $metadata);
|
|
||||||
$this->_deleteRow($metadata->getTableName(), $identifier);
|
|
||||||
$record->state(Doctrine_Entity::STATE_TCLEAN);
|
|
||||||
|
|
||||||
$this->_mapper->removeRecord($record);
|
|
||||||
$conn->commit();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$conn->rollback();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a single entity into the database, without any related entities.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record The entity to insert.
|
|
||||||
*/
|
|
||||||
public function doInsert(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
|
|
||||||
$fields = $record->getPrepared();
|
|
||||||
if (empty($fields)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//$class = $record->getClassMetadata();
|
|
||||||
$class = $this->_mapper->getClassMetadata();
|
|
||||||
$identifier = (array) $class->getIdentifier();
|
|
||||||
$fields = $this->_convertFieldToColumnNames($fields, $class);
|
|
||||||
|
|
||||||
$seq = $class->getTableOption('sequenceName');
|
|
||||||
if ( ! empty($seq)) {
|
|
||||||
$id = $conn->sequence->nextId($seq);
|
|
||||||
$seqName = $identifier[0];
|
|
||||||
$fields[$seqName] = $id;
|
|
||||||
$record->assignIdentifier($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_insertRow($class->getTableName(), $fields);
|
|
||||||
|
|
||||||
if (empty($seq) && count($identifier) == 1 &&
|
|
||||||
$class->getIdentifierType() != Doctrine::IDENTIFIER_NATURAL) {
|
|
||||||
if (strtolower($conn->getName()) == 'pgsql') {
|
|
||||||
$seq = $class->getTableName() . '_' . $identifier[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $conn->sequence->lastInsertId($seq);
|
|
||||||
|
|
||||||
if ( ! $id) {
|
|
||||||
throw new Doctrine_Mapper_Exception("Couldn't get last insert identifier.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->assignIdentifier($id);
|
|
||||||
} else {
|
|
||||||
$record->assignIdentifier(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an entity.
|
|
||||||
*/
|
|
||||||
public function doUpdate(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $classMetadata);
|
|
||||||
$data = $this->_convertFieldToColumnNames($record->getPrepared(), $classMetadata);
|
|
||||||
$this->_updateRow($classMetadata->getTableName(), $data, $identifier);
|
|
||||||
$record->assignIdentifier(true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class Doctrine_Mapper_Exception extends Doctrine_Exception {}
|
|
@ -1,318 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* $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>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The joined mapping strategy maps a single entity instance to several tables in the
|
|
||||||
* database as it is defined by <tt>Class Table Inheritance</tt>.
|
|
||||||
*
|
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
|
||||||
* @package Doctrine
|
|
||||||
* @subpackage DefaultStrategy
|
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
||||||
* @version $Revision$
|
|
||||||
* @link www.phpdoctrine.org
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
class Doctrine_Mapper_JoinedStrategy extends Doctrine_Mapper_Strategy
|
|
||||||
{
|
|
||||||
protected $_columnNameFieldNameMap = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts an entity that is part of a Class Table Inheritance hierarchy.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record record to be inserted
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function doInsert(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$class = $this->_mapper->getClassMetadata();
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
|
|
||||||
$dataSet = $this->_groupFieldsByDefiningClass($record);
|
|
||||||
$component = $class->getClassName();
|
|
||||||
$classes = $class->getParentClasses();
|
|
||||||
array_unshift($classes, $component);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$conn->beginInternalTransaction();
|
|
||||||
$identifier = null;
|
|
||||||
foreach (array_reverse($classes) as $k => $parent) {
|
|
||||||
$parentClass = $conn->getClassMetadata($parent);
|
|
||||||
if ($k == 0) {
|
|
||||||
$identifierType = $parentClass->getIdentifierType();
|
|
||||||
if ($identifierType == Doctrine::IDENTIFIER_AUTOINC) {
|
|
||||||
$this->_insertRow($parentClass->getTableName(), $dataSet[$parent]);
|
|
||||||
$identifier = $conn->sequence->lastInsertId();
|
|
||||||
} else if ($identifierType == Doctrine::IDENTIFIER_SEQUENCE) {
|
|
||||||
$seq = $record->getClassMetadata()->getTableOption('sequenceName');
|
|
||||||
if ( ! empty($seq)) {
|
|
||||||
$id = $conn->sequence->nextId($seq);
|
|
||||||
$identifierFields = (array)$parentClass->getIdentifier();
|
|
||||||
$dataSet[$parent][$identifierFields[0]] = $id;
|
|
||||||
$this->_insertRow($parentClass->getTableName(), $dataSet[$parent]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Doctrine_Mapper_Exception("Unsupported identifier type '$identifierType'.");
|
|
||||||
}
|
|
||||||
$record->assignIdentifier($identifier);
|
|
||||||
} else {
|
|
||||||
foreach ((array) $record->identifier() as $id => $value) {
|
|
||||||
$dataSet[$parent][$parentClass->getColumnName($id)] = $value;
|
|
||||||
}
|
|
||||||
$this->_insertRow($parentClass->getTableName(), $dataSet[$parent]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$conn->commit();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$conn->rollback();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an entity that is part of a Class Table Inheritance hierarchy.
|
|
||||||
*
|
|
||||||
* @param Doctrine_Entity $record record to be updated
|
|
||||||
* @return boolean whether or not the update was successful
|
|
||||||
* @todo Move to Doctrine_Table (which will become Doctrine_Mapper).
|
|
||||||
*/
|
|
||||||
public function doUpdate(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $classMetadata);
|
|
||||||
$dataSet = $this->_groupFieldsByDefiningClass($record);
|
|
||||||
$component = $classMetadata->getClassName();
|
|
||||||
$classes = $classMetadata->getParentClasses();
|
|
||||||
array_unshift($classes, $component);
|
|
||||||
|
|
||||||
foreach ($record as $field => $value) {
|
|
||||||
if ($value instanceof Doctrine_Entity) {
|
|
||||||
if ( ! $value->exists()) {
|
|
||||||
$value->save();
|
|
||||||
}
|
|
||||||
$idValues = $value->identifier();
|
|
||||||
$record->set($field, $idValues[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (array_reverse($classes) as $class) {
|
|
||||||
$parentTable = $conn->getClassMetadata($class);
|
|
||||||
$this->_updateRow($parentTable->getTableName(), $dataSet[$class], $identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->assignIdentifier(true);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes an entity that is part of a Class Table Inheritance hierarchy.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function doDelete(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
try {
|
|
||||||
$class = $this->_mapper->getClassMetadata();
|
|
||||||
$conn->beginInternalTransaction();
|
|
||||||
$this->_deleteComposites($record);
|
|
||||||
|
|
||||||
$record->state(Doctrine_Entity::STATE_TDIRTY);
|
|
||||||
|
|
||||||
$identifier = $this->_convertFieldToColumnNames($record->identifier(), $class);
|
|
||||||
|
|
||||||
// run deletions, starting from the class, upwards the hierarchy
|
|
||||||
$conn->delete($class->getTableName(), $identifier);
|
|
||||||
foreach ($class->getParentClasses() as $parent) {
|
|
||||||
$parentClass = $conn->getClassMetadata($parent);
|
|
||||||
$this->_deleteRow($parentClass->getTableName(), $identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
$record->state(Doctrine_Entity::STATE_TCLEAN);
|
|
||||||
|
|
||||||
$this->_mapper->removeRecord($record);
|
|
||||||
$conn->commit();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$conn->rollback();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds all parent classes as INNER JOINs and subclasses as OUTER JOINs
|
|
||||||
* to the query.
|
|
||||||
*
|
|
||||||
* Callback that is invoked during the SQL construction process.
|
|
||||||
*
|
|
||||||
* @return array The custom joins in the format <className> => <joinType>
|
|
||||||
*/
|
|
||||||
public function getCustomJoins()
|
|
||||||
{
|
|
||||||
$customJoins = array();
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
foreach ($classMetadata->getParentClasses() as $parentClass) {
|
|
||||||
$customJoins[$parentClass] = 'INNER';
|
|
||||||
}
|
|
||||||
foreach ($classMetadata->getSubclasses() as $subClass) {
|
|
||||||
if ($subClass != $this->_mapper->getComponentName()) {
|
|
||||||
$customJoins[$subClass] = 'LEFT';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $customJoins;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the discriminator column to the selected fields in a query as well as
|
|
||||||
* all fields of subclasses. In Class Table Inheritance the default behavior is that
|
|
||||||
* all subclasses are joined in through OUTER JOINs when querying a base class.
|
|
||||||
*
|
|
||||||
* Callback that is invoked during the SQL construction process.
|
|
||||||
*
|
|
||||||
* @return array An array with the field names that will get added to the query.
|
|
||||||
*/
|
|
||||||
public function getCustomFields()
|
|
||||||
{
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
$fields = array($classMetadata->getInheritanceOption('discriminatorColumn'));
|
|
||||||
if ($classMetadata->getSubclasses()) {
|
|
||||||
foreach ($classMetadata->getSubclasses() as $subClass) {
|
|
||||||
$fields = array_merge($conn->getClassMetadata($subClass)->getFieldNames(), $fields);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_unique($fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getFieldNames()
|
|
||||||
{
|
|
||||||
if ($this->_fieldNames) {
|
|
||||||
return $this->_fieldNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fieldNames = $this->_mapper->getClassMetadata()->getFieldNames();
|
|
||||||
$this->_fieldNames = array_unique($fieldNames);
|
|
||||||
|
|
||||||
return $fieldNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getFieldName($columnName)
|
|
||||||
{
|
|
||||||
if (isset($this->_columnNameFieldNameMap[$columnName])) {
|
|
||||||
return $this->_columnNameFieldNameMap[$columnName];
|
|
||||||
}
|
|
||||||
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
|
|
||||||
if ($classMetadata->hasColumn($columnName)) {
|
|
||||||
$this->_columnNameFieldNameMap[$columnName] = $classMetadata->getFieldName($columnName);
|
|
||||||
return $this->_columnNameFieldNameMap[$columnName];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($classMetadata->getSubclasses() as $subClass) {
|
|
||||||
$subTable = $conn->getClassMetadata($subClass);
|
|
||||||
if ($subTable->hasColumn($columnName)) {
|
|
||||||
$this->_columnNameFieldNameMap[$columnName] = $subTable->getFieldName($columnName);
|
|
||||||
return $this->_columnNameFieldNameMap[$columnName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Doctrine_Mapper_Exception("No field name found for column name '$columnName'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @todo Looks like this better belongs into the ClassMetadata class.
|
|
||||||
*/
|
|
||||||
public function getOwningClass($fieldName)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
if ($classMetadata->hasField($fieldName) && ! $classMetadata->isInheritedField($fieldName)) {
|
|
||||||
return $classMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($classMetadata->getParentClasses() as $parentClass) {
|
|
||||||
$parentTable = $conn->getClassMetadata($parentClass);
|
|
||||||
if ($parentTable->hasField($fieldName) && ! $parentTable->isInheritedField($fieldName)) {
|
|
||||||
return $parentTable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ((array)$classMetadata->getSubclasses() as $subClass) {
|
|
||||||
$subTable = $conn->getClassMetadata($subClass);
|
|
||||||
if ($subTable->hasField($fieldName) && ! $subTable->isInheritedField($fieldName)) {
|
|
||||||
return $subTable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Doctrine_Mapper_Exception("Unable to find defining class of field '$fieldName'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Analyzes the fields of the entity and creates a map in which the field names
|
|
||||||
* are grouped by the class names they belong to.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function _groupFieldsByDefiningClass(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$conn = $this->_mapper->getConnection();
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
$dataSet = array();
|
|
||||||
$component = $classMetadata->getClassName();
|
|
||||||
$array = $record->getPrepared();
|
|
||||||
|
|
||||||
$classes = array_merge(array($component), $classMetadata->getParentClasses());
|
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
|
||||||
$dataSet[$class] = array();
|
|
||||||
$parentClassMetadata = $conn->getClassMetadata($class);
|
|
||||||
foreach ($parentClassMetadata->getColumns() as $columnName => $definition) {
|
|
||||||
if ((isset($definition['primary']) && $definition['primary'] === true) ||
|
|
||||||
(isset($definition['inherited']) && $definition['inherited'] === true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$fieldName = $classMetadata->getFieldName($columnName);
|
|
||||||
if ( ! array_key_exists($fieldName, $array)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$dataSet[$class][$columnName] = $array[$fieldName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dataSet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* $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>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all mapping strategies used by mappers.
|
|
||||||
*
|
|
||||||
* @author Roman Borschel <roman@code-factory.org>
|
|
||||||
* @package Doctrine
|
|
||||||
* @subpackage Strategy
|
|
||||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
||||||
* @version $Revision$
|
|
||||||
* @link www.phpdoctrine.org
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
abstract class Doctrine_Mapper_Strategy
|
|
||||||
{
|
|
||||||
protected $_mapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The names of all the fields that are available on entities created by this mapper.
|
|
||||||
*/
|
|
||||||
protected $_fieldNames = array();
|
|
||||||
|
|
||||||
public function __construct(Doctrine_Mapper $mapper)
|
|
||||||
{
|
|
||||||
$this->_mapper = $mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assumes that the keys of the given field array are field names and converts
|
|
||||||
* them to column names.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function _convertFieldToColumnNames(array $fields, Doctrine_ClassMetadata $class)
|
|
||||||
{
|
|
||||||
$converted = array();
|
|
||||||
foreach ($fields as $fieldName => $value) {
|
|
||||||
$converted[$class->getColumnName($fieldName)] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $converted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* deletes all related composites
|
|
||||||
* this method is always called internally when a record is deleted
|
|
||||||
*
|
|
||||||
* @throws PDOException if something went wrong at database level
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function _deleteComposites(Doctrine_Entity $record)
|
|
||||||
{
|
|
||||||
$classMetadata = $this->_mapper->getClassMetadata();
|
|
||||||
foreach ($classMetadata->getRelations() as $fk) {
|
|
||||||
if ($fk->isComposite()) {
|
|
||||||
$obj = $record->get($fk->getAlias());
|
|
||||||
if ($obj instanceof Doctrine_Entity &&
|
|
||||||
$obj->state() != Doctrine_Entity::STATE_LOCKED) {
|
|
||||||
$obj->delete($this->_mapper->getConnection());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback that is invoked during the SQL construction process.
|
|
||||||
*/
|
|
||||||
public function getCustomJoins()
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback that is invoked during the SQL construction process.
|
|
||||||
*/
|
|
||||||
public function getCustomFields()
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFieldName($columnName)
|
|
||||||
{
|
|
||||||
return $this->_mapper->getClassMetadata()->getFieldName($columnName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFieldNames()
|
|
||||||
{
|
|
||||||
if ($this->_fieldNames) {
|
|
||||||
return $this->_fieldNames;
|
|
||||||
}
|
|
||||||
$this->_fieldNames = $this->_mapper->getClassMetadata()->getFieldNames();
|
|
||||||
return $this->_fieldNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOwningClass($fieldName)
|
|
||||||
{
|
|
||||||
return $this->_mapper->getClassMetadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract public function doDelete(Doctrine_Entity $record);
|
|
||||||
abstract public function doInsert(Doctrine_Entity $record);
|
|
||||||
abstract public function doUpdate(Doctrine_Entity $record);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a row into a table.
|
|
||||||
*
|
|
||||||
* @todo This method could be used to allow mapping to secondary table(s).
|
|
||||||
* @see http://www.oracle.com/technology/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html#SecondaryTable
|
|
||||||
*/
|
|
||||||
protected function _insertRow($tableName, array $data)
|
|
||||||
{
|
|
||||||
$this->_mapper->getConnection()->insert($tableName, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes rows of a table.
|
|
||||||
*
|
|
||||||
* @todo This method could be used to allow mapping to secondary table(s).
|
|
||||||
* @see http://www.oracle.com/technology/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html#SecondaryTable
|
|
||||||
*/
|
|
||||||
protected function _deleteRow($tableName, array $identifierToMatch)
|
|
||||||
{
|
|
||||||
$this->_mapper->getConnection()->delete($tableName, $identifierToMatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes rows of a table.
|
|
||||||
*
|
|
||||||
* @todo This method could be used to allow mapping to secondary table(s).
|
|
||||||
* @see http://www.oracle.com/technology/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html#SecondaryTable
|
|
||||||
*/
|
|
||||||
protected function _updateRow($tableName, array $data, array $identifierToMatch)
|
|
||||||
{
|
|
||||||
$this->_mapper->getConnection()->update($tableName, $data, $identifierToMatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -20,10 +20,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Doctrine_Null
|
* Null class representing a null value that has been fetched from
|
||||||
*
|
* the database or a fetched, empty association. This is for internal use only.
|
||||||
* Simple empty class representing a null value.
|
* User code should never deal with this null object.
|
||||||
* Used for extra fast null value testing with isset() rather than array_key_exists().
|
*
|
||||||
|
* Semantics are as follows:
|
||||||
|
*
|
||||||
|
* Regular PHP null : Value is undefined. When a field with that value is accessed
|
||||||
|
* and lazy loading is used the database is queried.
|
||||||
|
*
|
||||||
|
* Null object: Null valued of a field or empty association that has already been loaded.
|
||||||
|
* On access, the database is not queried.
|
||||||
*
|
*
|
||||||
* @package Doctrine
|
* @package Doctrine
|
||||||
* @subpackage Null
|
* @subpackage Null
|
||||||
|
@ -20,13 +20,13 @@ class ForumUser extends Doctrine_Entity
|
|||||||
$class->mapColumn('id', 'integer', 4, array(
|
$class->mapColumn('id', 'integer', 4, array(
|
||||||
'primary' => true,
|
'primary' => true,
|
||||||
'autoincrement' => true));
|
'autoincrement' => true));
|
||||||
$class->mapColumn('username', 'string', 50);
|
$class->mapColumn('username', 'string', 50, array('accessor' => 'getUsernameCustom'));
|
||||||
|
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public function getUsername()
|
public function getUsernameCustom()
|
||||||
{
|
{
|
||||||
return $this->rawGet('username') . "!";
|
return $this->rawGetField('username') . "!";
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user