Menu

Lesson 7 : Create Search module

p5chi
Attachments
search.png (118384 bytes)

In this chapter the Search module will be described.

The project has two types of searching:

  • By selecting a Category
  • A keyword in the search input with the auto-complete plug-in that will search products, categories and users

Firstly it is necessary to create an Entity and a Repository for Search Statistics table in which will be stored keywords search frequency.

The Search controller:

//src/Xshare/ProductBundle/Controller/SearchController.php
/**
     *
     * @return Response
     * @Route("/search/{searchWord}", name="general_search", defaults={"searchWord"=""})
     * @Method({"GET"})
     */

    public function searchAction($searchWord){

        //breadcrumbs
        $breadcrumbs = $this->get("white_october_breadcrumbs");
        $breadcrumbs->addItem($this->get('translator')->trans('Home'), $this->get("router")->generate("xshare_general_default_index"));
        $breadcrumbs->addItem($this->get('translator')->trans('Search result'), null);

        $result = array();

        $search = trim($searchWord);

        if (strlen($search)) {

            $repository = $this->getDoctrine()
                    ->getEntityManager()
                    ->getRepository('XshareProductBundle:SearchStatistics');

            $result = $repository->getGeneralSearchResults(strtolower($search));

            //save statistics
            $repository->saveSerchRequest($search);
        }

        return $this->render('XshareProductBundle:Search:searchResult.html.twig', array(
            'result'   => $result,
        ));
    }

/**
     * 
     * @return Response
     * @Route("/search-autocomplete",
     * name="search_autocomplete",
     * defaults={"_locale"="ro"})
     * @Method({"GET"})
     */
    public function autocompleteAction(Request $request){

        $search = strtolower( trim($request->get('term') ));

        $result = array();

        if (strlen($search) > 2) {

            $result = $this->getDoctrine()
                    ->getEntityManager()
                    ->getRepository('XshareProductBundle:SearchStatistics')
                    ->getSuggestSearchResults($search);
        }

        $response = new Response(json_encode($result));
        $response->headers->set('Content-Type', 'application/json');

        return $response;
    }

the Repository functions will look like this :

//src/Xshare/ProductBundle/Repository/SearchStatisticsRepository.php
...
public function getGeneralSearchResults($search) {

        $result = array();

        //search within the product name
        $dql = 'SELECT p.product_id id, p.name, p.description FROM XshareProductBundle:Product p
                WHERE (p.name LIKE :search_word
                OR p.description LIKE :search_word)
                AND p.enable = 1';

        $query = $this->_em->createQuery($dql)
                    ->setParameter('search_word', '%' . $search . '%');

        $result['products'] = $query->getArrayResult();

        //search within the categories
        $dql = 'SELECT c.name, c.category_id id FROM XshareProductBundle:Category c
                WHERE c.name LIKE :search_word';

        $query = $this->_em->createQuery($dql)
                    ->setParameter('search_word', '%' . $search . '%');

        $result['categories'] = $query->getArrayResult();

        //search within users
        $dql = 'SELECT u.username, u.firstname, u.lastname, u.user_id id FROM XshareUserBundle:User u
                WHERE u.username LIKE :search_word';

        $query = $this->_em->createQuery($dql)
                    ->setParameter('search_word', '%' . $search . '%');

        $result['users'] = $query->getArrayResult();

        return $result;
    }
    /**
     * general search implementation
     *
     * @author Iuli Dercaci
     * @param string $search
     * @return array
     */
    public function getSuggestSearchResults($search) {

        $result = array();

        //search within the product name
        $dql = 'SELECT p.name FROM XshareProductBundle:Product p
                WHERE (p.name LIKE :search_word
                OR p.description LIKE :search_word)
                AND p.enable = 1';

        $query = $this->_em->createQuery($dql)
                    ->setParameter('search_word', '%' . $search . '%');

        foreach ($query->getArrayResult() as $value) {
            $result[] = $value['name'];
        }

        //search within the categories
        $dql = 'SELECT c.name FROM XshareProductBundle:Category c
                WHERE c.name LIKE :search_word';

        $query = $this->_em->createQuery($dql)
                    ->setParameter('search_word', '%' . $search . '%');

        foreach ($query->getArrayResult() as $value) {
            $result[] = $value['name'];
        }

        //search within users
        $dql = 'SELECT u.username FROM XshareUserBundle:User u
                WHERE u.username LIKE :search_word';

        $query = $this->_em->createQuery($dql)
                    ->setParameter('search_word', '%' . $search . '%');

        foreach ($query->getArrayResult() as $value) {
            $result[] = $value['name'];
        }

        return $result;
    }

    /**
     * saving the search word
     *
     * @param string $searchWord
     * @return boolean
     */
    public function saveSerchRequest($searchWord) {

            $statistics = new \Xshare\ProductBundle\Entity\SearchStatistics();
            $statistics->setKeywordSearch($searchWord);
            $this->_em->persist($statistics);
            $this->_em->flush();

            return true;
    }
...

The layouts for the search forms for category search and keyword search :

#//src/Xshare/ProductBundle/Resources/views/Category/categoryListDropBox.html.twig
#
#<div class="categrories-list-block">
#    <form action="{{ path('category_redirect') }}" method="get" {{ form_enctype(form) }} #id="categories-select">
#       {{ form_label(form.category,"Categories"|trans) }}
#        {{ form_widget(form.category) }}
#    </form>
#</div>
#
#<script type="text/javascript">
#   /* categories drop down list related  functionality */
#    $('#categories-select select').change(function(){
#        if ($('#categories-select select').val() > 0) {
#            $('#categories-select').submit();
#        }
#    });
#    
#</script&amp>

and the keyword search layout:

//src/Xshare/ProductBundle/Resources/views/search.html.twig

#<div class="ui-widget search-box">
#<form action="{{ path('general_search') }}" method="get" id="search-form" >
#    <input type="text" id="search-input" value="{{ "Search"|trans }}..." #name="search-keyword" />
#    <input class="search-button" type="submit" value="" />
#</form>
#</div>
{% render "XshareProductBundle:Category:categoriesList" %}

#<script type="text/javascript">

    $(function(){

        //remove default value
        $('#search-input').focus(function(evt){
            if($(this).val() == '{{"Search" | trans}}...')
                $(this).val('');
        });

        //add default value
        $('#search-input').blur(function(evt){
            if($(this).val() == '')
                $(this).val('{{"Search" | trans}}...');
        });

        //form submit functionality
        $('#search-form').submit(
            function(evt){
                evt.preventDefault();
                var searchVal = $.trim($('input[type="text"]', $(this)).val());
                if(searchVal.length > 1) {
                    window.location = $(this).attr('action') + '/' + searchVal;
                }
            }
        );

        //auto-complete
        var cache = {}, lastXhr;

        $( '#search-input' ).autocomplete({
            minLength: 3,
            source: function( request, response ) {
                var term = request.term;
                console.log(term);
                if ( term in cache ) {
                    response( cache[ term ] );
                    return;
                }

                lastXhr = $.getJSON( "{{ path('search_autocomplete') }}", request, function( data, status, xhr ) {
                    cache[ term ] = data;
                    if ( xhr === lastXhr ) {
                        response( data );
                    }
                });
            }
        });

    });

#</script>

The jQuery Auto-complete plugin documentation:
http://jqueryui.com/demos/autocomplete/

The layout that will display the search results :

//src/Xshare/ProductBundle/Resources/views/Search/searchResult.html.twig
{% extends 'XshareProductBundle::product.html.twig' %}

{% block title %}{{ "Search results"|trans }}{% endblock %}

{% block stylesheets %}
    {{ parent() }}
    <link rel="stylesheet" href="{{ asset('bundles/xshareproduct/css/search.css') }}" type="text/css" media="all" />
{% endblock %}

{% block maincontent %}

    <h2 class="search-header" >{{"Search results"|trans}}</h2>
    <div class="search-items-list">
            {% if result.products is not empty %}
            <dl class="search-entity-items" >
            {% for item in result.products %}
                <dt><a class="search-entity-name" href="{{ path('xshare_show_product', { pid: item.id }) }}">{{ item.name }}</a></dt>
                <dd>{{ item.description }}</dd>
            {% endfor %}
            </dl>
            {% endif %}

            {% if result.categories is not empty %}
            <ul class="search-entity-items">
            {% for item in result.categories %}
                <li><a class="search-entity-name" href="{{ path('product_category_show', { id: item.id }) }}">{{ item.name }}</a></li>
            {% endfor %}
            </ul>
            {% endif %}

            {% if result.users is not empty %}
            <ul class="search-entity-items">
            {% for item in result.users %}
                <li><a class="search-entity-name" href="{{ path('user_details', { id: item.id }) }}">{{ item.username }}</a> {{ item.firstname }} {{ item.lastname }}</li>

            {% endfor %}
            </ul>
            {% endif %}
            {% if result.products is empty and result.categories is empty and result.users is empty %}
                <p>{{ "There are no results"|trans }}</p>
            {% endif %}
    </div>
    <div class="clear"></div>
{% endblock%}

Search Image


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.