1
0
mirror of synced 2024-12-05 03:06:05 +03:00

Merge branch 'hotfix/#1336-handle-custom-object-type-identifiers-in-uow'

Close #1336
This commit is contained in:
Marco Pivetta 2015-03-17 21:31:03 +00:00
commit da72f3e62c
8 changed files with 355 additions and 15 deletions

View File

@ -13,44 +13,50 @@ you wish. Here is an example skeleton of such a custom type class:
<?php
namespace My\Project\Types;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
/**
* My custom datatype.
*/
class MyType extends Type
{
const MYTYPE = 'mytype'; // modify to match your type name
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
// return the SQL used to create your column type. To create a portable column type, use the $platform.
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
// This is executed when the value is read from the database. Make your conversions here, optionally using the $platform.
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
// This is executed when the value is written to the database. Make your conversions here, optionally using the $platform.
}
public function getName()
{
return self::MYTYPE; // modify to match your constant name
}
}
The following assumptions are applied to mapping types by the ORM:
.. note::
- If the value of the field is *NULL* the method
``convertToDatabaseValue()`` is not called.
- The ``UnitOfWork`` never passes values to the database convert
method that did not change in the request.
The following assumptions are applied to mapping types by the ORM:
- If the value of the field is *NULL* the method
``convertToDatabaseValue()`` is not called.
- The ``UnitOfWork`` never passes values to the database convert
method that did not change in the request.
- The ``UnitOfWork`` internally assumes that entity identifiers are
castable to string. Hence, when using custom types that map to PHP
objects as IDs, such objects must implement the ``__toString()`` magic
method.
When you have implemented the type you still need to let Doctrine
know about it. This can be achieved through the

View File

@ -1566,15 +1566,17 @@ class UnitOfWork implements PropertyChangedListener
*
* @ignore
*
* @param string $idHash
* @param mixed $idHash (must be possible to cast it to string)
* @param string $rootClassName
*
* @return object|bool The found entity or FALSE.
*/
public function tryGetByIdHash($idHash, $rootClassName)
{
if (isset($this->identityMap[$rootClassName][$idHash])) {
return $this->identityMap[$rootClassName][$idHash];
$stringIdHash = (string) $idHash;
if (isset($this->identityMap[$rootClassName][$stringIdHash])) {
return $this->identityMap[$rootClassName][$stringIdHash];
}
return false;

View File

@ -0,0 +1,44 @@
<?php
/*
* 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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\DbalTypes;
class CustomIdObject
{
/**
* @var string
*/
public $id;
/**
* @param string $id
*/
public function __construct($id)
{
$this->id = (string) $id;
}
/**
* @return string
*/
public function __toString()
{
return $this->id;
}
}

View File

@ -0,0 +1,63 @@
<?php
/*
* 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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\DbalTypes;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
class CustomIdObjectType extends Type
{
const NAME = 'CustomIdObject';
const CLASSNAME = __CLASS__;
/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $value->id;
}
/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
$idObject = new CustomIdObject($value);
return $idObject;
}
/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return self::NAME;
}
}

View File

@ -0,0 +1,53 @@
<?php
/*
* 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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\Models\CustomType;
use Doctrine\Tests\DbalTypes\CustomIdObject;
/**
* @Entity
* @Table(name="custom_id_type_child")
*/
class CustomIdObjectTypeChild
{
const CLASSNAME = __CLASS__;
/**
* @Id @Column(type="CustomIdObject")
*
* @var CustomIdObject
*/
public $id;
/**
* @ManyToOne(targetEntity="Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent", inversedBy="children")
*/
public $parent;
/**
* @param CustomIdObject $id
* @param CustomIdObjectTypeParent $parent
*/
public function __construct(CustomIdObject $id, CustomIdObjectTypeParent $parent)
{
$this->id = $id;
$this->parent = $parent;
}
}

View File

@ -0,0 +1,53 @@
<?php
/*
* 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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\Models\CustomType;
use Doctrine\Tests\DbalTypes\CustomIdObject;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
* @Table(name="custom_id_type_parent")
*/
class CustomIdObjectTypeParent
{
const CLASSNAME = __CLASS__;
/**
* @Id @Column(type="CustomIdObject")
*
* @var CustomIdObject
*/
public $id;
/**
* @OneToMany(targetEntity="Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild", cascade={"persist", "remove"}, mappedBy="parent")
*/
public $children;
/**
* @param CustomIdObject $id
*/
public function __construct(CustomIdObject $id)
{
$this->id = $id;
$this->children = new ArrayCollection();
}
}

View File

@ -0,0 +1,110 @@
<?php
/*
* 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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\DbalTypes\CustomIdObject;
use Doctrine\Tests\DbalTypes\CustomIdObjectType;
use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild;
use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent;
use Doctrine\Tests\OrmFunctionalTestCase;
use Doctrine\DBAL\Types\Type as DBALType;
class CustomIdObjectTypeTest extends OrmFunctionalTestCase
{
protected function setUp()
{
if (DBALType::hasType(CustomIdObjectType::NAME)) {
DBALType::overrideType(CustomIdObjectType::NAME, CustomIdObjectType::CLASSNAME);
} else {
DBALType::addType(CustomIdObjectType::NAME, CustomIdObjectType::CLASSNAME);
}
$this->useModelSet('custom_id_object_type');
parent::setUp();
}
public function testFindByCustomIdObject()
{
$parent = new CustomIdObjectTypeParent(new CustomIdObject('foo'));
$this->_em->persist($parent);
$this->_em->flush();
$result = $this->_em->find(CustomIdObjectTypeParent::CLASSNAME, $parent->id);
$this->assertSame($parent, $result);
}
/**
* @group DDC-3622
* @group 1336
*/
public function testFetchJoinCustomIdObject()
{
$parent = new CustomIdObjectTypeParent(new CustomIdObject('foo'));
$parent->children->add(new CustomIdObjectTypeChild(new CustomIdObject('bar'), $parent));
$this->_em->persist($parent);
$this->_em->flush();
$result = $this
->_em
->createQuery(
'SELECT parent, children FROM '
. CustomIdObjectTypeParent::CLASSNAME
. ' parent LEFT JOIN parent.children children'
)
->getResult();
$this->assertCount(1, $result);
$this->assertSame($parent, $result[0]);
}
/**
* @group DDC-3622
* @group 1336
*/
public function testFetchJoinWhereCustomIdObject()
{
$parent = new CustomIdObjectTypeParent(new CustomIdObject('foo'));
$parent->children->add(new CustomIdObjectTypeChild(new CustomIdObject('bar'), $parent));
$this->_em->persist($parent);
$this->_em->flush();
// note: hydration is willingly broken in this example:
$result = $this
->_em
->createQuery(
'SELECT parent, children FROM '
. CustomIdObjectTypeParent::CLASSNAME
. ' parent LEFT JOIN parent.children children '
. 'WHERE children.id = ?1'
)
->setParameter(1, $parent->children->first()->id)
->getResult();
$this->assertCount(1, $result);
$this->assertSame($parent, $result[0]);
}
}

View File

@ -257,7 +257,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
'Doctrine\Tests\Models\GeoNames\Admin1',
'Doctrine\Tests\Models\GeoNames\Admin1AlternateName',
'Doctrine\Tests\Models\GeoNames\City'
)
),
'custom_id_object_type' => array(
'Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent',
'Doctrine\Tests\Models\CustomType\CustomIdObjectTypeChild',
),
);
/**
@ -496,6 +500,11 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
$conn->executeUpdate('DELETE FROM geonames_country');
}
if (isset($this->_usedModelSets['custom_id_object_type'])) {
$conn->executeUpdate('DELETE FROM custom_id_type_child');
$conn->executeUpdate('DELETE FROM custom_id_type_parent');
}
$this->_em->clear();
}