Jump to content

Categories sorting in the faceted search filter


Tomason

Recommended Posts

Hello,

can anyone help me out.....I need to:

1/ Get sorting of the categories in the faceted search filter by ID. At this moment I assume the sorting is default by NAME.

2/ I need to set up the new category ID for some already existing categories. Should I rename the ID of existing category in the database or is there any other way? 

In advance thanks for your feedback.

Subcategories sorting faceted filter.png

Link to comment
Share on other sites

  • 1 year later...
  • 1 year later...
On 10/21/2021 at 12:43 PM, 4you.software said:

It is not possible to sort by category position without major intervention in the module.
Unable to advise or give modified code.
It is an individual adjustment.
The faceted search module is impractical from the ground up.

Hi, I've get to sort categories by position modifing Block and Converter classes, but I can not get the way to override them. Have you the way to do it? Thank you very much

Link to comment
Share on other sites

Hi, I've got a solution overriding ProductListingFrontController, adding this code before renderfacets().

Hope it helps somebody, I spend few hours before I got it

       //Create new facetCollection to override original one
        $newFacetCollection = new FacetCollection();

        //Iterate facet collection looking for category
        $facetCollection = $result->getFacetCollection()->getFacets();
        foreach($facetCollection as $facet){
            if($facet->getType() == 'category'){

                $undOrderedFacets = $facet->getFilters();
                foreach($undOrderedFacets as $index => $value){
                    $category = new Category($value->getValue());
                    $value->position = $category->position;
                }

                //sort set of facets
                usort($undOrderedFacets, [$this, 'sortFiltersByPosition']);
                
                //sorted facets
                $orderedFacets = $undOrderedFacets;

                //create new facet to pas to collection
                $newFacet = new Facet();
                $newFacet->setLabel($facet->getLabel());
                $newFacet->setType($facet->getType());

                foreach($orderedFacets as $filter) {
                    $newFacet->addFilter($filter);
                }

                //Category facet has only 1 property
                $facetProperties = [
                    'filter_show_limit'=> $facet->getProperty('filter_show_limit')
                ];
                
                $newFacet->setProperty('filter_show_limit', $facetProperties['filter_show_limit']);
                
                $newFacet->setMultipleSelectionAllowed($facet->isMultipleSelectionAllowed());
                $newFacet->setWidgetType($facet->getWidgetType());

                $newFacetCollection->addFacet($newFacet);
            }
            else{
                $newFacetCollection->addFacet($facet);
            }
        }
        //Set new factocollection to result to pass to render
        $result->setFacetCollection($newFacetCollection);

 

  • Like 1
Link to comment
Share on other sites

On 11/17/2022 at 10:56 AM, ivo studiogenesis said:

Hi, I've got a solution overriding ProductListingFrontController, adding this code before renderfacets().

Hope it helps somebody, I spend few hours before I got it

       //Create new facetCollection to override original one
        $newFacetCollection = new FacetCollection();

        //Iterate facet collection looking for category
        $facetCollection = $result->getFacetCollection()->getFacets();
        foreach($facetCollection as $facet){
            if($facet->getType() == 'category'){

                $undOrderedFacets = $facet->getFilters();
                foreach($undOrderedFacets as $index => $value){
                    $category = new Category($value->getValue());
                    $value->position = $category->position;
                }

                //sort set of facets
                usort($undOrderedFacets, [$this, 'sortFiltersByPosition']);
                
                //sorted facets
                $orderedFacets = $undOrderedFacets;

                //create new facet to pas to collection
                $newFacet = new Facet();
                $newFacet->setLabel($facet->getLabel());
                $newFacet->setType($facet->getType());

                foreach($orderedFacets as $filter) {
                    $newFacet->addFilter($filter);
                }

                //Category facet has only 1 property
                $facetProperties = [
                    'filter_show_limit'=> $facet->getProperty('filter_show_limit')
                ];
                
                $newFacet->setProperty('filter_show_limit', $facetProperties['filter_show_limit']);
                
                $newFacet->setMultipleSelectionAllowed($facet->isMultipleSelectionAllowed());
                $newFacet->setWidgetType($facet->getWidgetType());

                $newFacetCollection->addFacet($newFacet);
            }
            else{
                $newFacetCollection->addFacet($facet);
            }
        }
        //Set new factocollection to result to pass to render
        $result->setFacetCollection($newFacetCollection);

 

Hi @ivo studiogenesis,

Thank you for your solution ! It seems to be exactly what I need (sort sub-category facets by position and not by name).

But I am novice in Prestashop (and php)... I try to insert your code in classes/controller/ProductListingFrontController.php and I don't see any change.

I insert code at the beginnig of protected function renderFacets(ProductSearchResult $result) {

Maybe it is wrong ? 

Can you help me please ? 

Thank you for your help

Edited by LiliW (see edit history)
Link to comment
Share on other sites

On 11/17/2022 at 10:56 AM, ivo studiogenesis said:

Hi, I've got a solution overriding ProductListingFrontController, adding this code before renderfacets().

Hope it helps somebody, I spend few hours before I got it

       //Create new facetCollection to override original one
        $newFacetCollection = new FacetCollection();

        //Iterate facet collection looking for category
        $facetCollection = $result->getFacetCollection()->getFacets();
        foreach($facetCollection as $facet){
            if($facet->getType() == 'category'){

                $undOrderedFacets = $facet->getFilters();
                foreach($undOrderedFacets as $index => $value){
                    $category = new Category($value->getValue());
                    $value->position = $category->position;
                }

                //sort set of facets
                usort($undOrderedFacets, [$this, 'sortFiltersByPosition']);
                
                //sorted facets
                $orderedFacets = $undOrderedFacets;

                //create new facet to pas to collection
                $newFacet = new Facet();
                $newFacet->setLabel($facet->getLabel());
                $newFacet->setType($facet->getType());

                foreach($orderedFacets as $filter) {
                    $newFacet->addFilter($filter);
                }

                //Category facet has only 1 property
                $facetProperties = [
                    'filter_show_limit'=> $facet->getProperty('filter_show_limit')
                ];
                
                $newFacet->setProperty('filter_show_limit', $facetProperties['filter_show_limit']);
                
                $newFacet->setMultipleSelectionAllowed($facet->isMultipleSelectionAllowed());
                $newFacet->setWidgetType($facet->getWidgetType());

                $newFacetCollection->addFacet($newFacet);
            }
            else{
                $newFacetCollection->addFacet($facet);
            }
        }
        //Set new factocollection to result to pass to render
        $result->setFacetCollection($newFacetCollection);

 

Another option would be to modify the  renderFacets function in

/modules/ps_facetedsearch/src/Product/SearchProvider.php

 

Adding something like

https://notepad.pw/code/TghAByeLU9YMywPWbMnw

Link to comment
Share on other sites

Hi @ventura,

thank you for you help.

unfortunately, I don't know  where to insert this code... 

I try

public function renderFacets(ProductSearchContext $context, ProductSearchResult $result)
    {
        list($activeFilters, $displayedFacets, $facetsVar) = $this->prepareActiveFiltersForRender($context, $result);

        // No need to render without facets
        if (empty($facetsVar)) {
            return '';
        }

        $this->module->getContext()->smarty->assign(
            [
                'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'),
                'facets' => $facetsVar,
                'js_enabled' => $this->module->isAjax(),
                'displayedFacets' => $displayedFacets,
                'activeFilters' => $activeFilters,
                'sort_order' => $result->getCurrentSortOrder()->toString(),
                'clear_all_link' => $this->updateQueryString(
                    [
                        'q' => null,
                        'page' => null,
                    ]
                ),
            ]
        );
		
		if ($displayedFacets[0]['type'] == 'category') {
        foreach ($displayedFacets[0]['filters'] as $key => &$facet) {
            $categories = Category::searchByName($idLang, $facet['label'], true);
			$facet['position']=  $categories['position'];
            }
        array_multisort(array_column($displayedFacets[0]['filters'], 'position'), SORT_ASC, $displayedFacets[0]['filters']);
        }

        return $this->module->fetch(
            'module:ps_facetedsearch/views/templates/front/catalog/facets.tpl'
        );
		

    }

==> ERROR  : Attempted to load class "Category" from namespace "PrestaShop\Module\FacetedSearch\Product".

 

 

Link to comment
Share on other sites

I am so sorry... I never modify php file... :(

I added 

use Category;

Thank you !

But I had the error ==> Notice: Undefined variable: idLang

So I changed like this : 

 $categories = Category::searchByName($idLang, $facet['label'], true);
==>
 $categories = Category::searchByName($facet['label'], true);

Now I have a new error ==> Notice: Undefined index: position

 

Edited by LiliW (see edit history)
Link to comment
Share on other sites

It is exactly what I want...

I use Prestashop v1.7.8.6 and Facets v3.8.0.

I have this error : ContextErrorException Notice: Undefined property: PrestaShop\PrestaShop\Core\Product\Search\ProductSearchContext::$shop

My  Search.Provider.php file  

<?php
/**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License 3.0 (AFL-3.0)
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/AFL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to [email protected] so we can send you a copy immediately.
 *
 * @author    PrestaShop SA <[email protected]>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
 */

namespace PrestaShop\Module\FacetedSearch\Product;

use Configuration;
use PrestaShop\Module\FacetedSearch\Filters;
use PrestaShop\Module\FacetedSearch\URLSerializer;
use PrestaShop\PrestaShop\Core\Product\Search\Facet;
use PrestaShop\PrestaShop\Core\Product\Search\FacetCollection;
use PrestaShop\PrestaShop\Core\Product\Search\FacetsRendererInterface;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchContext;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchProviderInterface;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchResult;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
use Ps_Facetedsearch;
use Tools;
use Category;

class SearchProvider implements FacetsRendererInterface, ProductSearchProviderInterface
{
    /**
     * @var Ps_Facetedsearch
     */
    private $module;

    /**
     * @var Filters\Converter
     */
    private $filtersConverter;

    /**
     * @var Filters\DataAccessor
     */
    private $dataAccessor;

    /**
     * @var URLSerializer
     */
    private $urlSerializer;

    /**
     * @var SearchFactory
     */
    private $searchFactory;

    public function __construct(
        Ps_Facetedsearch $module,
        Filters\Converter $converter,
        URLSerializer $serializer,
        Filters\DataAccessor $dataAccessor,
        SearchFactory $searchFactory = null
    ) {
        $this->module = $module;
        $this->filtersConverter = $converter;
        $this->urlSerializer = $serializer;
        $this->dataAccessor = $dataAccessor;
        $this->searchFactory = $searchFactory === null ? new SearchFactory() : $searchFactory;
    }

    /**
     * @return array
     */
    private function getAvailableSortOrders()
    {
        $sortSalesDesc = new SortOrder('product', 'sales', 'desc');
        $sortPosAsc = new SortOrder('product', 'position', 'asc');
        $sortNameAsc = new SortOrder('product', 'name', 'asc');
        $sortNameDesc = new SortOrder('product', 'name', 'desc');
        $sortPriceAsc = new SortOrder('product', 'price', 'asc');
        $sortPriceDesc = new SortOrder('product', 'price', 'desc');
        $translator = $this->module->getTranslator();

        return [
            $sortSalesDesc->setLabel(
                $translator->trans('Best sellers', [], 'Modules.Facetedsearch.Shop')
            ),
            $sortPosAsc->setLabel(
                $translator->trans('Relevance', [], 'Modules.Facetedsearch.Shop')
            ),
            $sortNameAsc->setLabel(
                $translator->trans('Name, A to Z', [], 'Shop.Theme.Catalog')
            ),
            $sortNameDesc->setLabel(
                $translator->trans('Name, Z to A', [], 'Shop.Theme.Catalog')
            ),
            $sortPriceAsc->setLabel(
                $translator->trans('Price, low to high', [], 'Shop.Theme.Catalog')
            ),
            $sortPriceDesc->setLabel(
                $translator->trans('Price, high to low', [], 'Shop.Theme.Catalog')
            ),
        ];
    }

    /**
     * @param ProductSearchContext $context
     * @param ProductSearchQuery $query
     *
     * @return ProductSearchResult
     */
    public function runQuery(
        ProductSearchContext $context,
        ProductSearchQuery $query
    ) {
        $result = new ProductSearchResult();
        // extract the filter array from the Search query
        $facetedSearchFilters = $this->filtersConverter->createFacetedSearchFiltersFromQuery($query);

        $context = $this->module->getContext();
        $facetedSearch = $this->searchFactory->build($context);
        // init the search with the initial population associated with the current filters
        $facetedSearch->initSearch($facetedSearchFilters);

        $orderBy = $query->getSortOrder()->toLegacyOrderBy(false);
        $orderWay = $query->getSortOrder()->toLegacyOrderWay();

        $filterProductSearch = new Filters\Products($facetedSearch);

        // get the product associated with the current filter
        $productsAndCount = $filterProductSearch->getProductByFilters(
            $query->getResultsPerPage(),
            $query->getPage(),
            $orderBy,
            $orderWay,
            $facetedSearchFilters
        );

        $result
            ->setProducts($productsAndCount['products'])
            ->setTotalProductsCount($productsAndCount['count'])
            ->setAvailableSortOrders($this->getAvailableSortOrders());

        // now get the filter blocks associated with the current search
        $filterBlockSearch = new Filters\Block(
            $facetedSearch->getSearchAdapter(),
            $context,
            $this->module->getDatabase(),
            $this->dataAccessor
        );

        $idShop = (int) $context->shop->id;
        $idLang = (int) $context->language->id;
        $idCurrency = (int) $context->currency->id;
        $idCountry = (int) $context->country->id;
        $idCategory = (int) $query->getIdCategory();

        $filterHash = md5(
            sprintf(
                '%d-%d-%d-%d-%d-%s',
                $idShop,
                $idCurrency,
                $idLang,
                $idCategory,
                $idCountry,
                serialize($facetedSearchFilters)
            )
        );

        $filterBlock = $filterBlockSearch->getFromCache($filterHash);
        if (empty($filterBlock)) {
            $filterBlock = $filterBlockSearch->getFilterBlock($productsAndCount['count'], $facetedSearchFilters);
            $filterBlockSearch->insertIntoCache($filterHash, $filterBlock);
        }

        $facets = $this->filtersConverter->getFacetsFromFilterBlocks(
            $filterBlock['filters']
        );

        $this->labelRangeFilters($facets);
        $this->addEncodedFacetsToFilters($facets);
        $this->hideUselessFacets($facets, (int) $result->getTotalProductsCount());

        $facetCollection = new FacetCollection();
        $nextMenu = $facetCollection->setFacets($facets);
        $result->setFacetCollection($nextMenu);

        $facetFilters = $this->urlSerializer->getActiveFacetFiltersFromFacets($facets);
        $result->setEncodedFacets($this->urlSerializer->serialize($facetFilters));

        return $result;
    }

    /**
     * Renders an product search result.
     *
     * @param ProductSearchContext $context
     * @param ProductSearchResult $result
     *
     * @return string the HTML of the facets
     */
	 
	 public function renderFacets(ProductSearchContext $context, ProductSearchResult $result)
    {
		
        $idShop = (int) $context->shop->id;
        $idLang = (int) $context->language->id;
		
        list($activeFilters, $displayedFacets, $facetsVar) = $this->prepareActiveFiltersForRender($context, $result);

        // No need to render without facets
        if (empty($facetsVar)) {
            return '';
        }
        // Sort by position in category
        if ($displayedFacets[0]['type'] == 'category') {
        foreach ($displayedFacets[0]['filters'] as $key => &$facet) {
            $categories = Category::searchByName($idLang, $facet['label'], true);
            $facet['position']=  $categories['position'];
            }
        array_multisort(array_column($displayedFacets[0]['filters'], 'position'), SORT_ASC, $displayedFacets[0]['filters']);
        }

        $this->module->getContext()->smarty->assign(
            [
                'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'),
                'facets' => $facetsVar,
                'js_enabled' => $this->module->isAjax(),
                'displayedFacets' => $displayedFacets,
                'activeFilters' => $activeFilters,
                'sort_order' => $result->getCurrentSortOrder()->toString(),
                'clear_all_link' => $this->updateQueryString(
                    [
                        'q' => null,
                        'page' => null,
                    ]
                ),
            ]
        );

        return $this->module->fetch(
            'module:ps_facetedsearch/views/templates/front/catalog/facets.tpl'
        );
    }

    /**
     * Renders an product search result of active filters.
     *
     * @param ProductSearchContext $context
     * @param ProductSearchResult $result
     *
     * @return string the HTML of the facets
     */
    public function renderActiveFilters(ProductSearchContext $context, ProductSearchResult $result)
    {
        list($activeFilters) = $this->prepareActiveFiltersForRender($context, $result);

        $this->module->getContext()->smarty->assign(
            [
                'activeFilters' => $activeFilters,
                'clear_all_link' => $this->updateQueryString(
                    [
                        'q' => null,
                        'page' => null,
                    ]
                ),
            ]
        );

        return $this->module->fetch(
            'module:ps_facetedsearch/views/templates/front/catalog/active-filters.tpl'
        );
    }

    /**
     * Prepare active filters for renderer.
     *
     * @param ProductSearchContext $context
     * @param ProductSearchResult $result
     *
     * @return array|null
     */
    private function prepareActiveFiltersForRender(ProductSearchContext $context, ProductSearchResult $result)
    {
        $facetCollection = $result->getFacetCollection();

        // not all search providers generate menus
        if (empty($facetCollection)) {
            return null;
        }

        $facetsVar = array_map(
            [$this, 'prepareFacetForTemplate'],
            $facetCollection->getFacets()
        );

        $displayedFacets = [];
        $activeFilters = [];
        foreach ($facetsVar as $idx => $facet) {
            // Remove undisplayed facets
            if (!empty($facet['displayed'])) {
                $displayedFacets[] = $facet;
            }

            // Check if a filter is active
            foreach ($facet['filters'] as $filter) {
                if ($filter['active']) {
                    $activeFilters[] = $filter;
                }
            }
        }

        return [
            $activeFilters,
            $displayedFacets,
            $facetsVar,
        ];
    }

    /**
     * Converts a Facet to an array with all necessary
     * information for templating.
     *
     * @param Facet $facet
     *
     * @return array ready for templating
     */
    protected function prepareFacetForTemplate(Facet $facet)
    {
        $facetsArray = $facet->toArray();
        foreach ($facetsArray['filters'] as &$filter) {
            $filter['facetLabel'] = $facet->getLabel();
            if ($filter['nextEncodedFacets'] || $facet->getWidgetType() === 'slider') {
                $filter['nextEncodedFacetsURL'] = $this->updateQueryString([
                    'q' => $filter['nextEncodedFacets'],
                    'page' => null,
                ]);
            } else {
                $filter['nextEncodedFacetsURL'] = $this->updateQueryString([
                    'q' => null,
                ]);
            }
        }
        unset($filter);

        return $facetsArray;
    }

    /**
     * Add a label associated with the facets
     *
     * @param array $facets
     */
    private function labelRangeFilters(array $facets)
    {
        $context = $this->module->getContext();

        foreach ($facets as $facet) {
            if (!in_array($facet->getType(), Filters\Converter::RANGE_FILTERS)) {
                continue;
            }

            foreach ($facet->getFilters() as $filter) {
                $filterValue = $filter->getValue();
                $min = empty($filterValue[0]) ? $facet->getProperty('min') : $filterValue[0];
                $max = empty($filterValue[1]) ? $facet->getProperty('max') : $filterValue[1];
                if ($facet->getType() === 'weight') {
                    $unit = Configuration::get('PS_WEIGHT_UNIT');
                    $filter->setLabel(
                        sprintf(
                            '%1$s%2$s - %3$s%4$s',
                            Tools::displayNumber($min),
                            $unit,
                            Tools::displayNumber($max),
                            $unit
                        )
                    );
                } elseif ($facet->getType() === 'price') {
                    $filter->setLabel(
                        sprintf(
                            '%1$s - %2$s',
                            $context->getCurrentLocale()->formatPrice($min, $context->currency->iso_code),
                            $context->getCurrentLocale()->formatPrice($max, $context->currency->iso_code)
                        )
                    );
                }
            }
        }
    }

    /**
     * This method generates a URL stub for each filter inside the given facets
     * and assigns this stub to the filters.
     * The URL stub is called 'nextEncodedFacets' because it is used
     * to generate the URL of the search once a filter is activated.
     */
    private function addEncodedFacetsToFilters(array $facets)
    {
        // first get the currently active facetFilter in an array
        $originalFacetFilters = $this->urlSerializer->getActiveFacetFiltersFromFacets($facets);

        foreach ($facets as $facet) {
            $activeFacetFilters = $originalFacetFilters;
            // If only one filter can be selected, we keep track of
            // the current active filter to disable it before generating the url stub
            // and not select two filters in a facet that can have only one active filter.
            if (!$facet->isMultipleSelectionAllowed() && !$facet->getProperty('range')) {
                foreach ($facet->getFilters() as $filter) {
                    if ($filter->isActive()) {
                        // we have a currently active filter is the facet, remove it from the facetFilter array
                        $activeFacetFilters = $this->urlSerializer->removeFilterFromFacetFilters(
                            $originalFacetFilters,
                            $filter,
                            $facet
                        );
                        break;
                    }
                }
            }

            foreach ($facet->getFilters() as $filter) {
                // toggle the current filter
                if ($filter->isActive() || $facet->getProperty('range')) {
                    $facetFilters = $this->urlSerializer->removeFilterFromFacetFilters(
                        $activeFacetFilters,
                        $filter,
                        $facet
                    );
                } else {
                    $facetFilters = $this->urlSerializer->addFilterToFacetFilters(
                        $activeFacetFilters,
                        $filter,
                        $facet
                    );
                }

                // We've toggled the filter, so the call to serialize
                // returns the "URL" for the search when user has toggled
                // the filter.
                $filter->setNextEncodedFacets(
                    $this->urlSerializer->serialize($facetFilters)
                );
            }
        }
    }

    /**
     * Remove the facet when there's only 1 result.
     * Keep facet status when it's a slider
     *
     * @param array $facets
     * @param int $totalProducts
     */
    private function hideUselessFacets(array $facets, $totalProducts)
    {
        foreach ($facets as $facet) {
            if ($facet->getWidgetType() === 'slider') {
                $facet->setDisplayed(
                    $facet->getProperty('min') != $facet->getProperty('max')
                );
                continue;
            }

            $totalFacetProducts = 0;
            $usefulFiltersCount = 0;
            foreach ($facet->getFilters() as $filter) {
                if ($filter->getMagnitude() > 0 && $filter->isDisplayed()) {
                    $totalFacetProducts += $filter->getMagnitude();
                    ++$usefulFiltersCount;
                }
            }

            $facet->setDisplayed(
                // There are two filters displayed
                $usefulFiltersCount > 1
                ||
                /*
                 * There is only one fitler and the
                 * magnitude is different than the
                 * total products
                 */
                (
                    count($facet->getFilters()) === 1
                    && $totalFacetProducts < $totalProducts
                    && $usefulFiltersCount > 0
                )
            );
        }
    }

    /**
     * Generate a URL corresponding to the current page but
     * with the query string altered.
     *
     * Params from $extraParams that have a null value are stripped,
     * and other params are added. Params not in $extraParams are unchanged.
     */
    private function updateQueryString(array $extraParams = [])
    {
        $uriWithoutParams = explode('?', $_SERVER['REQUEST_URI'])[0];
        $url = Tools::getCurrentUrlProtocolPrefix() . $_SERVER['HTTP_HOST'] . $uriWithoutParams;
        $params = [];
        $paramsFromUri = '';
        if (strpos($_SERVER['REQUEST_URI'], '?') !== false) {
            $paramsFromUri = explode('?', $_SERVER['REQUEST_URI'])[1];
        }
        parse_str($paramsFromUri, $params);

        foreach ($extraParams as $key => $value) {
            if (null === $value) {
                // Force clear param if null value is passed
                unset($params[$key]);
            } else {
                $params[$key] = $value;
            }
        }

        foreach ($params as $key => $param) {
            if (null === $param || '' === $param) {
                unset($params[$key]);
            }
        }

        $queryString = str_replace('%2F', '/', http_build_query($params, '', '&'));

        return $url . ($queryString ? "?$queryString" : '');
    }
}

Any idea ? 

Edited by LiliW (see edit history)
Link to comment
Share on other sites

  • 4 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...