diff --git a/ORM/Repository.php b/ORM/Repository.php index d0f3f827..29608918 100644 --- a/ORM/Repository.php +++ b/ORM/Repository.php @@ -159,6 +159,48 @@ public function findBy( return $this->execute($search, $resultType); } + /** + * Finds only one entity by a set of criteria. + * + * @param array $criteria Example: ['group' => ['best', 'worst'], 'job' => 'medic']. + * @param array|null $orderBy Example: ['name' => 'ASC', 'surname' => 'DESC']. + * @param string $resultType Result type returned. + * + * @return DocumentInterface|null The object. + */ + public function findOneBy(array $criteria, array $orderBy = [], $resultType = self::RESULTS_OBJECT) + { + $search = $this->createSearch(); + $search->setSize(1); + + foreach ($criteria as $field => $value) { + $search->addQuery(new TermsQuery($field, is_array($value) ? $value : [$value]), 'must'); + } + + foreach ($orderBy as $field => $direction) { + $search->addSort(new Sort($field, strcasecmp($direction, 'asc') == 0 ? Sort::ORDER_ASC : Sort::ORDER_DESC)); + } + + $result = $this + ->getManager() + ->getConnection() + ->search($this->types, $this->checkFields($search->toArray()), $search->getQueryParams()); + + if ($resultType === self::RESULTS_OBJECT) { + $rawData = $result['hits']['hits']; + if (!count($rawData)) { + return null; + } + + return (new Converter( + $this->getManager()->getTypesMapping(), + $this->getManager()->getBundlesMapping() + ))->convertToDocument($rawData[0]); + } + + return $this->parseResult($result, $resultType, ''); + } + /** * Returns search instance. * diff --git a/Tests/Functional/ORM/RepositoryTest.php b/Tests/Functional/ORM/RepositoryTest.php index 80d4c77b..596221a8 100644 --- a/Tests/Functional/ORM/RepositoryTest.php +++ b/Tests/Functional/ORM/RepositoryTest.php @@ -230,6 +230,86 @@ public function testFindBy($expectedResults, $criteria, $orderBy = [], $limit = $this->assertEquals($expectedResults, $results); } + /** + * Data provider for test find one by. + * + * @return array + */ + public function getFindOneByData() + { + $out = []; + + // Case #0 find one by title for not existed. + $out[] = [ + null, + ['title' => 'baz'], + ]; + + // Case #1 simple find one by title. + $out[] = [ + 1, + ['title' => 'foo'], + ]; + + // Case #2 find one by multiple titles and simple sort. + $out[] = [ + 2, + [ + 'title' => [ + 'foo', + 'bar', + ], + ], + ['title' => 'asc'], + ]; + + // Case #3 find one by multiple titles and multiple sorts. + $criteria = [ + 'description' => [ + 'foo', + 'goo', + ], + 'title' => [ + 'foo', + 'bar', + 'gar', + ], + ]; + $out[] = [ + 2, + $criteria, + [ + 'description' => 'ASC', + 'price' => 'DESC', + ], + ]; + + return $out; + } + + /** + * Check if find one by works as expected. + * + * @param int|null $expectedResult + * @param array $criteria + * @param array $orderBy + * + * @dataProvider getFindOneByData() + */ + public function testFindOneBy($expectedResult, $criteria, $orderBy = []) + { + $repo = $this->getManager()->getRepository('AcmeTestBundle:Product'); + + $result = $repo->findOneBy($criteria, $orderBy); + + if ($expectedResult === null) { + $this->assertNull($result); + } else { + $this->assertNotNull($result); + $this->assertEquals($expectedResult, $result->getId()); + } + } + /** * Test repository find method with array result type. */