bestit/commercetools-filter-bundle

Commerce tools filter and search implementation

8.3.0 2018-03-26 13:39 UTC

README

Add filter and search implementation for commerce tools. It still uses the commmercetools/php-sdk under the hood.

Installation

Step 1: Download the Bundle

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

$ composer require bestit/commercetools-filter-bundle

This command requires you to have Composer installed globally, as explained in the installation chapter of the Composer documentation.

Step 2: Enable the Bundle

Then, enable the bundle by adding it to the list of registered bundles in the app/AppKernel.php file of your project:

<?php
// app/AppKernel.php

// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...

            new BestIt\Commercetools\FilterBundle\BestItCommercetoolsFilterBundle(),
        );

        // ...
    }

    // ...
}

Step 3: Configure the Bundle

Minimum configuration (only all required fields)

# Minimum configuration for "BestItCommercetoolsFilterBundle"
best_it_commercetools_filter:
    
    # Client service id of commerce tools sdk client
    client_id: app.commercetools.client
    
    # Sorting. At least one sorting options must exist
    sorting:
    
        # Default sorting to use if no sorting is selected
        default: name_asc
        
        # This is an array with all available sortings.
        choices:
            name_asc:
            
                # Translation key
                translation: sorting_name_asc
                
                # Api query for sdk
                query: 'name.de asc'

Maximum configuration (all required and optional fields)

# Maximum configuration for "BestItCommercetoolsFilterBundle"
best_it_commercetools_filter:

    # Used translation domain (default "messages")
    translation_domain:   messages

    # Product normalizer (service id / implement interface)
    product_normalizer_id: best_it_commercetools_filter.normalizer.empty_product_normalizer

    # Client service id of commerce tools sdk client
    client_id:            ~ # Required

    # Optional facets factory config provider (service id / implement interface)
    config_provider_id:   best_it_commercetools_filter.provider.empty_facet_config_provider

    # Generator for filter urls
    url_generator_id:     best_it_commercetools_filter.generator.default_filter_url_generator

    # Cache life time. Enum Attribute labels are cached to minimize CommerceTools requests.
    # DEPRECATED: Do not use. Switch to enum_normalizer
    cache_life_time:      86400
    
    # Optional query for defining a base category. If used, only products under the base category will be selected. (default: null)
    base_category_query: 'custom(fields(alias="_SHOP_ROOT"))'
 
    # Sorting. At least one sorting options must exist
    sorting:              # Required

        # The default sort
        default:              ~ # Required
        
        # Default Sorting for the listing pages
        default_listing:      ~ # optional, if not set fallback to default sorting
        
        # Default Sorting for the search page
        default_search:       ~ # optional, if not set fallback to default sorting

        # Define the sorting id, the translation key and sort query. This is an array with all available sortings.
        choices:              # Required

            # Prototype
            key:

                # Api query for sdk (default: null for relevance sorting) 
                query:                ~

                # Translation key
                translation:          ~ # Required

    # Pagination settings
    pagination:

        # Products per page
        products_per_page:    20

        # Neighbours at pagination 1 => "1 2 3" | 2 => "1 2 3 4 5"
        neighbours:           1

    # View settings
    view:

        # Default view type (eg. grid, list)
        default:              list

    # Facet config
    facet:

        # Translation key for reset button or false for disable reset button
        reset:                reset

        # Translation key for reset button or false for disable submit button
        submit:               submit
    
    # Suggest config
    suggest:
        
        # Use fuzzy suggest (default: true)
        enable_fuzzy:         false
        
        # The fuzziness level is quantified in terms of the Damerau-Levenshtein distance.
        # If null, the platform selects level based on the length of the searched text 
        # Default: null
        fuzzy_level:          2
        
        # Mark matching variants with "isMatchingVariant". (default: false)
        match_variants:       true
        
    # Search config
    search:
        
        # Use fuzzy search (default: true)
        enable_fuzzy:         false
             
        # The fuzziness level is quantified in terms of the Damerau-Levenshtein distance.
        # If null, the platform selects level based on the length of the searched text
        # Default: null
        fuzzy_level:          2
                   
        # Mark matching variants with "isMatchingVariant". (default: false)
        match_variants:       true
        
    # Enum Normalizer
    # CommerceTools only returns enum keys. We have to normalize it.
    # This bundle ships one standard way - but you can define your own normalizer as well
    enum_normalizer:
    
        # Switch this normalizer on / off (default: true)
        enable: false
        
        # You can define your own cache pool (default: see id below)
        cache_id: cache.app
        
        # Time in seconds (default: 86400)
        cache_life_time: 60
        
    # Category Normalizer
    # CommerceTools only returns category id's. We have to normalize it.
    # This bundle ships one standard way - but you can define your own normalizer as well
    category_normalizer:
    
        # Switch this normalizer on / off (default: true)
        enable: false
        
        # You can define your own cache pool (default: see id below)
        cache_id: cache.app
        
        # Time in seconds (default: 86400)
        cache_life_time: 60      
          
        # Optional facet filter mode for listing (default: 'none')
        # Possible values:
        #   - none: No filter will be applied
        #   - parent: Only direct childrens categories will be shown
        #   - ancestors: All childrens and subchildrens will be shown
        facet_filter_type: 'parent'

Usage

Listing

Just execute the listing method of the filter manager in your listing controller. It need the Symfony Request object and the current listing category (Commercetools Object) for creating the listing request.

Example:

// ListingController.php

    public function indexAction(Request $request, Category $category): array
    {
        $result = $this->get('best_it_commercetools_filter.manager.filter_manager')->listing($request, $category);
        
        return [
            'products' => $result->getProducts(),
            'totalProducts' => $result->getTotalProducts(),
            'context' => $result->getContext(),
            'pagination' => $result->getPagination(),
            'sorting' => $result->getSorting(),
            'facetForm' => $result->getForm()
        ];
    }

Search

Just execute the search method of the filter manager in your listing controller. It need the Symfony Request object and the search string for creating the search request.

Example:

// SearchController.php

    public function indexAction(Request $request, Category $category): array
    {
        $result = $this->get('best_it_commercetools_filter.manager.filter_manager')->search($request, $request->query->get('search'));
        
        return [
            'products' => $result->getProducts(),
            'totalProducts' => $result->getTotalProducts(),
            'context' => $result->getContext(),
            'pagination' => $result->getPagination(),
            'sorting' => $result->getSorting(),
            'facetForm' => $result->getForm()
        ];
    }

Product Normalizer

In most cases, products need to be normalized for the frontend. You can choose one of the base normalizer from filter bundle or use your own if you implement the ProductNormalizerInterface and add the service id to the app config. The filter bundle contains two base normalizer:

  • ArrayProductNormalizer: Converts the ProductProjection object to array.
  • EmptyProductNormalizer: Just return the ProductProjection without normalization

EmptyProductNormalizer will be use if you don't fill the product_normalizer_id Parameter (@ config.yml).

Term Normalizer

There are cases where you have to normalize facet terms. Commercetools only ships enum keys and categories id for example, which aren't enough for your frontend. This bundle contains two default normalizers:

  • CategoryNormalizer: Converts category id's to their real name
  • EnumAttributeNormalizer: Converts enum keys to their label

But you can define your own TermNormalizer as well. Just implement the TermNormalizerInterface and add the tag best_it_commercetools_filter.term_normalizer to your service. Remember to disable the default normalizer in your config.

If you want to skip a term, just throw a SkipTermException. No further normalizer will be applied and the term will not be shown at frontend.

Config Provider id

You can add you own filter config provider. Just implement the FacetConfigProviderInterface and add your service id to config_provider_id (@ config.yml). The filter bundle default provider will be use if you don't fill the config_provider_id Parameter (@ config.yml), which returns no filters.

Url generator

The filter bundle need to create urls, but the route names can vary between projects. So you can add your own url generator for creating the correct urls with the url_generator_id parameter. The generator need to implement the FilterUrlGeneratorInterface. The bundle will use his own default generator if you do not fill the field.

Events

Request events

You can modify the commercetools client request with the filter and suggest post events. Check all events @ SuggestEvent and FilterEvent class.

Example usage: Extend request

// FilterRequestSubscriber.php

class FilterRequestSubscriber implements EventSubscriberInterface
{
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            SuggestEvent::PRODUCTS_REQUEST_POST => 'onProductsFilterRequest',
            FilterEvent::PRODUCTS_REQUEST_POST => 'onProductsSuggestRequest'
        ];
    }

    /**
     * On products request for filter
     * @param ProductProjectionSearchRequestEvent $event
     */
    public function onProductsFilterRequest(ProductProjectionSearchRequestEvent $event)
    {
        $request = $event->getRequest();

        $request
            ->expand('masterVariant.attributes[*].value')
            ->expand('productType');

        $event->setRequest($request);
    }

    /**
     * On products request for suggest
     * @param ProductProjectionSearchRequestEvent $event
     */
    public function onProductsSuggestRequest(ProductProjectionSearchRequestEvent $event)
    {
        $request = $event->getRequest();

        $request
            ->expand('categories[*]')
            ->expand('masterVariant.attributes[*].value[*].value')
            ->expand('productType');

        $event->setRequest($request);
    }
}