How can I render search results using Symfony? - forms

I've created a form for searching data in the DB.
This is the form:
<form role="search" method="post" action="{{ path('search') }}">
<div>
<input type="text" placeholder="Cerca" name ="search">
<span>
<button type="submit"></button>
</span>
</div>
</form>
And of course I also have the action in the controller:
public function searchAction(Request $request)
{
$request = $this->getRequest();
$data = $request->request->get('search');
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery('SELECT a FROM AppBundle:Article a WHERE a.titolo LIKE :data')
->setParameter('data', $data);
$result = $query->getResult();
return $this->render('default/search.html.twig', array('result' => $result));
}
In this way, I can write in the field what I want to search, and submit the form. The form is submitted, but it renders the form again without any message or results.
If I want to render the results, do I have to create a results template where I use a for loop to render all the results?
How can I render the results?

You're already passing $results to the render function. So with the required code in your template it should work without much hassle:
E.g.
{% for result in results %}
<div>
// use result, e.g. {{ result.title }} or whatever your data has to offer
</div>
{% else %}
<em>no results found</em>
{% endfor %}

Related

I can not recover the values ​of my checkbox in my controller

I'm working on a symfony project, I created a checkbox in twig, I want to get checked values ​​in my Controller but I still get a null, I do not know what the problem is please help:
My Action:
public function sendSmsAction(Request $request) {
if ($request->isMethod('get')) {
$ids = $request->query->get('id');
var_dump($ids);
die();
}
return $this->render('CeUtilisateurBundle:Utilisateur:sms.html.twig')
}
My Form in twig:
<form
method="GET"
action="{{path('ce_utilisateur_send_sms') }}"
id="lbad_form_sms"
name="users"
>
{% if listUsers is not empty %}
{% for usr in listUsers %}
<input
id="userchecked"
name="{{usr.id}}"
type="checkbox"
value="{{ usr.telephone }}"
/>
<span>{{ usr.username }}</span>
</form>
Aside from other issues in your example, in order to get those values you need to change the HTML part that displays checkboxes and give them different IDs, proper values and the same name:
<input id="telephone{{ usr.id }}" name="telephone[]" type="checkbox" value="{{ usr.telephone }}"/>
Notice that the name property is the same for every checkbox and has [] at the end - this allows to pass multiple values for one property.
Next to that I would suggest to use a POST request to submit the form, instead of the GET you are using. If you do so, you can get the selected phone numbers in the controller like that:
$telephones = $request->request->get('telephone');
If you want to submit the form with a GET request, then use query instead of the request:
$telephones = $request->query->get('telephone');
First, you're missing closing tags in twig for your if and for, but I assume they just got lost during copying.
Second, you give all your input elements the same id "userchecked". Each HTML element should have a unique ID, e.g. by appending your user ID.
Something similar happens with the name attribute. If you give multiple form elements the same name, only the last one will be submitted.
So your form should look something like this:
<form
method="GET"
action="{{path('ce_utilisateur_send_sms') }}"
id="lbad_form_sms"
name="users"
>
{% if listUsers is not empty %}
{% for usr in listUsers %}
<input
id="{{"userchecked_" ~ usr.id}}"
name="{{"checked_" ~ usr.id}}"
type="checkbox"
value="{{ usr.telephone }}"
/>
<span>{{ usr.username }}</span>
{% endfor %}
{% endif %}
<input type="submit" value="Envoyer un sms!"/>
</form>
To fix your controller, you have to get the input by the name field. Since it is dynamically created, it's easiest to just get all parameters and loop over them.
public function sendSmsAction(Request $request) {
if ($request->isMethod('get')) {
$parameters = $request->query->all();
foreach ($parameters as $name => $value) {
var_dump("$name: $value");
}
die();
}
return $this->render('CeUtilisateurBundle:Utilisateur:sms.html.twig')
}
Note that there will be no submitted parameter for an empty checkbox.

Render issue when validation fail with 2 forms on the same page

i need to render 2 forms on the same page. To do this, I have 2 actions in my controller, the first one manage the form 1 and render the entire page, the second manage the form 2 and render only this form.
The second action is called in twig with a :
{% render (controller("AppBundle:User:secondForm")) %}
Both are rendered, both work, excepted when the validation of the form 2 fail. The Controller return the user to the form with form errors but instead of returning to the entire page, it only render the form.
My code look like this :
First action:
/**
* #Route("/profile/global-page", name="global_page")
*/
public function Global1Action(Request $request)
{
[.......]
$process = $formHandler->process($user);
if ($process) {
$request->getSession()->getFlashBag()->add('success', 'ok');
return $this->redirect($this->generateUrl('an_url'));
}
return $this->render('entire_page.html.twig', array(
'form' => $form->createView(),
));
}
then Twig global (entire_page.html.twig):
{% extends 'base.html.twig' %}
{% block content %}
{#FlashBags#}
{% include 'components/alert.html.twig' with {'type':'success'} %}
<div class="col-md-offset-3 col-md-6">
<div class="col-md-12 row">
<div class="panel panel-profil">
<div class="panel-heading">
<h3 class="panel-title">form 1</h3>
</div>
<div class="panel-body">
<form action="{{ path('global_page') }}" method="POST" class="form-horizontal text-center">
{{ form_widget(form) }}
<button type="submit" name="submit" class="btn btn-main button-sm">Go</button>
</form>
</div>
</div>
</div>
</div>
{% render (controller("AppBundle:User:secondForm")) %}
{% endblock %}
then SecondForm action:
/**
* #Route("/profile/second-form", name="second_form")
*/
public function secondFormAction(Request $request)
{
[.......]
$process = $formHandler->process($user);
if ($process) {
$request->getSession()->getFlashBag()->add('success', 'ok');
return $this->redirect($this->generateUrl('an_url'));
}
return $this->render('only_form2.html.twig', array(
'form' => $form->createView(),
));
}
and finaly the second twig (only_form2.html.twig):
<div class="col-md-offset-3 col-md-6">
<div class="col-md-12 row">
<div class="panel panel-profil" style="margin-top: 10px;">
<div class="panel-heading">
<h3 class="panel-title"> second form panel </h3>
</div>
<div class="panel-body">
<form action="{{ path('second_form') }}" {{ form_enctype(form) }} method="POST">
{{ form_widget(form) }}
<button type="submit" name="submit" class="btn btn-main button-sm">Go 2</button>
</form>
</div>
</div>
</div>
I don't understand how to return the user to the entire page (with form errors) instead of rendering only the second form when it's validation fail.
Thank you !
Edit: I found this post which explain how to have 2 forms in one controller. Answer below seems to not work with Symfony 2.8
You can use a multi form action. Something like this :
public function multiformAction()
{
$form1 = $this->get('form.factory')->createNamedBuilder($formTypeA, 'form1name')
->add('foo', 'text')
->getForm();
$form2 = $this->get('form.factory')->createNamedBuilder($formTypeB, 'form2name')
->add('bar', 'text')
->getForm();
if('POST' === $request->getMethod()) {
if ($request->request->has('form1name') {
// handle the first form
}
if ($request->request->has('form2name') {
// handle the second form
}
}
return array(
'form1' => $form1->createView(),
'form2' => $form2->createView()
);
}
See this post. And this question
To solve this issue you need to send Form request to action where render whole page (Global1Action). Not to action where second form is created.
You will need also move both forms handling to Global1Action.
To change second form action use this method:
$form->setAction($this->generateUrl('global_page'))
Or you can implement ajax handling for second form (if you need to keep it's logic in separate action).

zend 2 -using Zend Forms in partial view helper

I have a form that is repeated over several different modules.
I would like to avoid repeating the same process over and over again and want to render the form once (in my application module) and then simply use a partial view script to call it wherever I wish like this;
<?php echo $this->partial('partialLayout/advertForm'); ?>
Following the advice here it would be pretty straightforward to do this if I simply created a HTML form in the partial script and then called it.
However, I want to use Zend Forms. Bearing in mind that zend form are loaded onto the page via the controller- how do I get around this.
i guess that the crux of the question is - how do you call a Zend Form in another module using a partial view script.
I did something like that with a ViewHelper. My solution is posted here, in Zend Forums.
There it is.
The code ViewHelper :
<?php
/**
* View helper to return the HTML code of the login form.
*
* #filesource LoginForm.php
* #encodage UTF-8
* #author DAFAP Informatique - Alain Pomirol
* #date 10 nov. 2015
* #version 2015-1
*/
namespace Login\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\View\Model\ViewModel;
use Login\Form\LoginForm as Formulaire;
class LoginForm extends AbstractHelper implements ServiceLocatorAwareInterface
{
protected $sm;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->sm = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->sm;
}
public function __invoke()
{
$form = new Formulaire();
$layout = new ViewModel(array(
'form' => $form
));
$viewRender = $this->getServiceLocator()->getServiceLocator()->get('ViewRenderer');
$layout->setTemplate('login/index/index.phtml');
return $viewRender->render($layout);
}
}
The statement in module.config.php in Login module :
'view_helpers' => array(
'invokables' => array(
'loginForm' => 'Login\View\Helper\LoginForm'
)
),
Use in view of the application module :
'view_helpers' => array(
'invokables' => array(
'loginForm' => 'Login\View\Helper\LoginForm'
)
),
It's a common problem! Forms can really be a slow-down with ZF2.
I use Twig (recommended) but you can port this approach to plain old ZF2 templates too.
Here's one such template I use nearly everywhere:
horizontal_form.twig
{# render rows only, not the form tag #}
{% do form.prepare() %}
{% for f in form %}
{% do f.setOption( 'twb-layout', 'horizontal' ) %}
{{ formRow( f ) }}
{% endfor %}
I've got a second with a slightly different approach to support a specific IdentifiableForm in my code:
identifiable_form.twig
<form id="{{ form.getAttribute('name') }}" name="{{ form.getAttribute('name') }}" action="{{ form.getAttribute('action') }}" class="form-horizontal" role="form">
<input type="hidden" name="class" value="{{ form.getAttribute('class') }}">
{% for f in form %}
{% do f.setOption( 'twb-layout', 'horizontal' ) %}
{% do f.setOption( 'twb-form-group-size', 'form-group-sm' ) %}
{{ formRow( f ) }}
{% endfor %}
<div class="hr-line-dashed"></div>
<button type="submit" class="btn btn-primary ladda-button" data-style="expand-right">Save</button>
</form>
With those two in the pocket, my usage looks like this:
In a twig template that should use a form
...include the template from the other:
<div class="panel-body">
{% set form = general_config_form %}
{% do form.prepare() %}
{% include 'application/generic/identifiable_form' %}
</div>
The controller action that operates that template looks like:
$vm = new ViewModel();
$vm->setTerminal( true );
$vm->setTemplate( 'application/admin/config/index' );
$sm = $this->getServiceLocator();
$fm = $sm->get( 'FormElementManager' );
$country = $config_mapper->get('general.country', true );
$general_config_form = $fm->get( GeneralConfigForm::class, [ 'country' => $country ] );
$general_config_form->setAttribute('action', '/admin-config/form-save' );
$vm->setVariable( 'general_config_form', $general_config_form );
In the end it's just a game of:
instantiating/loading the form in the action
setting the form as a variable in the ViewModel
including the "generic form template" from that action's actual template
On topic, if you hate rigging forms as much as I do, I made a little tool to save time here: https://github.com/Saeven/zf2-circlical-formtool
Second, if you use Bootstrap in your app, this form helper replacement really makes things look nice: https://github.com/neilime/zf2-twb-bundle
Good luck!

Django form for querying database. Is form.py needed and how to leave fields empty for query?

I would like to have an input form where a user inputs either an incident_id or a equipment_id or both. The problem I have now is for example, if I fill in incident_id and leave equipment_id blank, the query will return no hits even when incident_id finds a match, because my query uses blank equipment_id in the search. How can I set it up so that when I leave a field blank, it doesn't use it in the query? Do I need to validate this data? I'm not inserting any of it into the db.
I know Django forms uses POST, but in this instance I feel that GET is more appropriate, which means I don't have to use Django forms, but does that mean I also don't have to validate the data? Or should I just use POST anyway and validate that data with Django forms? I'm sorry this is so conceptual. I'm not finding many good answers online.
model.py
from django.db import models
class Incident(models.Model):
incident_id = models.CharField(max_length=1000, blank=True)
equipment_id = models.ForeignKey(Equipment, blank=True)
class Equipment(models.Model):
equipment_id = models.CharField(primary_key=True, max_length=100)
manufacturer = models.ForeignKey(Manufacturer)
equipment_category = models.ForeignKey(Equipment_Category)
validated= models.BooleanField(default=True)
in_service_date = models.DateTimeField('in service date', default=timezone.now)
view.py
#login_required
def search_incidents_query(request):
if request.method == 'GET':
incident_id_query = request.GET.get('incident_id')
equipment_id_query = request.GET.get('equipment_id')
try:
incident_id = str(incident_id_query)
except ValueError:
incident_id = None
try:
equipment_id = str(equipment_id_query)
except ValueError:
username = None
list = [incident_id,equipment_id]
if all(x is None for x in list): #check if `list` contains only None
incident_list = None #this in case the query comes back empty
else: #perform query
incident_list = Incident.objects.filter(incident_id=incident_id, equipment_id=equipment_id)
)
return render(request, 'search_incidents_query.html', {
'incident_list' : incident_list
})
search_incidents_query.html
{% extends "base.html" %}
{% load widget_tweaks %}
{% block content %}
<br>
<div class="container-fluid">
<!-----INPUT FORM------------------->
<form method='GET' action='/search_incidents/'>
<div class="row">
<div class="form-group col-md-3">
<label>Incident ID</label>
<input type="text" name="incident_id" value="{{ incident_id_query }}" class="form-control"/>
</div>
<div class="form-group col-md-3">
<label>Equipment ID</label>
<input type="text" name="equipment" value="{{ equipment_id_query }}" class="form-control"/>
</div>
</div>
</form>
</div>
{% endblock %}
The query
You should use a Q object for similar queries.
from django.db.models import Q
Incident.objects.filter(
Q(incident_id=incident_id) | Q(equipment_id=equipment_id)
)
More on Q objects.
Also, IMO this code needs to live in some Form class. If it was me, I would have put this code in some
The form
class IncidentSearchForm(forms.Form):
incident = forms.CharField(required=False)
# ModelChoiceField because we want django to do the validation for us
# TextInput because the default widget is a select tag
equipment = forms.ModelChoiceField(Equipment.objects.all(), required=False, widget=forms.TextInput) # TextInput because
def clean(self):
# I'd use the clean method to force the user to provide either an incident or equipment value
def search(self):
return Incident.objects.filter(
Q(incident_id=self.cleaned_data['incident']) |
Q(equipment_id=self.cleaned_data['equipment'])
)
The view
Why aren't you using Class Based Views already?
username = None. Wat?
You should use a form because never trust user input.
#login_required
def search_incidents_query(request):
form = IncidentSearchForm(request.GET or None)
incident_list = None
if 'equipment' in request.GET or 'incident' in request.GET:
incident_list = None # Or maybe Incident.objects.none()
if form.is_valid():
incident_list = form.search()
return render(request, 'search_incidents_query.html', {'form' : form})
The template
<form method='GET' action='/search_incidents/'>
<div class="row">
<div class="form-group col-md-3">
{{ form.incident }}
</div>
<div class="form-group col-md-3">
{{ form.equipment }}
</div>
</div>
<input type="submit" />
</form>
You can use a widget on the form to add form-control class to the fields.

Symfony2 forms are rendered empty

I've just started trying to build forms with Symfony2.4.2 and having a VERY frustrating time. I have successfully managed to build the example found in the book chapter 12. I've also tried to build essentially the same form in another project, in another bundle and always seems to end up with an empty form:
<html>
<head></head>
<body>
<form action="" method="post" name="form"> </form>
</body>
</html>
The code to create this:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use DMV\form3Bundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
// create a task and give it some dummy data for this example
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
return $this->render('DMVform3Bundle:Default:new.html.twig', array('form' => $form->createView(),));
}
}
new.html.twig:
{{ form(form) }}
I've tried tried delving into the "$form" at a breakpoint at the "return $this->render..." line that is suppose to render the form and I DO see the form elements texts in the structure but it is a very large and complex structure and I'm not sure what I should see exactly.
Any suggestions would be appreciated.
You can simply render form in your new.html.twig template like:
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{{ form(form) }}
But if you use Symfony version < 2.3, so try to use something that:
{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
<form action="{{ path('task_new') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<input type="submit" />
</form>