2

I'm trying to create a "SmartSearch" extension for Magento 2.0, but I really can't find out what the next step should be. What I have so far:

  • Working 2.0 Extension (registration.php, module is enabled and recognized)
  • I override the default autocomplete function by using this code: enter image description here

With this code I can manipulate the default autocomplete text. This is my SearchDataProvider:

class SearchDataProvider extends \Magento\CatalogSearch\Model\Autocomplete\DataProvider {
    /**
     * {@inheritdoc}
     */
    public function getItems()
    {
        $collection = $this->getSuggestCollection();

        $query = $this->queryFactory->get()->getQueryText();
        $result = [];
        foreach ($collection as $item) {

            $resultItem = $this->itemFactory->create([
                'title' => $item->getQueryText(),
                'data'  => $item->getData(),
                'num_results' => $item->getNumResults(),
            ]);
            if ($resultItem->getTitle() == $query) {
                array_unshift($result, $resultItem);
            } else {
                $result[] = $resultItem;
            }
        }
        return $result;
    }

    /**
     * Retrieve suggest collection for query
     *
     * @return Collection
     */
    private function getSuggestCollection()
    {
        return $this->queryFactory->get()->getSuggestCollection();
    }
}

If i add something after 'title', it's shown in the autocomplete dropdown.

What I want to do now is to load another provider that will return a collection of products that contain the search query text.

I already found a FullText search model, but I cannot figure out how to properly use this.

Does anyone have experience with creating a search collection in Magento 2.0?

  • It is better to avoid extending core classes when possible to eliminate dependencies on protected interface, which might change in the future. It is better just to implement necessary interfaces. – Alex Paliarush Nov 18 '15 at 21:53
6
+50

To perform full text search Magento\Framework\Api\Search\SearchInterface should be used. Please note that Service Contracts concept was introduced in Magento 2, even though Service API is available not for all core functionality yet, it should be used whenever possible by 3-rd party modules instead of direct access to models and collections. To avoid breaking changes in the future (after 2.1, 2.2 etc releases), only classes/interfaces and methods marked with @api annotation should be used.

Below is fully working example which will display up to 5 matching products in suggest (title and price). To test just replace original \Magento\CatalogSearch\Model\Autocomplete\DataProvider content with this one:

<?php
namespace Magento\CatalogSearch\Model\Autocomplete;

use Magento\Search\Model\QueryFactory;
use Magento\Search\Model\Autocomplete\DataProviderInterface;
use Magento\Search\Model\Autocomplete\ItemFactory;
use Magento\Framework\Api\Search\SearchCriteriaFactory as FullTextSearchCriteriaFactory;
use Magento\Framework\Api\Search\SearchInterface as FullTextSearchApi;
use Magento\Framework\Api\Search\FilterGroupBuilder;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Catalog\Api\ProductRepositoryInterface;

/**
 * Full text search implementation of autocomplete.
 */
class DataProvider implements DataProviderInterface
{
    const PRODUCTS_NUMBER_IN_SUGGEST = 5;

    /** @var QueryFactory */
    protected $queryFactory;

    /** @var ItemFactory */
    protected $itemFactory;

    /** @var \Magento\Framework\Api\Search\SearchInterface */
    protected $fullTextSearchApi;

    /** @var FullTextSearchCriteriaFactory */
    protected $fullTextSearchCriteriaFactory;

    /** @var FilterGroupBuilder */
    protected $searchFilterGroupBuilder;

    /** @var FilterBuilder */
    protected $filterBuilder;

    /** @var ProductRepositoryInterface */
    protected $productRepository;

    /** @var SearchCriteriaBuilder */
    protected $searchCriteriaBuilder;

    /**
     * Initialize dependencies.
     *
     * @param QueryFactory $queryFactory
     * @param ItemFactory $itemFactory
     * @param FullTextSearchApi $search
     * @param FullTextSearchCriteriaFactory $searchCriteriaFactory
     * @param FilterGroupBuilder $searchFilterGroupBuilder
     * @param FilterBuilder $filterBuilder
     * @param ProductRepositoryInterface $productRepository
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     */
    public function __construct(
        QueryFactory $queryFactory,
        ItemFactory $itemFactory,
        FullTextSearchApi $search,
        FullTextSearchCriteriaFactory $searchCriteriaFactory,
        FilterGroupBuilder $searchFilterGroupBuilder,
        FilterBuilder $filterBuilder,
        ProductRepositoryInterface $productRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder
    ) {
        $this->queryFactory = $queryFactory;
        $this->itemFactory = $itemFactory;
        $this->fullTextSearchApi = $search;
        $this->fullTextSearchCriteriaFactory = $searchCriteriaFactory;
        $this->filterBuilder = $filterBuilder;
        $this->searchFilterGroupBuilder = $searchFilterGroupBuilder;
        $this->productRepository = $productRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
    }

    /**
     * {@inheritdoc}
     */
    public function getItems()
    {
        $result = [];
        $query = $this->queryFactory->get()->getQueryText();
        $productIds = $this->searchProductsFullText($query);
        if ($productIds) {
            $searchCriteria = $this->searchCriteriaBuilder->addFilter('entity_id', $productIds, 'in')->create();
            $products = $this->productRepository->getList($searchCriteria);
            foreach ($products->getItems() as $product) {
                $resultItem = $this->itemFactory->create([
                    /** Feel free to add here necessary product data and then render in template */
                    'title' => $product->getName() . " " . $product->getPrice()
                ]);
                $result[] = $resultItem;
            }
        }
        return $result;
    }

    /**
     * Perform full text search and find IDs of matching products.
     *
     * @param string
     * @return int[]
     */
    private function searchProductsFullText($query)
    {
        $searchCriteria = $this->fullTextSearchCriteriaFactory->create();
        /** To get list of available request names see Magento/CatalogSearch/etc/search_request.xml */
        $searchCriteria->setRequestName('quick_search_container');
        $filter = $this->filterBuilder->setField('search_term')->setValue($query)->setConditionType('like')->create();
        $filterGroup = $this->searchFilterGroupBuilder->addFilter($filter)->create();
        $currentPage = 1;
        $searchCriteria->setFilterGroups([$filterGroup])
            ->setCurrentPage($currentPage)
            ->setPageSize(self::PRODUCTS_NUMBER_IN_SUGGEST);
        $searchResults = $this->fullTextSearchApi->search($searchCriteria);
        $productIds = [];
        /**
         * Full text search returns document IDs (in this case product IDs),
         * so to get products information we need to load them using filtration by these IDs
         */
        foreach ($searchResults->getItems() as $searchDocument) {
            $productIds[] = $searchDocument->getId();
        }
        return $productIds;
    }
}
  • 1
    Bingo! We've created a # of interfaces for search functionality to make it easy to extend search in Magento 2. All you should have to do is implement the appropriate interface(s). If you see methods or missing interfaces for search please let us know. – Chuck Nov 18 '15 at 22:11
  • Thank you for the clear explanation and the example code. The SmartSearch started working immediately after implementing your code. You're awesome. – Silvan Nov 19 '15 at 9:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.