Reputation: 88
What would be the best way to build a search form that i would render in my base twig template so it could be always visible?
I created a form type class according to my needs and can use it in a controller and then render the form on a template but it makes no sense to do the same for the other controllers and templates so i wrote a service:
<?php
namespace MyBundle\Services;
use Symfony\Component\Form\FormFactory;
use MyBundle\Form\FulltextSearchType;
class FulltextSearch {
private $formFactory;
public function __construct(FormFactory $formFactory) {
$this->formFactory = $formFactory;
}
public function mySearch() {
$form = $this->formFactory->createBuilder('MyBundle\Form\FulltextSearchType');
return $form->getForm()->createView();
//return $form->createView();
}
}
i also defined the service in the services.yml and config.yml and i render that in my template:
<form action="{{ path('my_search') }}" method="post">
{{ form_widget(fulltextSearch.mySearch) }}
</form>
It works, i get the form, i get the request after form submission but i doubt that this is the best way to achieve what my question title says
Upvotes: 3
Views: 4443
Reputation: 9585
This could be achieved injecting a service into all templates as global variable:
First, define your service with its dependencies:
services:
app.fulltextsearch:
class: MyBundle\Services\FulltextSearch
arguments: ['@form.factory']
Second, to define this service as a global Twig variable:
# app/config/config.yml
twig:
# ...
globals:
fulltextSearch: '@app.fulltextsearch'
That's it!
Whenever the global variable is accessed in the template, the service will be requested from the service container and you get access to that object.
On the other hand, I suggest you create the whole form within FulltextSearch::mySearch
method for major reusability:
class FulltextSearch
{
private $router;
//...
public function mySearch() {
$form = $this->formFactory->createBuilder()
->setAction($this->router->generate('my_search'))
->add('search', FulltextSearch::class)
->getForm();
return $form->createView();
}
}
Note: Make sure to inject the @router
service in app.fulltextsearch
definition as well as '@form.factory'
.
Next, you base search template looks like this, (no needed <form>
tag, action
and method
attributes, this last by default is POST
):
{{ form(fulltextSearch.mySearch) }}
Upvotes: 2
Reputation: 63
You also can use your search with ajax function, like this you can call a controller who return only answer in dynamical Div or something else in html, and you can pre-make a search with auto completion, and add to the 'form' of your search the second one who is launched on submit.
INFOS : for this you can replace your FormType.php by a hand writting form for more flexibility (personnal preference) cause is not a bug form, it's just one input.
FOR AJAX SEARCH : Controller.php
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use YourBundle\Entity\Yourentity;
class SearchController extends Controller
{
public function ajaxAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$string = $this->getRequest()->request->get('recherche');
$limit = 3;
$listsearch = $em->getRepository('YourBundle:Yourentity')->myFind($string, $limit);
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$jsonContent = $serializer->serialize($listsearch, 'json');
$response = new Response($jsonContent);
return $response;
}
}
Repository of your entity for define your own find method :
<?php
namespace YourBundle\Repository;
/**
* YourentityRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class YourentityRepository extends \Doctrine\ORM\EntityRepository
{
public function myFind($string, $limit)
{
return $this->getEntityManager()->createQuery('SELECT a FROM YourBundle:Yourentity a
WHERE a.onedata(name, id...) LIKE :string OR a.anotherdata LIKE :string')
->setParameter('string','%'.$string.'%')
->setMaxResults($limit)
->getResult();
}
}
view.html.twig + script
<form method="POST" action="" class="navbar-form navbar-left inline-form">
<input id="search" type="text" name="recherche" class="input-sm form-control">
<input type="submit" value="Search" class="btn-primary btn">
<div id="sresult" class="search-result">
</div>
</form>
<script>
$('#search').keyup(function() {
recherche = $(this).val();
$.ajax({
type: "POST",
url: "{{ path('search') }}",
dataType: "json",
data: {recherche : recherche},
success : function(response) {
document.getElementById("sresult").innerHTML = "";
if(response.length === 1){
var elmt = document.getElementById("sresult");
elmt.style.display = "block";
var result = response[0];
document.getElementById("sresult").innerHTML = "<div class=resultat><p> Title : "+result.title+"<br/>oneData : "+result.data+"<br/>Gender : "+result.gender+"</p><br/></div>";
}
else {
for(var i =0;i <= response.length-1;i++) {
var elmt = document.getElementById("sresult");
elmt.style.display = "block";
var result = response[i];
document.getElementById("sresult").innerHTML += "<div class=resultat><p> Title : "+result.title+"<br/>Data : "+result.data+"<br/>Gender : "+result.gender+"</p><br/></div>";
}
}
}
});
});
</script>
(It's an exempla for the result apply with the entity that you use)
for classical search :
Controller.php :
public function searchAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$user = $this->container->get('security.context')->getToken()->getUser();
$tabsearch = [];
$search = $request->request->get('recherche');
if ($search == null) {
$searchresult = $em->getRepository('AppBundle:User')->findAll();
}
else {
$searchresult = $em->getRepository('AppBundle:User')->findByUsername($search);
}
foreach ($searchresult as $datasearch)
{
$thisGender = $em->getRepository('AppBundle:datauser')->findOneByIduser($datasearch->getId());
$tabsearch[] = array(
'gender' => $thisGender->getGenre(),
'username' => $datasearch->getUsername(),
'lastlogin' => $datasearch->getLastlogin(),
'id' => $datasearch->getId(),
);
}
return $this->render('default/search.html.twig', array(
'tabsearchs' => $tabsearch,
));
}
For the view it's the same that with AJAX so you can apply the two method on the same input for your search bar,i have take this from one of my project so just modify for apply with your own project (entity and data). The search.html.twig for result is defined by you, use your tabsearch with a twig for loop ( {% for search in tabsearch %} ... ... )
The route are like this :
the classical way :
classical_search:
path: /result/search
defaults: { _controller: YourBundle:Default:search }
Ajax way :
ajax_search:
path: /search
defaults: { _controller: YourBundle:Default:ajax}
Upvotes: 0
Reputation: 46
I don't know if it's the best way, but :
create a controller action
public function getSearchFormAction(Request $request) {
...
return $this->render( 'YourBundle:searchform.html.twig', array( 'form' => $form->createView() ));
}
in your template
{{ render(controller('YourBundle:YourController:getSearchForm')) }}
Upvotes: 1