diff --git a/composer.json b/composer.json index 5c0bf5958..780105f3b 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "require": { "php": "^7.0", "ext-pdo": "*", - "doctrine/collections": "~1.3", + "doctrine/collections": "^1.4", "doctrine/dbal": ">=2.5-dev,<2.7-dev", "doctrine/instantiator": "~1.0.1", "doctrine/common": "^2.7.1", diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index df7dd5955..e115efbc9 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -716,6 +716,8 @@ methods: * ``in($field, array $values)`` * ``notIn($field, array $values)`` * ``contains($field, $value)`` +* ``startsWith($field, $value)`` +* ``endsWith($field, $value)`` .. note:: diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php index e3d0a165f..5cccadbe2 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -87,16 +87,18 @@ class BasicEntityPersister implements EntityPersister * @var array */ static private $comparisonMap = [ - Comparison::EQ => '= %s', - Comparison::IS => '= %s', - Comparison::NEQ => '!= %s', - Comparison::GT => '> %s', - Comparison::GTE => '>= %s', - Comparison::LT => '< %s', - Comparison::LTE => '<= %s', - Comparison::IN => 'IN (%s)', - Comparison::NIN => 'NOT IN (%s)', - Comparison::CONTAINS => 'LIKE %s', + Comparison::EQ => '= %s', + Comparison::IS => '= %s', + Comparison::NEQ => '!= %s', + Comparison::GT => '> %s', + Comparison::GTE => '>= %s', + Comparison::LT => '< %s', + Comparison::LTE => '<= %s', + Comparison::IN => 'IN (%s)', + Comparison::NIN => 'NOT IN (%s)', + Comparison::CONTAINS => 'LIKE %s', + Comparison::STARTS_WITH => 'LIKE %s', + Comparison::ENDS_WITH => 'LIKE %s', ]; /** diff --git a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php index 2b3bdedc4..c8df1919d 100644 --- a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php +++ b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -111,8 +111,18 @@ class SqlValueVisitor extends ExpressionVisitor { $value = $comparison->getValue()->getValue(); - return $comparison->getOperator() == Comparison::CONTAINS - ? "%{$value}%" - : $value; + switch ($comparison->getOperator()) { + case Comparison::CONTAINS: + return "%{$value}%"; + + case Comparison::STARTS_WITH: + return "{$value}%"; + + case Comparison::ENDS_WITH: + return "%{$value}"; + + default: + return $value; + } } } diff --git a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php index dbdaeb40c..8b00d998e 100644 --- a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php +++ b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -185,6 +185,16 @@ class QueryExpressionVisitor extends ExpressionVisitor $parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType()); $this->parameters[] = $parameter; + return $this->expr->like($field, $placeholder); + case Comparison::STARTS_WITH: + $parameter->setValue($parameter->getValue() . '%', $parameter->getType()); + $this->parameters[] = $parameter; + + return $this->expr->like($field, $placeholder); + case Comparison::ENDS_WITH: + $parameter->setValue('%' . $parameter->getValue(), $parameter->getType()); + $this->parameters[] = $parameter; + return $this->expr->like($field, $placeholder); default: $operator = self::convertComparisonOperator($comparison->getOperator()); diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index d329b8556..ebc7acee9 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -910,6 +910,38 @@ class EntityRepositoryTest extends OrmFunctionalTestCase $this->assertEquals(2, count($users)); } + public function testMatchingCriteriaStartsWithComparison() + { + $this->loadFixture(); + + $repository = $this->_em->getRepository(CmsUser::class); + + $users = $repository->matching(new Criteria(Criteria::expr()->startsWith('name', 'Foo'))); + $this->assertCount(0, $users); + + $users = $repository->matching(new Criteria(Criteria::expr()->startsWith('name', 'R'))); + $this->assertCount(1, $users); + + $users = $repository->matching(new Criteria(Criteria::expr()->startsWith('status', 'de'))); + $this->assertCount(2, $users); + } + + public function testMatchingCriteriaEndsWithComparison() + { + $this->loadFixture(); + + $repository = $this->_em->getRepository(CmsUser::class); + + $users = $repository->matching(new Criteria(Criteria::expr()->endsWith('name', 'foo'))); + $this->assertCount(0, $users); + + $users = $repository->matching(new Criteria(Criteria::expr()->endsWith('name', 'oman'))); + $this->assertCount(1, $users); + + $users = $repository->matching(new Criteria(Criteria::expr()->endsWith('status', 'ev'))); + $this->assertCount(2, $users); + } + /** * @group DDC-2478 */ diff --git a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php index 4e7434c85..0e306ab06 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php @@ -68,6 +68,9 @@ class QueryExpressionVisitorTest extends TestCase [$cb->contains('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', '%value%')], + [$cb->startsWith('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', 'value%')], + [$cb->endsWith('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', '%value')], + // Test parameter conversion [$cb->eq('object.field', 'value'), $qb->eq('o.object.field', ':object_field'), new Parameter('object_field', 'value')],