PHP code example of nramos / search-indexer

1. Go to this page and download the library: Download nramos/search-indexer library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

nramos / search-indexer example snippets


#[SearchIndex(indexName: 'biens', autoIndex: true)]

#[SearchProperty(propertyName: 'typeBien', relationProperties: [], filterable: true, sortable: true, searchable: true)]

#[SearchProperty(propertyName: 'id', isPk:true, relationProperties: [], filterable: true, sortable: true, searchable: true)]

#[ORM\ManyToMany(targetEntity: Heating::class, mappedBy: 'houses')]
#[SearchProperty(propertyName: 'heatings', relationProperties: ['name'], filterable: true)]
private Collection $heatings;

$filter = (new MeiliSearchFilter())
    ->addFilter('status', '=', 'active')
    ->addFilter('rating.users', '>', 85)
    ->openParenthesis()
    ->addFilter('genres', '=', 'horror', 'OR')
    ->addFilter('genres', '=', 'comedy')
    ->closeParenthesis()
    ->openParenthesis()
    ->addFilter('genres', '=', 'horror')
    ->addFilter('genres', '=', 'comedy')
    ->closeParenthesis()
    ->addInFilter('role', ['admin', 'user'])
    ->addLocationFilter('radius', 48.8566, 2.3522, 5, 'km')
    ->addLocationBounding('bounding', [48.8566, 2.3522, 49.8566, 2.4522], 'km')
    ->addExistenceFilter('release_date')
    ->addExistenceFilter('overview', false);


$results = $client->search(
    'houses', // Nom de l'index
    'search query', // Requête de recherche
    $filter, // Filtre de recherche
    10, // Limite
    1, // Page
    ['status', 'genres'] // Facettes
);


namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Nramos\SearchIndexer\Annotation\SearchIndex;
use Nramos\SearchIndexer\Annotation\SearchProperty;
use Nramos\SearchIndexer\Indexer\IndexableEntityInterface;

#[ORM\Entity]
#[ORM\Table(name: 'houses')]
#[SearchIndex(indexName: 'houses', autoIndex: true)]
class House implements IndexableEntityInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    #[ORM\Column(type: 'integer')]
    #[SearchProperty(propertyName: 'id',isPk: true, filterable: true, sortable: false)]
  
    private $id;

    #[ORM\Column(type: 'string')]
    #[SearchProperty(propertyName: 'name', filterable: true, sortable: false)]
    private ?string $name = null;

    #[ORM\Column(type: 'integer')]
    #[SearchProperty(propertyName: 'price', filterable: false, sortable: true)]
    private $price;

    #[ORM\ManyToOne(targetEntity: HouseType::class)]
    #[ORM\JoinColumn(name: 'house_type_id', referencedColumnName: 'id')]
    #[SearchProperty(propertyName: 'type', relationProperties: ['typeName'], filterable: true, sortable: false)]
    private $houseType;

    #[ORM\ManyToMany(targetEntity: Heating::class, mappedBy: 'houses')]
    #[SearchProperty(propertyName: 'heatings', relationProperties: ['name'], filterable: true)]
    private Collection $heatings;

    public function __construct()
    {
        $this->heatings = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getPrice(): mixed
    {
        return $this->price;
    }

    public function setPrice(mixed $price): void
    {
        $this->price = $price;
    }
    #[SearchProperty(propertyName: 'houseTypeFormated', filterable: true, sortable: false)]
    public function getHouseTypeFormated(): mixed
    {
        return $this->houseType " - de 50 m²";
    }
    public function getHouseType(): mixed
    {
        return $this->houseType;
    }

    public function setHouseType(mixed $houseType): void
    {
        $this->houseType = $houseType;
    }

    public function setId(mixed $id): void
    {
        $this->id = $id;
    }
}
 
#[SearchIndex(indexName: 'houses', autoIndex: false)]



namespace Nramos\SearchIndexer\Indexer;

use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Nramos\SearchIndexer\Annotation\SearchIndex;
use Psr\Log\LoggerInterface;
use ReflectionClass;

/**
 * @see SearchIndexerSubscriberTest
 */
#[AsDoctrineListener(event: Events::postPersist, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::postUpdate, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::preRemove, priority: 0, connection: 'default')]
class SearchIndexerSubscriber
{
    public function __construct(
        private readonly IndexerInterface $indexer,
        private readonly LoggerInterface $logger
    )
    {
    }

    public function getSubscribedEvents(): array
    {
        return [
            Events::postPersist,
            Events::postUpdate,
            Events::preRemove,
        ];
    }

    /**
     * @param LifecycleEventArgs<EntityManagerInterface> $args
     */
    public function postPersist(LifecycleEventArgs $args): void
    {
        try {
            $this->indexEntity($args);
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage());
        }

    }

    /**
     * @param LifecycleEventArgs<EntityManagerInterface> $args
     */
    public function preRemove(LifecycleEventArgs $args): void
    {
        $entity = $args->getObject();
        if (!$entity instanceof IndexableEntityInterface) {
            return;
        }

        $reflectionClass = new ReflectionClass($entity);

        if ($reflectionClass->getAttributes(SearchIndex::class) && $reflectionClass->getAttributes(SearchIndex::class)[0]->newInstance()->autoIndex) {
            try {
                $this->indexer->remove($entity);
            } catch (\Exception $e) {
                $this->logger->error($e->getMessage());
            }

        }
    }

    /**
     * @param LifecycleEventArgs<EntityManagerInterface> $args
     */
    public function postUpdate(LifecycleEventArgs $args): void
    {
        try {
            $this->indexEntity($args);
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage());
        }

    }

    /**
     * @param LifecycleEventArgs<EntityManagerInterface> $args
     */
    private function indexEntity(LifecycleEventArgs $args): void
    {
        $entity = $args->getObject();
        if (!$entity instanceof IndexableEntityInterface) {
            return;
        }

        $reflectionClass = new ReflectionClass($entity);

        if ($reflectionClass->getAttributes(SearchIndex::class) && $reflectionClass->getAttributes(SearchIndex::class)[0]->newInstance()->autoIndex) {
            try {
                $this->indexer->index($entity);
            } catch (\Exception $e) {
                $this->logger->error($e->getMessage());
            }

        }
    }
}

use Nramos\SearchIndexer\Indexer\IndexableObjects;
use Nramos\SearchIndexer\Indexer\SearchClientInterface;
class MultiSearchService
{

    public function __construct(
        private SearchClientInterface $client,
        private IndexableObjects $indexableObjects,
      
    ) {}
    
    public function searchAcrossIndexes(string $query, array $filters = [], int $limit = 100, int $offset = 0): array
    {
        $multiSearchQueries = [];
        $indexableMap = [];
        $indexedClasses = $this->indexableObjects->getIndexedClasses();

        foreach ($indexedClasses as $className) {
            if (!class_exists($className)) {
                throw new \InvalidArgumentException(\sprintf("La classe %s n'existe pas.", $className));
            }
                $multiSearchQueries[] =  [
                    'indexUid' => $indexName,
                    'q' => $query,
                    'facets' => [],
                ];
                $indexableMap[$indexName] = $indexable;
            
        }

        return $this->client->multiSearch($multiSearchQueries);
       
    }

#[SearchIndex(indexName: 'biens', autoIndex: true)]

#[SearchProperty(propertyName: 'typeBien', relationProperties: [], filterable: true, sortable: true, searchable: true)]

#[SearchProperty(propertyName: 'id', isPk:true, relationProperties: [], filterable: true, sortable: true, searchable: true)]

#[ORM\ManyToMany(targetEntity: Heating::class, mappedBy: 'houses')]
#[SearchProperty(propertyName: 'heatings', relationProperties: ['name'], filterable: true)]
private Collection $heatings;

$filter = (new MeiliSearchFilter())
    ->addFilter('status', '=', 'active')
    ->addFilter('rating.users', '>', 85)
    ->addInFilter('role', ['admin', 'user'])
    ->addLocationFilter('radius', 48.8566, 2.3522, 5, 'km');

$results = $client->search(
    'houses', // Index name
    'search query', // Search query
    $filter, // Search filter
    10, // Limit
    1, // Page
    ['status', 'genres'] // Facets
);

#[ORM\Entity]
#[ORM\Table(name: 'houses')]
#[SearchIndex(indexName: 'houses', autoIndex: true)]
class House implements IndexableEntityInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    #[ORM\Column(type: 'integer')]
    #[SearchProperty(propertyName: 'id', isPk: true, filterable: true, sortable: false)]
    private $id;

    #[ORM\Column(type: 'string')]
    #[SearchProperty(propertyName: 'name', filterable: true, sortable: false)]
    private ?string $name = null;

    #[ORM\Column(type: 'integer')]
    #[SearchProperty(propertyName: 'price', filterable: false, sortable: true)]
    private $price;
}

#[SearchIndex(indexName: 'houses', autoIndex: false)]

#[AsDoctrineListener(event: Events::postPersist, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::postUpdate, priority: 0, connection: 'default')]
#[AsDoctrineListener(event: Events::preRemove, priority: 0, connection: 'default')]
class SearchIndexerSubscriber
{
    public function __construct(
        private readonly IndexerInterface $indexer,
        private readonly LoggerInterface $logger
    ) {}

    public function postPersist(LifecycleEventArgs $args): void
    {
        $this->indexEntity($args);
    }
}

public function searchAcrossIndexes(string $query, array $filters = [], int $limit = 100, int $offset = 0): array
{
    return $this->client->multiSearch($multiSearchQueries);
}