diff --git a/composer.json b/composer.json index 3ab58cae..3700e7fb 100644 --- a/composer.json +++ b/composer.json @@ -1,69 +1,75 @@ { - "name": "meilisearch/search-bundle", - "description": "Seamless integration of Meilisearch into your Symfony project.", - "keywords": [ - "meilisearch", - "instant", - "search", - "api", - "symfony", - "bundle" - ], - "type": "symfony-bundle", - "license": "MIT", - "authors": [ - { - "name": "David Sanchez", - "email": "david38sanchez@gmail.com" - } - ], - "require": { - "php": "^7.4|^8.0", - "ext-json": "*", - "doctrine/doctrine-bundle": "^2.4", - "illuminate/collections": "^8.47", - "meilisearch/meilisearch-php": "^0.24.0", - "symfony/filesystem": "^4.4 || ^5.0 || ^6.0", - "symfony/property-access": "^4.4 || ^5.0 || ^6.0", - "symfony/serializer": "^4.4 || ^5.0 || ^6.0" - }, - "require-dev": { - "doctrine/orm": "^2.9", - "friendsofphp/php-cs-fixer": "^3.0", - "nyholm/psr7": "^1.3", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^0.12.90", - "phpstan/phpstan-doctrine": "^0.12.39", - "phpstan/phpstan-phpunit": "^0.12.20", - "phpstan/phpstan-symfony": "^0.12.37", - "phpunit/php-code-coverage": "^9.2", - "phpunit/phpunit": "^9.5", - "symfony/doctrine-bridge": "^4.4 || ^5.0 || ^6.0", - "symfony/http-client": "^4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^4.4 || ^5.0 || ^6.0", - "symfony/yaml": "^4.4 || ^5.0 || ^6.0" - }, - "autoload": { - "psr-4": { - "MeiliSearch\\Bundle\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "MeiliSearch\\Bundle\\Test\\": "tests/" - } - }, - "config": { - "sort-packages": true, - "allow-plugins": { - "phpstan/extension-installer": true - } - }, - "scripts": { - "phpstan": "./vendor/bin/phpstan --memory-limit=1G --ansi", - "test:unit": "./vendor/bin/phpunit --colors=always --verbose", - "test:unit:coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always --coverage-html=tests/coverage", - "lint:check": "./vendor/bin/php-cs-fixer fix -v --using-cache=no --dry-run", - "lint:fix": "./vendor/bin/php-cs-fixer fix -v --using-cache=no" + "name": "meilisearch/search-bundle", + "description": "Seamless integration of Meilisearch into your Symfony project.", + "keywords": [ + "meilisearch", + "instant", + "search", + "api", + "symfony", + "bundle" + ], + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "David Sanchez", + "email": "david38sanchez@gmail.com" } + ], + "repositories": { + "meilisearch/meilisearch-php": { + "type": "git", + "url": "https://github.com/meilisearch/meilisearch-php" + } + }, + "require": { + "php": "^7.4|^8.0", + "ext-json": "*", + "doctrine/doctrine-bundle": "^2.4", + "illuminate/collections": "^8.47", + "meilisearch/meilisearch-php": "dev-bump-meilisearch-v0.28.0 as 1.2.3", + "symfony/filesystem": "^4.4 || ^5.0 || ^6.0", + "symfony/property-access": "^4.4 || ^5.0 || ^6.0", + "symfony/serializer": "^4.4 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/orm": "^2.9", + "friendsofphp/php-cs-fixer": "^3.0", + "nyholm/psr7": "^1.3", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^0.12.90", + "phpstan/phpstan-doctrine": "^0.12.39", + "phpstan/phpstan-phpunit": "^0.12.20", + "phpstan/phpstan-symfony": "^0.12.37", + "phpunit/php-code-coverage": "^9.2", + "phpunit/phpunit": "^9.5", + "symfony/doctrine-bridge": "^4.4 || ^5.0 || ^6.0", + "symfony/http-client": "^4.4 || ^5.0 || ^6.0", + "symfony/phpunit-bridge": "^4.4 || ^5.0 || ^6.0", + "symfony/yaml": "^4.4 || ^5.0 || ^6.0" + }, + "autoload": { + "psr-4": { + "MeiliSearch\\Bundle\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "MeiliSearch\\Bundle\\Test\\": "tests/" + } + }, + "config": { + "sort-packages": true, + "allow-plugins": { + "phpstan/extension-installer": true + } + }, + "scripts": { + "phpstan": "./vendor/bin/phpstan --memory-limit=1G --ansi", + "test:unit": "./vendor/bin/phpunit --colors=always --verbose", + "test:unit:coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --colors=always --coverage-html=tests/coverage", + "lint:check": "./vendor/bin/php-cs-fixer fix -v --using-cache=no --dry-run", + "lint:fix": "./vendor/bin/php-cs-fixer fix -v --using-cache=no" + } } diff --git a/src/CollectionXX.php b/src/CollectionXX.php new file mode 100644 index 00000000..46793e6e --- /dev/null +++ b/src/CollectionXX.php @@ -0,0 +1,146 @@ +items; + } + + /** + * Get the collection of items as a plain array. + * + * @return array + */ + public function toArray() + { + return $this->items; + } + + /** + * The items contained in the collection. + * + * @var array + */ + protected $items = []; + + /** + * Create a new collection. + * + * @param mixed $items + * @return void + */ + public function __construct($items = []) + { + $this->items = $items; + } + + /** + * Get an item from the collection by key. + * + * @param mixed $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + if ($this->offsetExists($key)) { + return $this->items[$key]; + } + + return $default; + } + + /** + * Transform each item in the collection using a callback. + * + * @param callable $callback + * @return $this + */ + public function transform(callable $callback) + { + $this->items = $this->map($callback)->all(); + + return $this; + } + + /** + * Run a map over each of the items. + * + * @param callable $callback + * @return static + */ + public function map(callable $callback) + { + $keys = array_keys($this->items); + + $items = array_map($callback, $this->items, $keys); + + return new static(array_combine($keys, $items)); + } + + /** + * Count the number of items in the collection. + * + * @return int + */ + #[\ReturnTypeWillChange] + public function count() + { + return count($this->items); + } + + /** + * Get all of the items in the collection. + * + * @return array + */ + public function all() + { + return $this->items; + } + + /** + * {@inheritDoc} + */ + public function offsetSet($offset, $value): void + { + $this->items[$offset] = $value; + } + + /** + * {@inheritDoc} + */ + public function offsetExists($offset): bool + { + return isset($this->items[$offset]) || \array_key_exists($offset, $this->items); + } + + /** + * {@inheritDoc} + */ + public function offsetUnset($offset): void + { + unset($this->items[$offset]); + } + + /** + * {@inheritDoc} + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + if (isset($this->items[$offset])) { + return $this->items[$offset]; + } + + return null; + } +} diff --git a/src/Command/IndexCommand.php b/src/Command/IndexCommand.php index 6b6ceb9f..acc6d198 100644 --- a/src/Command/IndexCommand.php +++ b/src/Command/IndexCommand.php @@ -4,7 +4,7 @@ namespace MeiliSearch\Bundle\Command; -use Illuminate\Support\Collection; +use MeiliSearch\Bundle\CollectionXX; use MeiliSearch\Bundle\SearchService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -26,24 +26,23 @@ public function __construct(SearchService $searchService) parent::__construct(); } - protected function getIndices(): Collection + protected function getIndices(): CollectionXX { - return collect($this->searchService->getConfiguration()->get('indices')) - ->transform(function (array $item) { + return (new CollectionXX($this->searchService->getConfiguration()->get('indices')))->transform(function (array $item) { $item['name'] = $this->prefix.$item['name']; return $item; }); } - protected function getEntitiesFromArgs(InputInterface $input, OutputInterface $output): Collection + protected function getEntitiesFromArgs(InputInterface $input, OutputInterface $output): CollectionXX { $indices = $this->getIndices(); - $indexNames = collect(); + $indexNames = new CollectionXX(); if ($indexList = $input->getOption('indices')) { $list = \explode(',', $indexList); - $indexNames = collect($list)->transform(function (string $item): string { + $indexNames = (new CollectionXX($list))->transform(function (string $item): string { // Check if the given index name already contains the prefix if (!str_contains($item, $this->prefix)) { return $this->prefix.$item; @@ -58,11 +57,15 @@ protected function getEntitiesFromArgs(InputInterface $input, OutputInterface $o 'No indices specified. Please either specify indices using the cli option or YAML configuration.' ); - return collect(); + return new CollectionXX(); } if (count($indexNames) > 0) { - return $indices->reject(fn (array $item) => !in_array($item['name'], $indexNames->toArray(), true)); + foreach ($indices->getItems() as $key => $value) { + if (!in_array($value['name'], $indexNames->toArray(), true)) { + unset($indices[$key]); + } + } } return $indices; diff --git a/src/Command/MeiliSearchCreateCommand.php b/src/Command/MeiliSearchCreateCommand.php index 0afc6a81..a95a83ba 100644 --- a/src/Command/MeiliSearchCreateCommand.php +++ b/src/Command/MeiliSearchCreateCommand.php @@ -4,6 +4,7 @@ namespace MeiliSearch\Bundle\Command; +use MeiliSearch\Bundle\CollectionXX; use MeiliSearch\Bundle\Exception\InvalidSettingName; use MeiliSearch\Bundle\Exception\TaskException; use MeiliSearch\Bundle\Model\Aggregator; @@ -50,7 +51,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (is_subclass_of($entityClassName, Aggregator::class)) { $indexes->forget($key); - $indexes = collect(array_merge( + $indexes = new CollectionXX(array_merge( $indexes->toArray(), array_map( static fn ($entity) => ['name' => $index['name'], 'class' => $entity], @@ -85,6 +86,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $task = $indexInstance->{$method}($value); + + $indexInstance->waitForTask($task['taskUid']); $task = $indexInstance->getTask($task['taskUid']); if ('failed' === $task['status']) { diff --git a/src/Command/MeiliSearchDeleteCommand.php b/src/Command/MeiliSearchDeleteCommand.php index b1a9a636..5ccd8cbf 100644 --- a/src/Command/MeiliSearchDeleteCommand.php +++ b/src/Command/MeiliSearchDeleteCommand.php @@ -4,6 +4,7 @@ namespace MeiliSearch\Bundle\Command; +use MeiliSearch\Bundle\CollectionXX; use MeiliSearch\Exceptions\ApiException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -33,10 +34,17 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $indexToDelete = collect($this->getEntitiesFromArgs($input, $output))->unique('name'); + $list = $this->getEntitiesFromArgs($input, $output); + + $indexesToDelete = []; + foreach($list as $element) { + $hash = $element['name']; + $indexesToDelete[$hash] = $element; + } + $indexesToDelete = new CollectionXX(array_values($indexesToDelete)); /** @var array $index */ - foreach ($indexToDelete as $index) { + foreach ($indexesToDelete as $index) { $indexName = $index['name']; try { $this->searchService->deleteByIndexName($indexName); @@ -47,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln('Deleted '.$indexName.''); } - if (0 === count($indexToDelete)) { + if (0 === count($indexesToDelete)) { $output->writeln('Cannot delete index. Not found.'); } diff --git a/src/Command/MeiliSearchImportCommand.php b/src/Command/MeiliSearchImportCommand.php index 30b23af3..bb935336 100644 --- a/src/Command/MeiliSearchImportCommand.php +++ b/src/Command/MeiliSearchImportCommand.php @@ -9,6 +9,7 @@ use MeiliSearch\Bundle\Exception\TaskException; use MeiliSearch\Bundle\Model\Aggregator; use MeiliSearch\Bundle\SearchService; +// use MeiliSearch\Bundle\CollectionXX; use MeiliSearch\Client; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -74,7 +75,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (is_subclass_of($entityClassName, Aggregator::class)) { $indexes->forget($key); - $indexes = collect(array_merge( + $indexes = new CollectionXX(array_merge( $indexes->toArray(), array_map( fn ($entity) => ['class' => $entity], diff --git a/src/SearchService.php b/src/SearchService.php index 03f0741b..07a520a8 100644 --- a/src/SearchService.php +++ b/src/SearchService.php @@ -5,7 +5,7 @@ namespace MeiliSearch\Bundle; use Doctrine\Persistence\ObjectManager; -use Illuminate\Support\Collection; +use MeiliSearch\Bundle\CollectionXX; /** * Interface SearchService. @@ -22,7 +22,7 @@ public function isSearchable($className): bool; public function getSearchable(): array; - public function getConfiguration(): Collection; + public function getConfiguration(): CollectionXX; /** * Get the index name for the given `$className`. diff --git a/src/Services/MeiliSearchService.php b/src/Services/MeiliSearchService.php index be2e7154..2edba069 100644 --- a/src/Services/MeiliSearchService.php +++ b/src/Services/MeiliSearchService.php @@ -6,7 +6,7 @@ use Doctrine\Common\Util\ClassUtils; use Doctrine\Persistence\ObjectManager; -use Illuminate\Support\Collection; +use MeiliSearch\Bundle\CollectionXX; use MeiliSearch\Bundle\Engine; use MeiliSearch\Bundle\Entity\Aggregator; use MeiliSearch\Bundle\Exception\ObjectIdNotFoundException; @@ -25,7 +25,7 @@ final class MeiliSearchService implements SearchService { private SerializerInterface $normalizer; private Engine $engine; - private Collection $configuration; + private CollectionXX $configuration; private PropertyAccessor $propertyAccessor; private array $searchableEntities; private array $entitiesAggregators; @@ -37,7 +37,7 @@ public function __construct(SerializerInterface $normalizer, Engine $engine, arr { $this->normalizer = $normalizer; $this->engine = $engine; - $this->configuration = new Collection($configuration); + $this->configuration = new CollectionXX($configuration); $this->propertyAccessor = PropertyAccess::createPropertyAccessor(); $this->setSearchableEntities(); @@ -63,7 +63,7 @@ public function getSearchable(): array return $this->searchableEntities; } - public function getConfiguration(): Collection + public function getConfiguration(): CollectionXX { return $this->configuration; } @@ -73,8 +73,9 @@ public function getConfiguration(): Collection */ public function searchableAs(string $className): string { - $indexes = Collection::wrap($this->getConfiguration()->get('indices')); - $index = $indexes->firstWhere('class', $className); + $indexes = $this->getConfiguration()->get('indices'); + $indexes = array_filter($indexes, fn ($item) => $item['class'] == $className); + $index = reset($indexes); return $this->getConfiguration()->get('prefix').$index['name']; } diff --git a/tests/BaseKernelTestCase.php b/tests/BaseKernelTestCase.php index 1eb39228..0f08084f 100644 --- a/tests/BaseKernelTestCase.php +++ b/tests/BaseKernelTestCase.php @@ -186,12 +186,11 @@ protected function getFileName(string $indexName, string $type): string private function cleanUp(): void { - collect($this->searchService->getConfiguration()->get('indices')) - ->each(function ($item): bool { - $this->cleanupIndex($this->getPrefix().$item['name']); + $indexes = $this->searchService->getConfiguration()->get('indices'); - return true; - }); + foreach ($indexes as $item) { + $this->cleanupIndex($this->getPrefix().$item['name']); + } $this->cleanupIndex($this->getPrefix().'indexA'); $this->cleanupIndex($this->getPrefix().'indexB'); diff --git a/tests/Integration/SearchTest.php b/tests/Integration/SearchTest.php index abff7ea6..c01f688e 100644 --- a/tests/Integration/SearchTest.php +++ b/tests/Integration/SearchTest.php @@ -63,7 +63,9 @@ public function testSearchImportAggregator(): void $commandTester = new CommandTester($command); $commandTester->execute([ '--indices' => $this->index->getUid(), - ]); + ], ['verbosity' => true, 'capture_stderr_separately' => true]); + + var_dump($commandTester->getOutput()->__toString()); $output = $commandTester->getDisplay();