diff --git a/.circleci/config.yml b/.circleci/config.yml index 3cba59e..60d22a7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,13 +2,30 @@ version: 2 jobs: build: docker: - - image: circleci/php:7.1-cli + - image: circleci/php:7.2-cli working_directory: ~/project steps: - checkout + + - run: + name: Install PHPUnit + command: | + composer require phpunit/phpunit:7.5.17 --prefer-dist --prefer-stable --prefer-lowest --no-suggest + + - run: + name: Run tests / Symfony 3^3 + command: | + php vendor/bin/phpunit + + - run: + name: Run tests / Symfony 4^3 + command: | + composer update -n --prefer-dist --prefer-stable --no-suggest + php vendor/bin/phpunit + - run: - name: Run tests + name: Run tests / Symfony 5^0 command: | - composer install -n --prefer-dist --no-suggest - composer test \ No newline at end of file + composer update -n --prefer-dist --no-suggest + php vendor/bin/phpunit \ No newline at end of file diff --git a/Http/AsyncTestClient.php b/Http/AsyncTestClient.php deleted file mode 100644 index 7d23938..0000000 --- a/Http/AsyncTestClient.php +++ /dev/null @@ -1,119 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Apisearch\Http; - -use Clue\React\Block; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\AsyncKernel; - -/** - * Class AsyncTestClient. - */ -class AsyncTestClient extends Client implements HttpClient -{ - /** - * @var AsyncKernel - * - * Async kernel - */ - private $kernel; - - /** - * TestClient constructor. - * - * @param AsyncKernel $kernel - * @param string $version - * @param RetryMap $retryMap - */ - public function __construct( - AsyncKernel $kernel, - string $version, - RetryMap $retryMap - ) { - $this->kernel = $kernel; - - parent::__construct( - $version, - $retryMap - ); - } - - /** - * Get a response given some parameters. - * Return an array with the status code and the body. - * - * @param string $url - * @param string $method - * @param array $query - * @param array $body - * @param array $server - * - * @return array - */ - public function get( - string $url, - string $method, - array $query = [], - array $body = [], - array $server = [] - ): array { - $method = trim(strtolower($method)); - $requestParts = $this->buildRequestParts( - $url, - $query, - $body, - $server - ); - - $headersFormatted = []; - foreach ($server as $key => $value) { - $headersFormatted['HTTP_'.str_replace('-', '_', $key)] = $value; - } - - $request = new Request( - array_map('urldecode', $query), - [], - [], - [], - [], - array_merge($headersFormatted, [ - 'CONTENT_TYPE' => 'application/json', - 'HTTP_REFERER' => 'http://localhost', - ]), - json_encode($requestParts->getParameters()['json']) - ); - - $request->setMethod($method); - $request->server->set('REQUEST_URI', $requestParts->getUrl()); - - $promise = $this - ->kernel - ->handleAsync($request); - - $response = Block\await( - $promise, - $this - ->kernel - ->getContainer() - ->get('reactphp.event_loop') - ); - - return [ - 'code' => $response->getStatusCode(), - 'body' => json_decode($response->getContent(), true), - ]; - } -} diff --git a/Transformer/ItemTransformed.php b/Transformer/ItemTransformed.php index bda7ff3..15673f0 100644 --- a/Transformer/ItemTransformed.php +++ b/Transformer/ItemTransformed.php @@ -16,12 +16,30 @@ namespace Apisearch\Transformer; use Apisearch\Model\Item; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event as ContractEvent; +use Symfony\Component\EventDispatcher\Event as ComponentEvent; /** - * Class ItemTransformed. + * This code adds usability with Symfony 3.4 -> 5.0 */ -class ItemTransformed extends Event +if (class_exists("Symfony\Contracts\EventDispatcher\Event")) { + + /** + * Class BaseTransformed. + */ + class BaseTransformed extends ContractEvent {} +} else { + + /** + * Class BaseTransformed. + */ + class BaseTransformed extends ComponentEvent {} +} + +/** + * Class ItemTransformed + */ +class ItemTransformed extends BaseTransformed { /** * @var Item diff --git a/Transformer/Transformer.php b/Transformer/Transformer.php index 107a529..cbe382a 100644 --- a/Transformer/Transformer.php +++ b/Transformer/Transformer.php @@ -18,6 +18,7 @@ use Apisearch\Model\Item; use Apisearch\Model\ItemUUID; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface; /** * Class Transformer. @@ -142,15 +143,29 @@ public function toItem($object): ? Item foreach ($this->writeTransformers as $writeTransformer) { if ($writeTransformer->isValidObject($object)) { $item = $writeTransformer->toItem($object); - $this - ->eventDispatcher - ->dispatch( - 'apisearch.item_transformed', - new ItemTransformed( - $item, - $object - ) - ); + + if ($this->eventDispatcher instanceof ContractsEventDispatcherInterface) { + $this + ->eventDispatcher + ->dispatch( + new ItemTransformed( + $item, + $object + ), + 'apisearch.item_transformed' + ); + } else { + $this + ->eventDispatcher + ->dispatch( + 'apisearch.item_transformed', + new ItemTransformed( + $item, + $object + ) + ); + } + return $item; } diff --git a/Url/UrlBuilder.php b/Url/UrlBuilder.php deleted file mode 100644 index c113ac8..0000000 --- a/Url/UrlBuilder.php +++ /dev/null @@ -1,477 +0,0 @@ - - */ - -declare(strict_types=1); - -namespace Apisearch\Url; - -use Apisearch\Query\Filter; -use Apisearch\Query\SortBy; -use Apisearch\Result\Aggregation; -use Apisearch\Result\Counter; -use Apisearch\Result\Result; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\RouterInterface; - -/** - * Class UrlBuilder. - */ -class UrlBuilder -{ - /** - * Routes dictionary. - */ - private $routesDictionary = []; - - /** - * @var RouterInterface - * - * Router - */ - private $router; - - /** - * @var array - * - * Routes cache - */ - private $routesCache = []; - - /** - * UrlBuilder constructor. - * - * @param RouterInterface $router - */ - public function __construct(RouterInterface $router) - { - $this->router = $router; - } - - /** - * Set routes dictionary. - * - * @param array $routesDictionary - */ - public function setRoutesDictionary(array $routesDictionary) - { - $this->routesDictionary = $routesDictionary; - } - - /** - * Guess filter value. - * - * @param Result $result - * @param Aggregation $aggregation - * @param Counter $counter - * - * @return string - */ - public function guessFilterValue( - Result $result, - Aggregation $aggregation, - Counter $counter - ): string { - return $counter->isUsed() - ? $this->removeFilterValue( - $result, - $aggregation->getName(), - $counter->getId() - ) - : $this->addFilterValue( - $result, - $aggregation->getName(), - $counter->getId() - ); - } - - /** - * Add filter into query. - * - * @param Result $result - * @param string $filterName - * @param string $value - * - * @return string - */ - public function addFilterValue( - Result $result, - string $filterName, - string $value - ): string { - if (isset($this->routesCache[spl_object_hash($result)][$filterName])) { - return str_replace( - ['{id}', '{slug}'], - $result->getAggregation($filterName)->getCounter($value)->getValues(), - $this->routesCache[spl_object_hash($result)][$filterName] - ); - } - - $urlParameters = $this->generateQueryUrlParameters($result, $filterName); - if ( - !isset($urlParameters[$filterName]) || - !in_array($value, $urlParameters[$filterName]) - ) { - $urlParameters[$filterName][] = $value; - } - - $urlElements = $this->createUrlByUrlParameters( - $result, - $urlParameters - ); - - $templateRoute = in_array($urlElements['field'], [false, $filterName]) - ? $urlElements['template_path'] - : parse_url($urlElements['route'], PHP_URL_PATH); - - $filteredUrlParameters = $urlElements['url_parameters']; - $routeQuery = parse_url($urlElements['route'], PHP_URL_QUERY); - $route = rtrim("$templateRoute?$routeQuery", '?'); - if (isset($filteredUrlParameters[$filterName])) { - $paremeterKey = array_search($value, $filteredUrlParameters[$filterName]); - $route = str_replace( - "{$filterName}[$paremeterKey]=$value", - "{$filterName}[$paremeterKey]={id}", - $route - ); - } - - $this->routesCache[spl_object_hash($result)][$filterName] = $route; - - return $urlElements['route']; - } - - /** - * Remove filter from query. - * - * @param Result $result - * @param string $filterName - * @param string $value - * - * @return string - */ - public function removeFilterValue( - Result $result, - string $filterName, - string $value = null - ): string { - $urlParameters = $this->generateQueryUrlParameters($result); - - if ( - is_null($value) || - !isset($urlParameters[$filterName]) - ) { - unset($urlParameters[$filterName]); - } elseif (false !== ($key = array_search($value, $urlParameters[$filterName]))) { - unset($urlParameters[$filterName][$key]); - } - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Change price range. - * - * @param Result $result - * - * @return string - */ - public function removePriceRangeFilter(Result $result): string - { - $urlParameters = $this->generateQueryUrlParameters($result); - unset($urlParameters['price']); - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Get current. - * - * @param Result $result - * @param bool $addQuersyStringPlaceholder - * - * @return string - */ - public function getCurrent( - Result $result, - bool $addQuersyStringPlaceholder = false - ): ? string { - $urlParameters = $this->generateQueryUrlParameters($result); - if ($addQuersyStringPlaceholder) { - $urlParameters['q'] = '{{q}}'; - } - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Set pagination. - * - * @param Result $result - * @param int $page - * - * @return string - */ - public function addPage( - Result $result, - int $page - ): string { - $urlParameters = $this->generateQueryUrlParameters($result); - $urlParameters['page'] = $page; - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Add previous page. - * - * @param Result $result - * - * @return string|null - */ - public function addPrevPage(Result $result): ? string - { - $query = $result->getQuery(); - $urlParameters = $this->generateQueryUrlParameters($result); - $page = $query->getPage(); - $prevPage = $page - 1; - - if ($prevPage < 1) { - return null; - } - - $urlParameters['page'] = $prevPage; - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Add next page. - * - * @param Result $result - * - * @return string|null - */ - public function addNextPage(Result $result): ? string - { - $query = $result->getQuery(); - $urlParameters = $this->generateQueryUrlParameters($result); - $page = $query->getPage(); - $nextPage = $page + 1; - - if ((($nextPage - 1) * $query->getSize()) > $result->getTotalHits()) { - return null; - } - - $urlParameters['page'] = $nextPage; - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Add sort by. Return null if doesn't change. - * - * @param Result $result - * @param string $field - * @param string $mode - * - * @return string - */ - public function addSortBy( - Result $result, - string $field, - string $mode - ): ? string { - $urlParameters = $this->generateQueryUrlParameters($result); - - if ( - isset($urlParameters['sort_by'][$field]) && - $urlParameters['sort_by'][$field] == $mode - ) { - return null; - } - - if ( - !isset($urlParameters['sort_by']) && - SortBy::SCORE === [$field => $mode] - ) { - return null; - } - - unset($urlParameters['sort_by']); - - if (SortBy::SCORE !== [$field => $mode]) { - $urlParameters['sort_by'][$field] = $mode; - } - - return $this->createUrlByUrlParameters( - $result, - $urlParameters - )['route']; - } - - /** - * Query to url parameters. - * - * @param Result $result - * @param string $filterName - * - * @return array - */ - private function generateQueryUrlParameters( - Result $result, - string $filterName = null - ): array { - $query = $result->getQuery(); - $queryFilters = $query->getFilters(); - $urlParameters = []; - foreach ($queryFilters as $currentFilterName => $filter) { - /* - * Special case for elements with LEVEL. - */ - $urlParameters[$currentFilterName] = ( - !is_null($filterName) && - $currentFilterName === $filterName && - Filter::MUST_ALL_WITH_LEVELS === $filter->getApplicationType() - ) - ? [] - : $filter->getValues(); - } - - unset($urlParameters['_query']); - - $queryFilter = $query->getFilter('_query'); - $queryString = $queryFilter instanceof Filter - ? $queryFilter->getValues()[0] - : ''; - - if (!empty($queryString)) { - $urlParameters['q'] = $queryString; - } - - $sort = $query->getSortBy(); - if (SortBy::SCORE !== $sort) { - $urlParameters['sort_by'] = $sort; - } - - return $urlParameters; - } - - /** - * Generate url by query parameters. - * - * Given a route dictionary, we should apply the first one encontered - * - * @param Result $result - * @param array $urlParameters - * - * @return string[] - */ - private function createUrlByUrlParameters( - Result $result, - array $urlParameters - ): array { - foreach ($this->routesDictionary as $field => $route) { - if ( - !isset($urlParameters[$field]) || - ( - is_array($urlParameters[$field]) && - 1 !== count($urlParameters[$field]) - ) - ) { - continue; - } - - $value = is_array($urlParameters[$field]) - ? reset($urlParameters[$field]) - : $urlParameters[$field]; - - if (!$result - ->getAggregation($field) - ->getAllElements()[$value] instanceof Counter) { - continue; - } - - unset($urlParameters[$field]); - $path = $this - ->router - ->getRouteCollection() - ->get($route) - ->getPath(); - preg_match_all( - '~\{(.+?)\}~', - $path, - $matches - ); - - return [ - 'route' => urldecode($this - ->router - ->generate( - $route, - array_merge( - array_intersect_key( - $result - ->getAggregation($field) - ->getAllElements()[$value] - ->getValues(), - array_flip($matches[1]) - ), - $urlParameters - ), - UrlGeneratorInterface::ABSOLUTE_URL - )), - 'url_parameters' => $urlParameters, - 'template_path' => $path, - 'field' => $field, - ]; - } - - return [ - 'route' => urldecode($this - ->router - ->generate( - $this->routesDictionary['main'], - $urlParameters, - UrlGeneratorInterface::ABSOLUTE_URL - )), - 'url_parameters' => $urlParameters, - 'template_path' => urldecode($this - ->router - ->generate( - $this->routesDictionary['main'], - [], - UrlGeneratorInterface::ABSOLUTE_URL - ) - ), - 'field' => false, - ]; - } -} diff --git a/composer.json b/composer.json index 86bf66d..d65e8a7 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,6 @@ { "name": "apisearch-io/php-client", + "description": "PHP Library for Apisearch", "type": "library", "license": "MIT", "authors": [ @@ -11,29 +12,15 @@ "require": { "php": ">=7.1", "ext-curl": "*", - "symfony/event-dispatcher": "^3.4|^4.0", - "nesbot/carbon": "^1.22|^2.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.0.0", - "mmoreram/php-formatter": "^1.3.1", - "friendsofphp/php-cs-fixer": "^2.5.0" - }, - "suggest": { - "symfony/yaml": "Install for Yaml import/export" + "symfony/event-dispatcher": "^3.4 || ^4.0 || ^5.0", + "symfony/yaml": "^3.4 || ^4.0 || ^5.0", + "nesbot/carbon": "^1.22 || ^2.0", + "phpunit/phpunit": "7.5.*" }, "autoload": { "psr-4": { "Apisearch\\": "" } }, - "scripts": { - "fix-code": [ - "vendor/bin/php-cs-fixer fix --config=.php_cs", - "vendor/bin/php-formatter f:h:f . --exclude=vendor", - "vendor/bin/php-formatter f:s:f . --exclude=vendor", - "vendor/bin/php-formatter f:u:s . --exclude=vendor" - ], - "test": "vendor/bin/phpunit" - } -} \ No newline at end of file + "minimum-stability": "dev" +}