From 380058194736e3eb6f219195a06fe60e1480039e Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 16 Aug 2011 16:24:50 +0200 Subject: [PATCH] [DDC-551] Altered persisters to make filters work with EXTRA_LAZY associations --- .../ORM/Persisters/BasicEntityPersister.php | 35 +++++- .../ORM/Persisters/ManyToManyPersister.php | 51 +++++++- .../ORM/Persisters/OneToManyPersister.php | 12 +- .../Tests/ORM/Functional/SQLFilterTest.php | 115 ++++++++++++++++++ 4 files changed, 205 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 19da2e200..9da8e95fc 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -861,9 +861,17 @@ class BasicEntityPersister $lockSql = ' ' . $this->_platform->getWriteLockSql(); } + $alias = $this->_getSQLTableAlias($this->_class->name); + + $filterSql = $this->generateFilterConditionSQL($this->_class, $alias); + if('' !== $filterSql) { + if($conditionSql) $conditionSql .= ' AND '; + $conditionSql .= $filterSql; + } + return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL() . $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' - . $this->_getSQLTableAlias($this->_class->name), $lockMode) + . $alias, $lockMode) . $this->_selectJoinSql . $joinSql . ($conditionSql ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset) @@ -1340,10 +1348,33 @@ class BasicEntityPersister $criteria = array_merge($criteria, $extraConditions); } + $alias = $this->_getSQLTableAlias($this->_class->name); + + $sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform) - . ' ' . $this->_getSQLTableAlias($this->_class->name) + . ' ' . $alias . ' WHERE ' . $this->_getSelectConditionSQL($criteria); + $filterSql = $this->generateFilterConditionSQL($this->_class, $alias); + if('' !== $filterSql) { + $sql .= ' AND ' . $filterSql; + } + return (bool) $this->_conn->fetchColumn($sql, array_values($criteria)); } + + private function generateFilterConditionSQL(ClassMetadata $targetEntity, $targetTableAlias) + { + $filterSql = ''; + + $first = true; + foreach($this->_em->getEnabledFilters() as $filter) { + if("" !== $filterExpr = $filter->addFilterConstraint($targetEntity, $targetTableAlias)) { + if ( ! $first) $sql .= ' AND '; else $first = false; + $filterSql .= '(' . $filterExpr . ')'; + } + } + + return $filterSql; + } } diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 6ca2b15a5..ee0b9e1b0 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -208,7 +208,7 @@ class ManyToManyPersister extends AbstractCollectionPersister if ($whereClause !== '') { $whereClause .= ' AND '; } - $whereClause .= "$joinTableColumn = ?"; + $whereClause .= "t.$joinTableColumn = ?"; if ($class->containsForeignIdentifier) { $params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])]; @@ -217,7 +217,14 @@ class ManyToManyPersister extends AbstractCollectionPersister } } } - $sql = 'SELECT count(*) FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); + + $sql = 'SELECT count(*)' + . ' FROM ' . $joinTable['name'] . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . $whereClause + . $filterSql; return $this->_conn->fetchColumn($sql, $params); } @@ -293,8 +300,44 @@ class ManyToManyPersister extends AbstractCollectionPersister } } } - $sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; + + list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($mapping); + + $sql = 'SELECT 1' + . ' FROM ' . $joinTable['name'] . ' t' + . $joinTargetEntitySQL + . ' WHERE ' . $whereClause + . $filterSql; return (bool)$this->_conn->fetchColumn($sql, $params); } -} \ No newline at end of file + + public function getFilterSql($mapping) + { + $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']); + + // Get the SQL for the filters + $filterSql = ''; + foreach($this->_em->getEnabledFilters() as $filter) { + if("" !== $filterExpr = $filter->addFilterConstraint($targetClass, 'te')) { + $filterSql .= ' AND (' . $filterExpr . ')'; + } + } + + // A join is needed if there is filtering on the target entity + $joinTargetEntitySQL = ''; + if('' !== $filterSql) { + $joinTargetEntitySQL = ' JOIN ' + . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' te' + . ' ON'; + + $first = true; + foreach($mapping['relationToTargetKeyColumns'] as $joinTableColumn => $targetTableColumn) { + if(!$first) $joinTargetEntitySQL .= ' AND '; else $first = false; + $joinTargetEntitySQL .= ' t.' . $joinTableColumn . ' = ' . 'te.' . $targetTableColumn; + } + } + + return array($joinTargetEntitySQL, $filterSql); + } +} diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index 5e889ddb9..2dfd18e79 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -133,7 +133,7 @@ class OneToManyPersister extends AbstractCollectionPersister if ($where != '') { $where .= ' AND '; } - $where .= $joinColumn['name'] . " = ?"; + $where .= 't.' . $joinColumn['name'] . " = ?"; if ($class->containsForeignIdentifier) { $params[] = $id[$class->getFieldForColumn($joinColumn['referencedColumnName'])]; } else { @@ -141,7 +141,15 @@ class OneToManyPersister extends AbstractCollectionPersister } } - $sql = "SELECT count(*) FROM " . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where; + $sql = "SELECT count(*) FROM " . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . " t WHERE " . $where; + + // Apply the filters + foreach($this->_em->getEnabledFilters() as $filter) { + if("" !== $filterExpr = $filter->addFilterConstraint($class, 't')) { + $sql .= ' AND (' . $filterExpr . ')'; + } + } + return $this->_conn->fetchColumn($sql, $params); } diff --git a/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php b/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php index f9afc130d..fc6312d97 100644 --- a/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/SQLFilterTest.php @@ -25,6 +25,7 @@ require_once __DIR__ . '/../../TestInit.php'; class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase { private $userId, $userId2, $articleId, $articleId2; + private $groupId, $groupId2; public function setUp() { @@ -32,6 +33,15 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase parent::setUp(); } + public function tearDown() + { + parent::tearDown(); + + $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_LAZY; + $class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY; + } + public function testConfigureFilter() { $config = new \Doctrine\ORM\Configuration(); @@ -307,6 +317,109 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase } + private function loadLazyFixtureData() + { + $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; + $class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY; + $this->loadFixtureData(); + } + + private function useCMSArticleTopicFilter() + { + $conf = $this->_em->getConfiguration(); + $conf->addFilter("article_topic", "\Doctrine\Tests\ORM\Functional\CMSArticleTopicFilter"); + $this->_em->enableFilter("article_topic")->setParameter("topic", "Test1", \Doctrine\DBAL\Types\Type::getType(\Doctrine\DBAL\Types\Type::STRING)->getBindingType()); + } + + public function testOneToMany_ExtraLazyCountWithFilter() + { + $this->loadLazyFixtureData(); + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + + $this->assertFalse($user->articles->isInitialized()); + $this->assertEquals(2, count($user->articles)); + + $this->useCMSArticleTopicFilter(); + + $this->assertEquals(1, count($user->articles)); + } + + public function testOneToMany_ExtraLazyContainsWithFilter() + { + $this->loadLazyFixtureData(); + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + $filteredArticle = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId2); + + $this->assertFalse($user->articles->isInitialized()); + $this->assertTrue($user->articles->contains($filteredArticle)); + + $this->useCMSArticleTopicFilter(); + + $this->assertFalse($user->articles->contains($filteredArticle)); + } + + public function testOneToMany_ExtraLazySliceWithFilter() + { + $this->loadLazyFixtureData(); + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + + $this->assertFalse($user->articles->isInitialized()); + $this->assertEquals(2, count($user->articles->slice(0,10))); + + $this->useCMSArticleTopicFilter(); + + $this->assertEquals(1, count($user->articles->slice(0,10))); + } + + private function useCMSGroupPrefixFilter() + { + $conf = $this->_em->getConfiguration(); + $conf->addFilter("group_prefix", "\Doctrine\Tests\ORM\Functional\CMSGroupPrefixFilter"); + $this->_em->enableFilter("group_prefix")->setParameter("prefix", "foo%", \Doctrine\DBAL\Types\Type::getType(\Doctrine\DBAL\Types\Type::STRING)->getBindingType()); + } + + public function testManyToMany_ExtraLazyCountWithFilter() + { + $this->loadLazyFixtureData(); + + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId2); + + $this->assertFalse($user->groups->isInitialized()); + $this->assertEquals(2, count($user->groups)); + + $this->useCMSGroupPrefixFilter(); + + $this->assertEquals(1, count($user->groups)); + } + + public function testManyToMany_ExtraLazyContainsWithFilter() + { + $this->loadLazyFixtureData(); + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId2); + $filteredArticle = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId2); + + $this->assertFalse($user->groups->isInitialized()); + $this->assertTrue($user->groups->contains($filteredArticle)); + + $this->useCMSGroupPrefixFilter(); + + $this->assertFalse($user->groups->contains($filteredArticle)); + } + + public function testManyToMany_ExtraLazySliceWithFilter() + { + $this->loadLazyFixtureData(); + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId2); + + $this->assertFalse($user->groups->isInitialized()); + $this->assertEquals(2, count($user->groups->slice(0,10))); + + $this->useCMSGroupPrefixFilter(); + + $this->assertEquals(1, count($user->groups->slice(0,10))); + } + private function loadFixtureData() { $user = new CmsUser; @@ -367,6 +480,8 @@ class SQLFilterTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->userId2 = $user2->getId(); $this->articleId = $article1->id; $this->articleId2 = $article2->id; + $this->groupId = $group->id; + $this->groupId2 = $group2->id; } }