Meilisearch is an open-source search engine written in Rust, designed to create lightning-fast and hyper-relevant search experiences out of the box.
composer require setono/sylius-meilisearch-plugin
# config/packages/setono_sylius_meilisearch.yaml
setono_sylius_meilisearch:
indexes:
products:
document: 'Setono\SyliusMeilisearchPlugin\Document\Product'
entities: [ 'App\Entity\Product\Product' ]
search:
index: products
In your .env.local
add your parameters:
###> setono/sylius-meilisearch-plugin ###
MEILISEARCH_HOST=http://localhost:7700
MEILISEARCH_MASTER_KEY=YOUR_MASTER_KEY
MEILISEARCH_SEARCH_KEY=YOUR_SEARCH_KEY
###< setono/sylius-meilisearch-plugin ###
# config/routes/setono_sylius_meilisearch.yaml
setono_sylius_meilisearch:
resource: "@SetonoSyliusMeilisearchPlugin/Resources/config/routes.yaml"
or if your app doesn't use locales:
# config/routes/setono_sylius_meilisearch.yaml
setono_sylius_meilisearch:
resource: "@SetonoSyliusMeilisearchPlugin/Resources/config/routes_no_locale.yaml"
The entities you've configured for indexing has to implement the Setono\SyliusMeilisearchPlugin\Model\IndexableInterface
.
In a typical Sylius application for the Product
entity it could look like this:
<?php
declare(strict_types=1);
namespace App\Entity\Product;
use Doctrine\ORM\Mapping as ORM;
use Setono\SyliusMeilisearchPlugin\Model\IndexableAwareTrait;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;
use Sylius\Component\Core\Model\Product as BaseProduct;
/**
* @ORM\Entity
* @ORM\Table(name="sylius_product")
*/
class Product extends BaseProduct implements IndexableInterface
{
public function getDocumentIdentifier(): ?string
{
return (string) $this->getId();
}
}
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate -n
php bin/console assets:install
To populate the index you can run the following command:
php bin/console setono:sylius-meilisearch:index
The plugin will detect most changes in your entities and update the index accordingly. However, it will not detect changes in associations, so you might need to reindex the whole index from time to time.
When indexing entities, most likely there are some of the entities that you don't want included in the index. There are two ways you can do this. 1) When data is fetched from the database or 2) when data is traversed during indexing. Obviously, the first option is the most efficient, but let's look at both.
Here you will listen to the \Setono\SyliusMeilisearchPlugin\Event\QueryBuilderForDataProvisionCreated
event and modify the query builder accordingly.
Here is an example where we filter out disabled products:
<?php
namespace App\EventSubscriber;
use Doctrine\ORM\QueryBuilder;
use Setono\SyliusMeilisearchPlugin\Event\QueryBuilderForDataProvisionCreated;
use Sylius\Component\Resource\Model\ToggleableInterface;use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class FilterDisabledEntitiesSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
QueryBuilderForDataProvisionCreated::class => 'filter',
];
}
public function filter(QueryBuilderForDataProvisionCreated $event): void
{
if(!is_a($event->entity, ToggleableInterface::class, true)) {
return;
}
$queryBuilder = $event->getQueryBuilder();
$alias = $queryBuilder->getRootAliases()[0];
$queryBuilder->andWhere($alias . '.enabled = true');
}
}
Here you will implement the \Setono\SyliusMeilisearchPlugin\Model\FilterableInterface
in your entity and implement the filter
method.
The example below is the same as the previous example, but this time we filter out disabled products when traversing the data:
<?php
namespace App\Entity\Product;
use Doctrine\ORM\Mapping as ORM;
use Setono\SyliusMeilisearchPlugin\Model\FilterableInterface;
use Setono\SyliusMeilisearchPlugin\Model\IndexableInterface;
use Sylius\Component\Core\Model\Product as BaseProduct;
/**
* @ORM\Entity
* @ORM\Table(name="sylius_product")
*/
class Product extends BaseProduct implements IndexableInterface, FilterableInterface
{
public function getDocumentIdentifier(): ?string
{
return (string) $this->getId();
}
public function filter(): bool
{
return $this->isEnabled();
}
}
To run the functional tests in the plugin, here are the steps:
-
Ensure you have Meilisearch running and that you've set the required environment variables in
.env.test.local
. -
Create the test database
php bin/console doctrine:database:create --env=test
-
Update the test database schema
php bin/console doctrine:schema:update --env=test --force
-
Load fixtures
php bin/console sylius:fixtures:load -n --env=test
-
Populate the index
php bin/console setono:sylius-meilisearch:index --env=test
-
Run the tests
vendor/bin/phpunit --testsuite Functional