1
0
mirror of synced 2024-12-13 14:56:01 +03:00
doctrine2/lib/Doctrine/Mapper.php

1014 lines
33 KiB
PHP

<?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 Move all finder stuff to EntityRepository.
* @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;
/**
* @var array $identityMap first level cache
* @todo Move to UnitOfWork.
*/
protected $_identityMap = array();
/**
* Null object.
*/
private $_nullObject;
/**
* A list of registered entity listeners.
*/
private $_entityListeners = 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);
}
}
/**
* createQuery
* creates a new Doctrine_Query object and adds the component name
* of this table as the query 'from' part
*
* @param string Optional alias name for component aliasing.
*
* @return Doctrine_Query
*/
public function createQuery($alias = '')
{
if ( ! empty($alias)) {
$alias = ' ' . trim($alias);
}
return Doctrine_Query::create($this->_conn)->from($this->getComponentName() . $alias);
}
/**
* 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_Record the created record object
*/
public function create(array $array = array())
{
$record = new $this->_domainClassName($this, true);
$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_Record $entity, $callback, $eventType)
{
if ($this->_entityListeners) {
$event = new Doctrine_Event($entity, $eventType);
foreach ($this->_entityListeners as $listener) {
$listener->$callback($event);
}
}
}
public function detach(Doctrine_Record $entity)
{
return $this->_conn->unitOfWork->detach($entity);
}
/**
* Finds an entity by its primary key.
*
* @param $id database row id
* @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
* @return mixed Array or Doctrine_Record or false if no result
* @todo Remove. Move to EntityRepository.
*/
public function find($id, $hydrationMode = null)
{
if (is_null($id)) {
return false;
}
$id = is_array($id) ? array_values($id) : array($id);
return $this->createQuery()
->where(implode(' = ? AND ', (array) $this->_classMetadata->getIdentifier()) . ' = ?')
->fetchOne($id, $hydrationMode);
}
/**
* Finds all entities of the mapper's class.
* Use with care.
*
* @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
* @return Doctrine_Collection
* @todo Remove. Move to EntityRepository.
*/
public function findAll($hydrationMode = null)
{
return $this->createQuery()->execute(array(), $hydrationMode);
}
/**
* findBySql
* finds records with given SQL where clause
* returns a collection of records
*
* @param string $dql DQL after WHERE clause
* @param array $params query parameters
* @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD
* @return Doctrine_Collection
*
* @todo This actually takes DQL, not SQL, but it requires column names
* instead of field names. This should be fixed to use raw SQL instead.
* @todo Remove. Move to EntityRepository.
*/
public function findBySql($dql, array $params = array(), $hydrationMode = null)
{
return $this->createQuery()->where($dql)->execute($params, $hydrationMode);
}
/**
* findByDql
* finds records with given DQL where clause
* returns a collection of records
*
* @param string $dql DQL after WHERE clause
* @param array $params query parameters
* @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD
* @return Doctrine_Collection
* @todo Remove. Move to EntityRepository.
*/
public function findByDql($dql, array $params = array(), $hydrationMode = null)
{
$query = new Doctrine_Query($this->_conn);
$component = $this->getComponentName();
$dql = 'FROM ' . $component . ' WHERE ' . $dql;
return $query->query($dql, $params, $hydrationMode);
}
/**
* Executes a named query.
*
* @param string $queryName The name that was used when storing the query.
* @param array $params The query parameters.
* @return mixed The result.
* @deprecated
*/
public function executeNamedQuery($queryName, $params = array(), $hydrationMode = Doctrine::HYDRATE_RECORD)
{
return Doctrine_Manager::getInstance()
->createNamedQuery($queryName)
->execute($params, $hydrationMode);
}
/**
* clear
* clears the first level cache (identityMap)
*
* @return void
* @todo what about a more descriptive name? clearIdentityMap?
*/
public function clear()
{
$this->_identityMap = array();
//$this->_conn->unitOfWork->clearIdentitiesForEntity($this->_classMetadata->getRootClassName());
}
/**
* addRecord
* adds a record to identity map
*
* @param Doctrine_Record $record record to be added
* @return boolean
* @todo Better name? registerRecord? Move elsewhere to the new location of the identity maps.
*/
public function addRecord(Doctrine_Record $record)
{
$id = implode(' ', $record->identifier());
if (isset($this->_identityMap[$id])) {
return false;
}
/*if ($this->_conn->unitOfWork->containsIdentity($id, $record->getClassMetadata()->getRootClassname())) {
return false;
}*/
//$this->_conn->unitOfWork->registerIdentity($record);
$this->_identityMap[$id] = $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).
*/
public function manage(Doctrine_Record $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_Record $record record to be removed
* @return boolean
* @todo Move elsewhere to the new location of the identity maps.
*/
public function removeRecord(Doctrine_Record $record)
{
$id = implode(' ', $record->identifier());
if (isset($this->_identityMap[$id])) {
unset($this->_identityMap[$id]);
return true;
}
/*if ($this->_conn->unitOfWork->containsIdentity($id, $record->getClassMetadata()->getRootClassName())) {
$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_Record
*/
public function getRecord(array $data)
{
if ( ! empty($data)) {
$identifierFieldNames = (array)$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) {
$record = new $this->_domainClassName($this, true, $data);
$data = array();
return $record;
}
$id = implode(' ', $id);
if (isset($this->_identityMap[$id])) {
//if ($this->_conn->unitOfWork->containsIdentity($id, $this->_classMetadata->getRootClassName())) {
$record = $this->_identityMap[$id];
//$record = $this->_conn->unitOfWork->getByIdentity($id, $this->_classMetadata->getRootClassName());
$record->hydrate($data);
} else {
$record = new $this->_domainClassName($this, false, $data);
//$this->_conn->unitOfWork->registerIdentity($record);
$this->_identityMap[$id] = $record;
}
$data = array();
} else {
$record = new $this->_domainClassName($this, true, $data);
}
return $record;
}
/**
* @param $id database row id
*/
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
*/
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
*/
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;
}
/**
* Hydrates the given data into the entity.
*
*/
public function hydrate(Doctrine_Record $entity, array $data)
{
$this->_values = array_merge($this->_values, $this->cleanData($data));
$this->_data = array_merge($this->_data, $data);
$this->_extractIdentifier(true);
}
/**
* getTree
*
* getter for associated tree
*
* @return mixed if tree return instance of Doctrine_Tree, otherwise returns false
* @todo Part of the NestedSet Behavior plugin. Move outta here some day...
*/
public function getTree()
{
return $this->_classMetadata->getTree();
}
/**
* isTree
*
* determine if table acts as tree
*
* @return mixed if tree return true, otherwise returns false
* @todo Part of the NestedSet Behavior plugin. Move outta here some day...
*/
public function isTree()
{
return $this->_classMetadata->isTree();
}
/**
* 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;
}
/**
* returns a string representation of this object
*
* @return string
*/
public function __toString()
{
return Doctrine_Lib::getTableAsString($this);
}
/**
* findBy
*
* @param string $column
* @param string $value
* @param string $hydrationMode
* @return void
* @todo Remove. Move to EntityRepository.
*/
protected function findBy($fieldName, $value, $hydrationMode = null)
{
return $this->createQuery()->where($fieldName . ' = ?')->execute(array($value), $hydrationMode);
}
/**
* findOneBy
*
* @param string $column
* @param string $value
* @param string $hydrationMode
* @return void
* @todo Remove. Move to EntityRepository.
*/
protected function findOneBy($fieldName, $value, $hydrationMode = null)
{
$results = $this->createQuery()->where($fieldName . ' = ?')->limit(1)->execute(
array($value), $hydrationMode);
return $hydrationMode === Doctrine::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst();
}
/**
* __call
*
* Adds support for magic finders.
* findByColumnName, findByRelationAlias
* findById, findByContactId, etc.
*
* @return void
* @throws Doctrine_Mapper_Exception If the method called is an invalid find* method
* or no find* method at all and therefore an invalid
* method call.
* @todo Remove. Move to EntityRepository.
*/
public function __call($method, $arguments)
{
if (substr($method, 0, 6) == 'findBy') {
$by = substr($method, 6, strlen($method));
$method = 'findBy';
} else if (substr($method, 0, 9) == 'findOneBy') {
$by = substr($method, 9, strlen($method));
$method = 'findOneBy';
} else {
try {
throw new Exception();
} catch (Exception $e) {
echo $e->getTraceAsString() . "<br/><br/>";
}
throw new Doctrine_Mapper_Exception("Undefined method '$method'.");
}
if (isset($by)) {
if ( ! isset($arguments[0])) {
throw new Doctrine_Mapper_Exception('You must specify the value to findBy.');
}
$fieldName = Doctrine::tableize($by);
$hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
if ($this->_classMetadata->hasField($fieldName)) {
return $this->$method($fieldName, $arguments[0], $hydrationMode);
} else if ($this->_classMetadata->hasRelation($by)) {
$relation = $this->_classMetadata->getRelation($by);
if ($relation['type'] === Doctrine_Relation::MANY) {
throw new Doctrine_Mapper_Exception('Cannot findBy many relationship.');
}
return $this->$method($relation['local'], $arguments[0], $hydrationMode);
} else {
throw new Doctrine_Mapper_Exception('Cannot find by: ' . $by . '. Invalid field or relationship alias.');
}
}
}
/**
* Saves an entity and all it's related entities.
*
* @param Doctrine_Record $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_Record $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_Record::STATE_LOCKED) {
return false;
}
$record->state(Doctrine_Record::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_Record::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_Record $record The entity to insert/update.
*/
protected function _insertOrUpdate(Doctrine_Record $record)
{
$record->preSave();
$this->notifyEntityListeners($record, 'preSave', Doctrine_Event::RECORD_SAVE);
switch ($record->state()) {
case Doctrine_Record::STATE_TDIRTY:
$this->_insert($record);
break;
case Doctrine_Record::STATE_DIRTY:
case Doctrine_Record::STATE_PROXY:
$this->_update($record);
break;
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_TCLEAN:
// do nothing
break;
}
$record->postSave();
$this->notifyEntityListeners($record, 'postSave', Doctrine_Event::RECORD_SAVE);
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public function saveSingleRecord(Doctrine_Record $record)
{
$this->_insertOrUpdate($record);
}
/**
* _saveRelated
* saves all related records to $record
*
* @throws PDOException if something went wrong at database level
* @param Doctrine_Record $record
*/
protected function _saveRelated(Doctrine_Record $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_Record && $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_Record $record
* @return void
*/
public function saveAssociations(Doctrine_Record $record)
{
foreach ($record->getReferences() as $relationName => $relatedObject) {
$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() . ' = ?';
$this->_conn->execute($query, array($r->getIncremented(), $record->getIncremented()));
}
$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);
//$this->saveSingleRecord($assocRecord);
}
}
}
}
/**
* Updates an entity.
*
* @param Doctrine_Record $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_Record $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_Record $record record to be inserted
* @return boolean
*/
protected function _insert(Doctrine_Record $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_Record $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_Record::STATE_LOCKED);
$this->_mappingStrategy->doDelete($record);
$record->postDelete();
$this->notifyEntityListeners($record, 'postDelete', Doctrine_Event::RECORD_DELETE);
return true;
}
public function hasAttribute($key)
{
switch ($key) {
case Doctrine::ATTR_LOAD_REFERENCES:
case Doctrine::ATTR_QUERY_LIMIT:
case Doctrine::ATTR_COLL_KEY:
case Doctrine::ATTR_VALIDATE:
return true;
default:
return false;
}
}
public function executeQuery(Doctrine_Query $query)
{
}
public function getTable()
{
return $this->_classMetadata;
}
public function getClassMetadata()
{
return $this->_classMetadata;
}
public function getIdentityMap()
{
return $this->_identityMap;
}
public function dump()
{
var_dump($this->_invokedMethods);
}
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();
}
}