diff --git a/src/Service/SearchService.php b/src/Service/SearchService.php index 8d6ddf5..57f7e97 100644 --- a/src/Service/SearchService.php +++ b/src/Service/SearchService.php @@ -5,107 +5,95 @@ declare(strict_types=1); namespace Pcm\SearchBundle\Service; use Pcm\SearchBundle\Entity\SearchIndex; -use Pcm\SearchBundle\Entity\Interface\SearchableInterface; +use Pcm\SearchBundle\Interface\SearchableInterface; use Doctrine\ORM\EntityManagerInterface; -class SearchService +final class SearchService { - public function __construct(private EntityManagerInterface $em) {} - - /** - * Given an $entity that implements SearchableInterface, this method - * creates or updates a SearchIndex $entity - * - * @param SearchableInterface $entity - * @return void - */ - public function index(SearchableInterface $entity) + public function __construct(private EntityManagerInterface $em) { - $searchIndex = $this->createSearchResult($entity); + } - $this->em->persist($searchIndex); + public function index(SearchableInterface $entity): void + { + $index = $this->createSearchResult($entity); + + $this->em->persist($index); + $this->em->flush(); + } + + public function unIndex(SearchableInterface $entity): void + { + $entityId = $this->requireEntityId($entity); + $index = $this->findIndex($this->resolveClass($entity), $entityId); + + if ($index === null) { + return; + } + + $this->em->remove($index); $this->em->flush(); } /** - * Given an $entity that implements SearchableInterface, this method removes - * the item from the search index - * - * @param SearchableInterface $entity - * @return void - */ - public function unIndex(SearchableInterface $entity) - { - $class = $this->em->getClassMetadata($entity::class)->getName(); - - $search_result = $this - ->em - ->getRepository(SearchIndex::class) - ->findOneBy(['entityClass' => $class, 'entityId' => $entity->getId()]); - - if ($search_result) { - $this->em->remove($search_result); - $this->em->flush(); - } - } - - /** - * Given an entity that implements SearchableInterface, this method first checks - * if the relevant SearchIndex entity exists. If it doesn't, it's created. The - * title and index data are set based on the methods in the $entity - * - * @param SearchableInterface $entity - * @return SearchIndex + * Finds or creates the SearchIndex row for the given entity and populates + * its title and search text from the entity. The returned row is unflushed. */ public function createSearchResult(SearchableInterface $entity): SearchIndex { - $values = []; + $entityId = $this->requireEntityId($entity); + $class = $this->resolveClass($entity); + $index = $this->findIndex($class, $entityId) ?? new SearchIndex($class, $entityId); - foreach ($entity->getSearchValues() as $value) { - $values[] = $value; - } + $index->setTitle($entity->getSearchTitle()); + $index->setSearchText(implode(' ', $entity->getSearchValues())); - $data = implode(' ', $values); - - $class = $this->em->getClassMetadata($entity::class)->getName(); - - $searchResult = $this - ->em - ->getRepository(SearchIndex::class) - ->findOneBy( - [ - 'entityClass' => $class, - 'entityId' => $entity->getId() - ] - ); - - if (!$searchResult) { - $searchResult = new SearchIndex(); - $searchResult->setEntityClass($class); - $searchResult->setEntityId($entity->getId()); - } - - $searchResult->setTitle($entity->getSearchTitle()); - $searchResult->setData($data); - - return $searchResult; + return $index; } /** - * Finds all searchable Doctrine entities the implement SearchableInterface - * @return array + * @return class-string[] */ public function getSearchableClasses(): array { - $metadata = $this->em->getMetadataFactory()->getAllMetadata(); - $searchables = []; + $classes = []; - foreach ($metadata as $meta) { + foreach ($this->em->getMetadataFactory()->getAllMetadata() as $meta) { if ($meta->reflClass->implementsInterface(SearchableInterface::class)) { - $searchables[] = $meta->name; + $classes[] = $meta->name; } } - return $searchables; + return $classes; + } + + private function findIndex(string $class, int $entityId): ?SearchIndex + { + return $this->em + ->getRepository(SearchIndex::class) + ->findOneBy(['entityClass' => $class, 'entityId' => $entityId]); + } + + private function requireEntityId(SearchableInterface $entity): int + { + $entityId = $entity->getId(); + + if ($entityId === null) { + throw new \LogicException('Entity must be persisted before indexing.'); + } + + return $entityId; + } + + /** + * @template T of object + * + * @param T $entity + * + * @return class-string + */ + private function resolveClass(object $entity): string + { + return $this->em->getClassMetadata($entity::class)->getName(); } }