diff --git a/src/Controller/Filter.php b/src/Controller/Filter.php new file mode 100644 index 0000000..fa749dc --- /dev/null +++ b/src/Controller/Filter.php @@ -0,0 +1,82 @@ +, Gally Team + * @copyright 2022-present Smile + * @license Open Software License v. 3.0 (OSL-3.0) + */ + +declare(strict_types=1); + +namespace Gally\SyliusPlugin\Controller; + +use Gally\SyliusPlugin\Form\Type\Filter\GallyDynamicFilterType; +use Gally\SyliusPlugin\Grid\Filter\Type\SelectFilterType; +use Gally\SyliusPlugin\Search\Adapter; +use Gally\SyliusPlugin\Service\FilterConverter; +use Sylius\Bundle\TaxonomyBundle\Doctrine\ORM\TaxonRepository; +use Sylius\Component\Channel\Context\ChannelContextInterface; +use Sylius\Component\Locale\Context\LocaleContextInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +final class Filter extends AbstractController +{ + public function __construct( + private Adapter $adapter, + private ChannelContextInterface $channelContext, + private LocaleContextInterface $localeContext, + private TaxonRepository $taxonRepository, + private FormFactoryInterface $formFactory, + private FilterConverter $filterConverter, + ) { + } + + public function viewMore(Request $request, string $filterField): Response + { + $search = $request->get('search'); + $filters = $request->get('filters')['gally'] ?? []; + $gallyFilters = []; + foreach ($filters as $field => $value) { + $gallyFilter = $this->filterConverter->convert($field, $value); + if ($gallyFilter) { + $gallyFilters[] = $gallyFilter; + } + } + + $choices = []; + $currentTaxonId = $request->get('taxon'); + $aggregationOptions = $this->adapter->viewMoreOption( + $this->channelContext->getChannel(), + $currentTaxonId ? $this->taxonRepository->find($currentTaxonId) : null, + $this->localeContext->getLocaleCode(), + $filterField, + $gallyFilters, + $search, + ); + + foreach ($aggregationOptions as $option) { + $choices[$option['label']] = $option['value']; + } + + $options = [ + 'block_prefix' => 'sylius_gally_filter_checkbox', + 'choices' => $choices, + 'expanded' => true, + 'multiple' => true, + ]; + + $form = $this->formFactory->createNamed('criteria')->add('gally', GallyDynamicFilterType::class); + $form->get('gally')->add($filterField, SelectFilterType::class, $options); + $form->get('gally')->get($filterField)->setData($filters[$filterField] ?? null); + $html = $this->renderView('@GallySyliusPlugin/Grid/Filter/gally_dynamic_filter.html.twig', ['form' => $form]); + + return $this->json(['html' => $html]); + } +} diff --git a/src/Form/Type/Filter/GallyDynamicFilterType.php b/src/Form/Type/Filter/GallyDynamicFilterType.php index 9fbec0f..ffc9b7e 100644 --- a/src/Form/Type/Filter/GallyDynamicFilterType.php +++ b/src/Form/Type/Filter/GallyDynamicFilterType.php @@ -15,16 +15,29 @@ namespace Gally\SyliusPlugin\Form\Type\Filter; use Gally\SyliusPlugin\Event\GridFilterUpdateEvent; +use Gally\SyliusPlugin\Grid\Filter\Type\SelectFilterType; use Gally\SyliusPlugin\Search\Aggregation\Aggregation; use Gally\SyliusPlugin\Search\Aggregation\AggregationOption; use Sylius\Bundle\GridBundle\Form\Type\Filter\BooleanFilterType; -use Sylius\Bundle\GridBundle\Form\Type\Filter\SelectFilterType; +use Sylius\Bundle\TaxonomyBundle\Doctrine\ORM\TaxonRepository; +use Sylius\Component\Grid\Parameters; +use Sylius\Component\Locale\Context\LocaleContextInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\RangeType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; class GallyDynamicFilterType extends AbstractType { + public function __construct( + private UrlGeneratorInterface $router, + private RequestStack $requestStack, + private TaxonRepository $taxonRepository, + private LocaleContextInterface $localeContext, + ) { + } + /** * @var Aggregation[] */ @@ -78,16 +91,20 @@ public function buildForm(FormBuilderInterface $builder, array $options): void /** @var AggregationOption $option */ $choices[$option->getLabel()] = $option->getId(); } + $options = [ + 'block_prefix' => 'sylius_gally_filter_checkbox', + 'label' => $aggregation->getLabel(), + 'choices' => $choices, + 'expanded' => true, + 'multiple' => true, + ]; + if ($aggregation->hasMore()) { + $options['has_more_url'] = $this->buildHasMoreUrl($aggregation->getField()); + } $builder->add( $aggregation->getField(), SelectFilterType::class, - [ - 'block_prefix' => 'sylius_gally_filter_checkbox', - 'label' => $aggregation->getLabel(), - 'choices' => $choices, - 'expanded' => true, - 'multiple' => true, - ] + $options ); break; default: @@ -100,4 +117,24 @@ public function onFilterUpdate(GridFilterUpdateEvent $event): void { $this->aggregations = $event->getAggregations(); } + + private function buildHasMoreUrl(string $field): string + { + $request = $this->requestStack->getCurrentRequest(); + $parameters = new Parameters($request->query->all()); + $criteria = $parameters->get('criteria', []); + $search = (isset($criteria['search'], $criteria['search']['value'])) ? $criteria['search']['value'] : ''; + unset($criteria['search']); + $taxon = $this->taxonRepository->findOneBySlug($request->attributes->get('slug'), $this->localeContext->getLocaleCode()); + + return $this->router->generate( + 'gally_filter_view_more_ajax', + [ + 'filterField' => $field, + 'search' => $search, + 'filters' => $criteria, + 'taxon' => $taxon->getId(), + ] + ); + } } diff --git a/src/Grid/Filter/GallyDynamicFilter.php b/src/Grid/Filter/GallyDynamicFilter.php index 11272b8..effb454 100644 --- a/src/Grid/Filter/GallyDynamicFilter.php +++ b/src/Grid/Filter/GallyDynamicFilter.php @@ -14,35 +14,22 @@ namespace Gally\SyliusPlugin\Grid\Filter; +use Gally\SyliusPlugin\Service\FilterConverter; use Sylius\Component\Grid\Data\DataSourceInterface; use Sylius\Component\Grid\Filtering\FilterInterface; class GallyDynamicFilter implements FilterInterface { + public function __construct(private FilterConverter $filterConverter) + { + } + public function apply(DataSourceInterface $dataSource, string $name, $data, array $options): void { foreach ($data as $field => $value) { - if ('' === $value) { - continue; - } - - if (str_contains($field, '_slider')) { - $field = str_replace('_slider', '', $field); - $values = explode(';', $value, 2); - $dataSource->restrict($dataSource->getExpressionBuilder()->andX( - $dataSource->getExpressionBuilder()->greaterThanOrEqual($field, (int) $values[0]), - $dataSource->getExpressionBuilder()->lessThanOrEqual($field, (int) $values[1]), - )); - } elseif (str_contains($field, '_boolean')) { - $field = str_replace('_boolean', '', $field); - $value = ('true' === $value); - $dataSource->restrict($dataSource->getExpressionBuilder()->equals($field, $value)); - } else { - if (\is_array($value)) { - $dataSource->restrict($dataSource->getExpressionBuilder()->in($field, $value)); - } else { - $dataSource->restrict($dataSource->getExpressionBuilder()->equals($field, $value)); - } + $gallyFilter = $this->filterConverter->convert($field, $value); + if ($gallyFilter) { + $dataSource->restrict($gallyFilter); } } } diff --git a/src/Grid/Filter/Type/SelectFilterType.php b/src/Grid/Filter/Type/SelectFilterType.php new file mode 100644 index 0000000..4f50753 --- /dev/null +++ b/src/Grid/Filter/Type/SelectFilterType.php @@ -0,0 +1,40 @@ +, Gally Team + * @copyright 2022-present Smile + * @license Open Software License v. 3.0 (OSL-3.0) + */ + +declare(strict_types=1); + +namespace Gally\SyliusPlugin\Grid\Filter\Type; + +use Sylius\Bundle\GridBundle\Form\Type\Filter\SelectFilterType as BaseSelectFilterType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolver; + +final class SelectFilterType extends AbstractType +{ + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefined(['has_more_url']) + ->addAllowedTypes('has_more_url', 'string'); + } + + public function getParent(): string + { + return BaseSelectFilterType::class; + } + + public function buildView(FormView $view, FormInterface $form, array $options): void + { + $view->vars['has_more_url'] = $options['has_more_url'] ?? null; + } +} diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index a04f43b..29e8071 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -31,6 +31,9 @@ + + + @@ -42,6 +45,19 @@ + + + + + + + + + + + + + @@ -64,11 +80,20 @@ + + + + + + + + + diff --git a/src/Resources/config/shop_routing.yml b/src/Resources/config/shop_routing.yml index a71e255..b5a53e5 100644 --- a/src/Resources/config/shop_routing.yml +++ b/src/Resources/config/shop_routing.yml @@ -1 +1,7 @@ -# Define your own shop routes here +#sylius_shop_partial_cart_add_item_ajax: +gally_filter_view_more_ajax: + path: /viewMore/{filterField} + methods: [ GET ] + defaults: + _controller: Gally\SyliusPlugin\Controller\Filter::viewMore + _format: json diff --git a/src/Resources/public/view-more.css b/src/Resources/public/view-more.css new file mode 100644 index 0000000..c9bc333 --- /dev/null +++ b/src/Resources/public/view-more.css @@ -0,0 +1,4 @@ +#searchbar .field .view-more { + color: rgba(0, 0, 0, 0.87); + cursor: pointer; +} diff --git a/src/Resources/public/view-more.js b/src/Resources/public/view-more.js new file mode 100644 index 0000000..b991750 --- /dev/null +++ b/src/Resources/public/view-more.js @@ -0,0 +1,25 @@ +$(document).ready(function() { + $(document).on( + 'click', + '#searchbarTextField .view-more', + function (event) { + event. preventDefault(); + + let linkEl = $(event.target), + form = linkEl.closest('form'), + choicesEl = $('#' + linkEl.data('for') + ' > .fields'); + + form.addClass('loading'); + + $.get( + linkEl.data('href'), + function( data ) { + choicesEl.replaceWith($(data.html).find('.fields')); + linkEl.hide(); + } + ).always(function() { + form.removeClass('loading'); + }); + } + ); +}); diff --git a/src/Resources/translations/messages.en.yaml b/src/Resources/translations/messages.en.yaml index b820acd..a4b4678 100644 --- a/src/Resources/translations/messages.en.yaml +++ b/src/Resources/translations/messages.en.yaml @@ -13,6 +13,7 @@ gally_sylius: header: Gally configuration filters: headline: Filters + view_more: View more form: active: Enable Gally product_index_batch_size: Product Indexing Batch Size diff --git a/src/Resources/views/Form/_checkbox.html.twig b/src/Resources/views/Form/_checkbox.html.twig index c3e5360..146dc19 100644 --- a/src/Resources/views/Form/_checkbox.html.twig +++ b/src/Resources/views/Form/_checkbox.html.twig @@ -3,5 +3,8 @@ {{- form_label(form) -}} {% set attr = attr|merge({'class': attr.class|default ~ ' ui'}) %} {{- form_widget(form, {'attr': attr}) -}} + {% if has_more_url %} + {{ 'gally_sylius.ui.filters.view_more'|trans }} + {% endif %} {%- endblock sylius_gally_filter_checkbox_row %} diff --git a/src/Resources/views/Product/Index/_filters.html.twig b/src/Resources/views/Product/Index/_filters.html.twig index 49f9c42..58eadb8 100644 --- a/src/Resources/views/Product/Index/_filters.html.twig +++ b/src/Resources/views/Product/Index/_filters.html.twig @@ -3,6 +3,10 @@
+ {# Keep the current search in the query on filtering #} + {% if products.parameters.get('criteria').search is defined %} + + {% endif %}