diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index a707577d7..77d71dfc0 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -833,15 +833,11 @@ class ClassMetadataInfo implements ClassMetadata // Cascades $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array(); + if (in_array('all', $cascades)) { - $cascades = array( - 'remove', - 'persist', - 'refresh', - 'merge', - 'detach' - ); + $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach'); } + $mapping['cascade'] = $cascades; $mapping['isCascadeRemove'] = in_array('remove', $cascades); $mapping['isCascadePersist'] = in_array('persist', $cascades); diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 19da2e200..328e3f091 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -26,6 +26,7 @@ use PDO, Doctrine\ORM\ORMException, Doctrine\ORM\OptimisticLockException, Doctrine\ORM\EntityManager, + Doctrine\ORM\UnitOfWork, Doctrine\ORM\Query, Doctrine\ORM\PersistentCollection, Doctrine\ORM\Mapping\MappingException, @@ -1213,7 +1214,7 @@ class BasicEntityPersister } else { $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.'; } - + $conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name']; } else if ($assoc !== null && strpos($field, " ") === false && strpos($field, "(") === false) { // very careless developers could potentially open up this normally hidden api for userland attacks, @@ -1224,6 +1225,7 @@ class BasicEntityPersister } else { throw ORMException::unrecognizedField($field); } + $conditionSql .= (is_array($value)) ? ' IN (?)' : (($value === null) ? ' IS NULL' : ' = ?'); } return $conditionSql; @@ -1313,18 +1315,96 @@ class BasicEntityPersister continue; // skip null values. } - $type = null; - if (isset($this->_class->fieldMappings[$field])) { + $types[] = $this->getType($field, $value); + $params[] = $this->getValue($value); + } + + return array($params, $types); + } + + /** + * Infer field type to be used by parameter type casting. + * + * @param string $field + * @param mixed $value + * @return integer + */ + private function getType($field, $value) + { + switch (true) { + case (isset($this->_class->fieldMappings[$field])): $type = Type::getType($this->_class->fieldMappings[$field]['type'])->getBindingType(); - } - if (is_array($value)) { - $type += Connection::ARRAY_PARAM_OFFSET; + break; + + case (isset($this->_class->associationMappings[$field])): + $assoc = $this->_class->associationMappings[$field]; + + if (count($assoc['sourceToTargetKeyColumns']) > 1) { + throw Query\QueryException::associationPathCompositeKeyNotSupported(); + } + + $targetClass = $this->_em->getClassMetadata($assoc['targetEntity']); + $targetColumn = $assoc['joinColumns'][0]['referencedColumnName']; + $type = null; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = Type::getType($targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type'])->getBindingType(); + } + + break; + + default: + $type = null; + } + + if (is_array($value)) { + $type += Connection::ARRAY_PARAM_OFFSET; + } + + return $type; + } + + /** + * Retrieve parameter value + * + * @param mixed $value + * @return mixed + */ + private function getValue($value) + { + if (is_array($value)) { + $newValue = array(); + + foreach ($value as $itemValue) { + $newValue[] = $this->getIndividualValue($itemValue); } - $params[] = $value; - $types[] = $type; + return $newValue; } - return array($params, $types); + + return $this->getIndividualValue($value); + } + + /** + * Retrieve an invidiual parameter value + * + * @param mixed $value + * @return mixed + */ + private function getIndividualValue($value) + { + if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) { + if ($this->_em->getUnitOfWork()->getEntityState($value) === UnitOfWork::STATE_MANAGED) { + $idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value); + } else { + $class = $this->_em->getClassMetadata(get_class($value)); + $idValues = $class->getIdentifierValues($value); + } + + $value = $idValues[key($idValues)]; + } + + return $value; } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 029d55252..16d3b7bb4 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -3,8 +3,8 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\Tests\Models\CMS\CmsUser; -use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\CMS\CmsPhonenumber; require_once __DIR__ . '/../../TestInit.php'; @@ -18,6 +18,12 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase parent::setUp(); } + public function tearDown() + { + $this->_em->getConfiguration()->setEntityNamespaces(array()); + parent::tearDown(); + } + public function loadFixture() { $user = new CmsUser; @@ -33,13 +39,66 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->persist($user2); $this->_em->flush(); + $user1Id = $user->getId(); + unset($user); unset($user2); + $this->_em->clear(); return $user1Id; } + + public function loadAssociatedFixture() + { + $address = new CmsAddress(); + $address->city = "Berlin"; + $address->country = "Germany"; + $address->street = "Foostreet"; + $address->zip = "12345"; + + $user = new CmsUser(); + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'freak'; + $user->setAddress($address); + + $this->_em->persist($user); + $this->_em->persist($address); + $this->_em->flush(); + $this->_em->clear(); + + return array($user->id, $address->id); + } + + public function buildUser($name, $username, $status, $address) + { + $user = new CmsUser(); + $user->name = $name; + $user->username = $username; + $user->status = $status; + $user->setAddress($address); + + $this->_em->persist($user); + $this->_em->flush(); + + return $user; + } + + public function buildAddress($country, $city, $street, $zip) + { + $address = new CmsAddress(); + $address->country = $country; + $address->city = $city; + $address->street = $street; + $address->zip = $zip; + + $this->_em->persist($address); + $this->_em->flush(); + + return $address; + } public function testBasicFind() { @@ -64,6 +123,53 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals('dev', $users[0]->status); } + public function testFindByAssociationWithIntegerAsParameter() + { + $address1 = $this->buildAddress('Germany', 'Berlim', 'Foo st.', '123456'); + $user1 = $this->buildUser('Benjamin', 'beberlei', 'dev', $address1); + + $address2 = $this->buildAddress('Brazil', 'São Paulo', 'Bar st.', '654321'); + $user2 = $this->buildUser('Guilherme', 'guilhermeblanco', 'freak', $address2); + + $address3 = $this->buildAddress('USA', 'Nashville', 'Woo st.', '321654'); + $user3 = $this->buildUser('Jonathan', 'jwage', 'dev', $address3); + + unset($address1); + unset($address2); + unset($address3); + + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $addresses = $repository->findBy(array('user' => array($user1->getId(), $user2->getId()))); + + $this->assertEquals(2, count($addresses)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress',$addresses[0]); + } + + public function testFindByAssociationWithObjectAsParameter() + { + $address1 = $this->buildAddress('Germany', 'Berlim', 'Foo st.', '123456'); + $user1 = $this->buildUser('Benjamin', 'beberlei', 'dev', $address1); + + $address2 = $this->buildAddress('Brazil', 'São Paulo', 'Bar st.', '654321'); + $user2 = $this->buildUser('Guilherme', 'guilhermeblanco', 'freak', $address2); + + $address3 = $this->buildAddress('USA', 'Nashville', 'Woo st.', '321654'); + $user3 = $this->buildUser('Jonathan', 'jwage', 'dev', $address3); + + unset($address1); + unset($address2); + unset($address3); + + $this->_em->clear(); + + $repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress'); + $addresses = $repository->findBy(array('user' => array($user1, $user2))); + + $this->assertEquals(2, count($addresses)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress',$addresses[0]); + } public function testFindFieldByMagicCall() { @@ -99,12 +205,6 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(2, count($users)); } - public function tearDown() - { - $this->_em->getConfiguration()->setEntityNamespaces(array()); - parent::tearDown(); - } - /** * @expectedException \Doctrine\ORM\ORMException */ @@ -201,28 +301,6 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $repos->foo(); } - public function loadAssociatedFixture() - { - $address = new CmsAddress(); - $address->city = "Berlin"; - $address->country = "Germany"; - $address->street = "Foostreet"; - $address->zip = "12345"; - - $user = new CmsUser(); - $user->name = 'Roman'; - $user->username = 'romanb'; - $user->status = 'freak'; - $user->setAddress($address); - - $this->_em->persist($user); - $this->_em->persist($address); - $this->_em->flush(); - $this->_em->clear(); - - return array($user->id, $address->id); - } - /** * @group DDC-817 */