In this chapter the Search module will be described.
The project has two types of searching:
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&>
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%}