I'm a beginner ;)
I'm trying to have a searchBar form in a navbar located in base.twig.html, searching for the game just by it's name.
But i can't get any results when typing in the form in navbar. The functions in repo and controller are correct, I checked it with "dd".
This is my controller:
[Route('/search/SBresult', name: 'app_search')]
public function searchBar(Request $request, EntityManagerInterface $entityManager, string $Nazwa='Nazwa'):Response
{
$form = $this->createFormBuilder([])
->add('Nazwa', TextType::class, ['required' => false, 'label' => 'wpisz nazwę gry',
'label_attr'=> ['class'=>'text-white ps-3'], 'attr' => ['class'=>'form-control form-
control-lg']])
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$Nazwa = $form->get('Nazwa')->getData();
//dd($Nazwa);
$Gry = $entityManager->getRepository(Gra::class)->findGra($Nazwa);
//dd($Gry);
return $this->render('search/SBresult.html.twig', ['Gry' => $Gry]);
//return $this->redirectToRoute('app_search', ['Gry' => $Gry], Response:: HTTP_SEE_OTHER);
}
return $this->render('search/searchBar.html.twig', ['form' => $form->createView()]);
}
I have a template search/SBresult.html.twig with a proper {% for Gry in Gry %}
And this is how I render the form in base.twig.html:
{{render (controller('App\\Controller\\SearchController::searchBar'))}}
I think that the problem lies in rendering the form in a navbar, because I've already made a searchform with many options to choose, exactly the same way, and it works. But this form is rendered in its own template, not in base.
What should I do to get the results from the searchBar in base.twig.html?
Related
I want to process a form in a separate controller. While the Symfony docs show how to change a form's action and method, they don't show how the controller selected in $form->setAction() is actually supposed to handle the form. Is it present in Request? Do we make another Form object so we can check $form->isSubmitted() and $form->isValid()?
It's a pretty glaring omission.
Here is a quick example:
Controller for displaying the form
/**
* #route("/form", name="form_route")
*/
public function formAction()
{
$form = $this->createFormBuilder()
->setAction($this->generateUrl('task_route'))
->setMethod('POST')
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();
return $this->render('form.html.twig', [
'form' => $form->createView(),
]);
}
Controller to handle submission
Is it present in Request?
Yes, the form data is in the request.
Do we make another Form object so we can check $form->isSubmitted() and $form->isValid()?
The form has to be recreated so that you can handle and validate the request.
/**
* #route("/task", name="task_route")
*/
public function postAction(Request $request)
{
$form = $this->createFormBuilder()
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()){
$task = $form->getData();
/* ... */
}
//render task to view submission
return $this->render('task.html.twig', [
'task' => $task,
]);
}
This does have some duplicate code even when using entities and Symfony's Form Classes, which is why Symfony Docs recommends using the same controller for processing forms.
I'm learning Symfony to move an old website from flat-PHP to a framework.
In the old website I have a page with two levels of forms: the first one is just a button with which the user accepts some conditions, while the second one sends a message to the website admin. When the user clicks the button in the first form, the page is refreshed and the second form appears. The important thing is that the user can't get access to the second form without pushing the first button.
Right now, the action method is this one:
/**
* #Route("/ask-consultation", name="ask_consultation")
*/
public function askConsultationAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $form = $this->createFormBuilder()
->add('submit', SubmitType::class, array('label' => 'Confermo'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$consultation = new Consultation;
$form = $this->createForm(AskConsultationType::class, $consultation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$consultation = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($consultation);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('visitor/consultation/ask_step2.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $form->createView(),
]);
}
When I go to /ask-consultation it shows me the button to accept certain conditions; when I click the button, it shows me the form to send the message, but when I send it, I don't get redirected to homepage, but again to the first page of /ask-consultation.
I understand why this code doesn't work, but I can't understand how to make it work. One solution could be some sort of modal dialog for the first form, but if possible I'd prefer to handle all the passages in PHP. Is it possible to split the form handling without changing the route?
The most important thing in my case is that the user can't get to the second form without first having clicked on the first button.
Render everything in a single form but use javascript (jQuery) to hide the ask-consultation part of the form. When the user clicks "Confirm", then unhide the form. This avoids the controller altogether for the confirmation step. You don't seem to be recording this confirmation anyway, so just let the client side handle things. You can still check (on form submission) to see that the "confirmation" was accepted, for example by setting a hidden variable through javascript.
It turns out the answer was quite simple.
In my mind, I had to handle the second form inside the first if ($form->isSubmitted() && $form->isValid()), but of course that's not the path taken by PHP. Everytime the route gets loaded, askConsultationAction() is run from its first instruction. I simply had to initialize both forms (with different variable names, obviously), and handle the forms one after the other, rather than one inside the other.
This code works like a charm:
/**
* #Route("/ask-consultation", name="ask_consultation")
*/
public function askConsultationAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$consultation = new Consultation;
$conditions_form = $this->createFormBuilder()
->add('submit', SubmitType::class, array('label' => 'Confermo'))
->getForm();
$consultation_form = $this->createForm(AskConsultationType::class, $consultation);
$consultation_form->handleRequest($request);
$conditions_form->handleRequest($request);
if ($conditions_form->isSubmitted() && $conditions_form->isValid()) {
return $this->render('visitor/consultation/ask_step2.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $consultation_form->createView(),
]);
}
if ($consultation_form->isSubmitted() && $consultation_form->isValid()) {
$consultation = $consultation_form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($consultation);
$em->flush();
return $this->redirectToRoute('homepage');
}
return $this->render('visitor/consultation/ask_step1.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
'sidebars' => $em->getRepository('AppBundle:Sidebar')->findAllOrderedBySortingPosition(),
'form' => $conditions_form->createView(),
]);
}
Maybe, it would be even better if I used an else... if....
I have showUsersAction()-method inside the Defaultcontroller which should render a form where it should be possible to select a user from a list, press a submit-button and then redirects to a route /showItems/{userId} where the items of a user are shown.
I know that it would be possible to do that easy with a link, but I want to make use of ChoiceType:
First I copied an example of ChoiceType from the Symfony documentation with a minimal change:
/**
* #Route("/showUsers", name="showUsers")
*/
public function showUsersAction(){
$users = $this->getDoctrine()->getManager()->getRepository('AppBundle:User')->findAll();
$form = $this->createFormBuilder()
->setMethod('POST')
->add('user', ChoiceType::class, [
'choices' => $users,
'choice_label' => function($user) {
/** #var User $user */
return strtoupper($user->getUsername());//here is the problem
},
'choice_attr' => function($user) {
return ['class' => 'user_'.strtolower($user->getUsername())];
},
])
->getForm();
return $this->render('default/showUsers.html.twig',
array('users' => $users, 'form' => $form->createView()));
}
I am sure $users gives an array with objects of the class User. When I execute the route in the browser I get following error message:
Error: Call to a member function getUsername() on a non-object
in src\AppBundle\Controller\DefaultController.php at line 50
Line 50 is the commented line return strtoupper($user->getUsername());
What is the problem and how can I solve?
And how can I get the selected User after I submitted via a submit button to the same route?
EDIT: (because of possible duplication)
Of course I know that the method getUsername() can not be called, because $user is a non-object, which should not be related to the Symfony documentation. So my question relates to a Symfony special solution which has absolutly nothing to do with 100 of other problems where the Error is the same.
Use entity type instead. Here is a link to documentation. It's a child type of a choice type, with exactly same functionality, and also every option returns an entity object.
Because I had trouble with setting up the entity type field too, I decided to post my solution for the whole action function and the twig file here:
action method:
/**
* #Route("/showUsers", name="showUsers")
*/
public function showUsersAction(Request $request){
// gets array of all users in the database
$users = $this->getDoctrine()->getManager()->getRepository('AppBundle:User')->findAll();
$form = $this->createFormBuilder()
->setMethod('POST')
->add('users', EntityType::class, array(
'class' => 'AppBundle:User',
'choices' => $users,
// This combination of 'expanded' and 'multiple' implements radio buttons
'expanded' => true,
'multiple' => false,
'choice_label' => function ($user) {
return $user->__toString();
}
))
// Adds a submit button to the form.
// The 'attr' option adds a class to the html rendered form
->add('selected', SubmitType::class, array('label' => 'Show User Items', 'attr' => array('class' => 'button')))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// gets the selected user
$selectedUser = $form["users"]->getData();
// redirects to route 'showItems' with the id of the selected user
return $this->redirectToRoute('showItems', array('userId' => $selectedUser->getId()));
}
// renders 'default/showUsers.html.twig' with the form as an argument
return $this->render('default/showUsers.html.twig', array('form' => $form->createView()));
}
twig file:
{#
// app/Resources/views/default/showUsers.html.twig
Description:
twig file that implements a form in which one of all users can get
selected via radio button to show the items of the user after a click
on the submit button.
#author goulashsoup
#}
{% extends 'base.html.twig' %}
{% block title %}Users{% endblock %}
{% block body %}
<div class="users">
{{ form_start(form) }}
{% for user in form.users %}
{{ form_widget(user) }}
{{ form_label(user) }}
<br>
{% endfor %}
<br>
{{ form_end(form) }}
</div>
{% endblock %}
I am doing a form with Symfony 2.3 to suscribe to a newsletter.
The form is working good in is own template (newsletter.html.twig).
My controller action:
public function newsletterAction(Request $request)
{
$newsletter = new Newsletter();
$form = $this->createFormBuilder($newsletter)
->add('email', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($newsletter);
$em->flush();
$request->getSession()->getFlashBag()->add('notice', 'Vous venez de vous enregistré à la Newsletter d\'Emoovio.');
}
return $this->render('MyBundle:Global:newsletter.html.twig', array(
'form' => $form->createView(),
));
}
My template where it's working (newsletter.html.twig) :
{{ form(form) }}
My template where it does not work (index.html.twig):
////
{% render (controller("EmooviofrontBundle:Global:newsletter")) %}
////
The form is display but it's not working. May be is miss something. Has anyone had the same problem and could explain me. Thank you.
I think that when you submit your form, the POST data arrives in the indexAction. This action does not validate your form and renders the normal page. While rendering it will call the Global:newsletterAction but just as a sub request in the GET method without form data.
You could try to apply an action to your formdata
$form = $this->createFormBuilder($newsletter, array(
'action' => $this->generateUrl('global_newsletter'),
'method' => 'POST',
))
->add('email', 'email')
->add('submit', 'submit')
->getForm();
I'm new with Symfony2 (2.4.4).
I want to create a HTML layout which shows always a form on top (searchbar). I send the form via post and would like to redirect to another controller, which should pass the user input and generate an output. I created a new function like this:
public function searchFormAction(Request $request)
{
//$defaultData = array('sstring' => 'Suche');
$form = $this->createFormBuilder()
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'))
->getForm();
$form->handleRequest($request);
if($request->isMethod('POST'))
{
return $this->redirect('SchmanEmployeeBundle:Employee:search', array(
'sstring' => $form->get('sstring')->getData();
));
}
return $this->render('SchmanEmployeeBundle:Employee:searchForm.html.twig', array(
'form' => $form->createView()
));
}
I extended my base layout (base.html.twig) and include the form with the render function
{% render(controller('SchmanEmployeeBundle:Employee:searchForm')) %}
This works fine and the form is always present in my layout. The given HTML looks like this:
<form name="form" method="post" action="/app_dev.php/">
<div><input type="search" id="form_sstring" name="form[sstring]" required="required"></div>
<div><button type="submit" id="form_submit" name="form[submit]">suchen</button></div>
Now I have 3 questions:
If I submit the form, I don't want to be redirected to the searchAction Controller. This is because the $request->isMethod is always GET. Why? The form actions is post?
In the Symfony Webtool the form section is also empty. I see all form fields (sstring) and the data is always null. Where's the user input?
First off, your form is set to be POST by default, so you should be good. Second, you don't pass any data to be filled by your form, and I think you should. Third, you don't check if the form is valid, which includes the test if it's submitted. You should do this:
$defaultData = array(); // No need for a class object, array is enough
$form = $this->createFormBuilder($defaultData)
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'))
->getForm();
$form->handleRequest($request);
if($form->isValid())
{
// Happens if the form is submitted
return $this->redirect('SchmanEmployeeBundle:Employee:search', array(
'sstring' => $form->get('sstring')->getData(); // TODO: This will probably produce an error, fix it
));
}
return $this->render('SchmanEmployeeBundle:Employee:searchForm.html.twig', array(
'form' => $form->createView()
));
Also, I think you shouldn't worry about the form method because you don't have different implementations for other methods. This is the usual way the forms are handled in Symfony. You should read on forms in detail before proceeding, the article is quite informative.
I guess its because you didnt specified, in your routing configuration, that the method of this function is POST.
Because the form never submitted to your function (your function want GET, but send POST)
Where is the last question?
There is a great code to make your search function, it should work (sorry if you dont use annotation).
One good point, you can now use your searchType everywhere in your project, you should make your form like that instead of formbuilder into your controller. Easier to read and to use.
Controller:
/**
* To search something
*
* #Route("/search", name="search")
* #Template()
*/
public function searchAction()
{
$form = $this->createForm(new searchType());
$request = $this->get('request');
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$informations = $form->get('search')->getData();
//make things here
}
}
}
And here is the searchType class:
class searchType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'));
}
/**
* #return string
*/
public function getName()
{
return 'yournamespace_searchType';
}
}