Commit 7b711ae7 authored by romanb's avatar romanb

refactorings. merged hydration bugfix from 0.11.

parent 74ce82bd
......@@ -867,7 +867,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab
{
$columnName = $this->getColumnName($fieldName);
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)
{
/*// check for custom accessor, if not done yet.
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();
}*/
$this->_invokeCustomAccessor($fieldName);
// Use built-in accessor functionality
$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()
{
return $this->_entityName;
......
......@@ -59,7 +59,7 @@ class Doctrine_EntityRepository
if ( ! empty($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.
* Use with care.
* Finds all entities in the repository.
*
* @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD
* @return Doctrine_Collection
* @param int $hydrationMode
* @return mixed
*/
public function findAll($hydrationMode = null)
{
......
......@@ -102,8 +102,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$driver = new Doctrine_Hydrator_RecordDriver($this->_em);
}
$event = new Doctrine_Event(null, Doctrine_Event::HYDRATE, null);
$s = microtime(true);
// Used variables during hydration
......@@ -112,8 +110,6 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$rootComponentName = $this->_queryComponents[$rootAlias]['table']->getClassName();
// if only one class is involved we can make our lives easier
$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
// It's the identifier "memory"
$identifierMap = array();
......@@ -142,13 +138,11 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
// disable lazy-loading of related elements during hydration
$component['table']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false);
$componentName = $component['table']->getClassName();
//$listeners[$componentName] = $component['table']->getRecordListener();
$identifierMap[$dqlAlias] = array();
$resultPointers[$dqlAlias] = array();
$idTemplate[$dqlAlias] = '';
}
$cache = array();
// Evaluate 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'];
$componentName = $class->getComponentName();
// just event stuff
//$event->set('data', $rowData[$rootAlias]);
//$listeners[$componentName]->preHydrate($event);
//--
// Check for an existing element
$index = false;
if ($isSimpleQuery || ! isset($identifierMap[$rootAlias][$id[$rootAlias]])) {
$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?
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
// (related) components.
// (related) classes.
foreach ($rowData as $dqlAlias => $data) {
$index = false;
$map = $this->_queryComponents[$dqlAlias];
$componentName = $map['table']->getComponentName();
// just event stuff
//$event->set('data', $data);
//$listeners[$componentName]->preHydrate($event);
//--
$componentName = $map['table']->getClassName();
$parent = $map['parent'];
$relation = $map['relation'];
$relationAlias = $relation->getAlias();
......@@ -263,30 +241,18 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$oneToOne = false;
if (isset($nonemptyComponents[$dqlAlias])) {
$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);
// just event stuff
//$event->set('data', $element);
//$listeners[$componentName]->postHydrate($event);
//--
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);
} else {
$driver->addRelatedElement($baseElement, $relationAlias, $element);
}
$identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey(
$driver->getReferenceValue($baseElement, $relationAlias));
} else {
$index = $identifierMap[$path][$id[$parent]][$id[$dqlAlias]];
}
} else if ( ! isset($baseElement[$relationAlias])) {
$driver->setRelatedElement($baseElement, $relationAlias,
......
This diff is collapsed.
<?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);
}
}
\ No newline at end of file
<?php
class Doctrine_Mapper_Exception extends Doctrine_Exception {}
\ No newline at end of file
This diff is collapsed.
<?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);
}
}
\ No newline at end of file
......@@ -20,10 +20,17 @@
*/
/**
* Doctrine_Null
*
* Simple empty class representing a null value.
* Used for extra fast null value testing with isset() rather than array_key_exists().
* Null class representing a null value that has been fetched from
* the database or a fetched, empty association. This is for internal use only.
* User code should never deal with this null object.
*
* 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
* @subpackage Null
......
......@@ -20,13 +20,13 @@ class ForumUser extends Doctrine_Entity
$class->mapColumn('id', 'integer', 4, array(
'primary' => 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') . "!";
}
*/
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment