I have an issue trying to render a controller which returns a template with formView.
I understood about the sub-request, but I am having difficult time to show any kind of errors.
I think the problem is that after it sees the form is invalid it redirectsToRoute and it looses the POST Request.
If I don't say redirectTo it just renders the view.
base.html.twig
{{ render(controller('AppBundle:Utility:renderSignUpWizard'), {request: app.request}) }}
Utility Controller
/**
* #Route("/registration/wizard/", name="registration.wizard")
*/
public function renderSignUpWizardAction(Request $request)
{
/** #var $user User */
$user = $this->getUser();
$form = $this->createForm(SignUpWizardType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
// save changes to user
$this->persistAndSave($user);
// redirect to profile
return $this->redirectToRoute('profile');
}
else if($form->isSubmitted() && !$form->isValid())
{
return $this->redirectToRoute('home');
}
return $this->render('partials/signup-wizard.html.twig', array
(
'form' => $form->createView(),
));
}
If you could show the twig file where you put the form I could have given a clearer answer. Check the twig file you tell your controller to render and add the following:
Simple way to generate your form(doesn't include errors):
{{ form_start(form) }}
{{ form_widget(form) }
{{ form_end(form) }}
Add:
{{ form_errors(form) }}
if you want erors for a specific field:
{{ form_errors(form.name) }}
Related
Im learning Symfony and I'm creating a CRUD app for practicing.
I want to implement a search function in the page where I list my db items. I was wondering what is the correct way to achieve this.
Right now, I have created a searchType and searchController with the next code:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class searchController extends Controller
{
public function searchAction(){
$formulario = $this->createForm('AppBundle\Form\SearchType');
return $this->render('searchBar.html.twig', ['form' => $formulario->createView()]);
}
}
class SearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('key', ChoiceType::class,
['choices' => [
'Elegir Campo...' => 0,
'Modelo' => 1,
'Marca' => 2,
'Año' => 3,
'Propietario' => 4
]
])
->add('term', TextType::class)
->add('buscar', SubmitType::class)
;
}
}
I have another controller called itemController, where i have the list, add, modify and delete actions. With the twig render() function, I'm rendering the searchBar in the items list page. Which is the correct way to get the values from the 'key' and the 'term' elements and use them to make queries against the db?
I have tried to achieve this without the searchController/searchType and I used a simple <form> in the template and got the key and term values with $request->get() method in the listAction. After I created a switch-case statement to execute queries according to the key value. I could achieve what i wanted like this, but I want to be able to do this the correct way.
Can someone help me/give me some hints on how to continue from this?
Thanks.
Update:
Items Controller:
/**
*#Route("/items", name="items")
*/
public function listAction(Request $request){
$em = $this->getDoctrine()->getManager();
$items = $em->getRepository('AppBundle:Item')->findAll();
return $this->render('items.html.twig', ['items' => $items]);
}
My items.html.twig:
{% extends base.html.twig %}
{% block body %}
{{ render(controller('AppBundle:search:search')) }}
...
{% endblock %}
My searchBar.html.twig:
{{ form_start(form, {'attr': {'class': 'form-inline float-left my-2 my-lg-0'}}) }}
{{ form_widget(form.key) }}
{{ form_widget(form.term, {'attr': {'class': 'ml-1'}}) }}
{{ form_widget(form.buscar, {'attr': {'class': 'btn btn-outline-success ml-1'}}) }}
{{ form_end(form) }}
What i tried with routing and works with the searchController:
/**
* #Route("/search", name="search")
*/
public function searchAction(Request $request){
$em = $this->getDoctrine()->getManager();
$formulario = $this->createForm('AppBundle\Form\SearchType');
$formulario->handleRequest($request);
if($formulario->isSubmitted() && $formulario->isValid()){
$data = $formulario->getData();
$key = $data["key"];
$term = $data["term"];
$items = $em->getRepository('Item::class')->findByTerm($key, $term);
return $this->render('items.html.twig', ['items' => $items]);
}
return $this->render('searchBar.html.twig', ['form' => $formulario->createView()]);
}
If i go to /search and search for an item, it redirects me to my items page with the item i searched. But, If i use the search bar in my items page that i rendered using {{ render(controller('AppBundle:search:search')) }}, it doesn't work.
You are not very far from reaching your goal.
On your Controller, you can update your code to process the incoming request:
public function searchAction(Request $request){
$formulario = $this->createForm('AppBundle\Form\SearchType');
$formulario->handleRequest($request);
if ($formulario->isSubmitted() && $formulario->isValid()) {
$data = $formulario->getData();
// ... perform some action, such as saving the data to the database or search
}
return $this->render('searchBar.html.twig', ['form' => $formulario->createView()]);
}
You can find here more information about Processing Forms
To search into your database, you can process your repositories corresponding to the data. For more information about that, you can go here on Symfony Doctrine documentation
To go further, there is a bundle allowing simplified management of search forms: Lexik Form Filter Bundle
Using Symfony 4 to build a support ticket form:
Created route and functions in page controller
/**
* #Route("/support/ticket")
*/
public function ticket(){
return $this->render('support/ticket/ticket.html.twig');
}
public function new(Request $request)
{
// creates a Ticket and gives it some dummy data for this example
$ticket = new Ticket();
$form = $this->createFormBuilder($ticket)
->add('category', ChoiceType::class, array(
'choices' => array(
'ROMAC eHR' => 1,
'ROMAC Website' => 2,
'ROMAC Guide' => 3,
)
))
->add('comment', TextareaType::class)
->add('save', SubmitType::class, array('label' => 'Submit Ticket'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// $form->getData() holds the submitted values
// but, the original `$task` variable has also been updated
$ticket = $form->getData();
// ... perform some action, such as saving the task to the database
// for example, if Ticket is a Doctrine entity, save it!
// $entityManager = $this->getDoctrine()->getManager();
// $entityManager->persist($task);
// $entityManager->flush();
return $this->redirectToRoute('ticket_success');
}
return $this->render('support/ticket/ticket.html.twig', array(
'form' => $form->createView(),
));
}
And then render the form in the twig template:
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.category) }}
{{ form_row(form.comment) }}
{{ form_end(form) }}
When I load the page I get a Symfony error stating that "Variable form does not exist".
I have followed the documentation https://symfony.com/doc/current/forms.html. Where/how can I find the issue?
I assume you get this error while accessing "/support/ticket"
You have "form" variable missing in this function
public function ticket(){
return $this->render('support/ticket/ticket.html.twig');
}
I will suggest to wrap your code in twig template in a "if" block
{% if form is defined %}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.category) }}
{{ form_row(form.comment) }}
{{ form_end(form) }}
{% endif %}
Or you need to adjust your controller functions accordingly
I have a problem with form.
For start, in my main twig I include the twig i use for the form:
{% include 'MainBundle::manage.html.twig'%}
The controller is mainController
The controller displays everything that appears in main twig.
The action of the form :
public function manageAction(Request $request){
$manageForm = $this->createFormBuilder()
->add('submitFile', FileType::class, array('label' => 'File to Submit'))
->add('send', SubmitType::class)->getForm();
if ($request->getMethod('post') == 'POST') {
// Bind request to the form
$manageForm->bindRequest($request);
// If form is valid
if ($manageForm->isValid()) {
// Get file
$file = $manageForm->get('submitFile');
$file->getData();
}
}
return $this->render('MainBundle::main.html.twig', array(
'manageForm' => $manageForm->createView()
));
}
routing.yml
manage:
path: /manage
defaults:
_controller: MainBundle:Main:manage
requirements:
_method: POST
manage.html.twig
{% extends 'MainBundle::main.html.twig' %}
{% block content %}
<form action="" method="post">
{{ form_widget(manageForm) }}
<input type="submit" />
</form>
{% endblock %}
when i run in the route /main I should have my form display but i have an error
Variable "manageForm" does not exist.
I guess the controller does not take the view...
In your particular case - change to following.
return $this->render('MainBundle::manage.html.twig', array(
'manageForm' => $manageForm->createView()
));
If you extend the main/base twig in other twig, then you need to render this other twig (always).
Parameters which you pass to extended twig will be available in twig you have extended (in this case the main.html.twig), but it doesn't work the other way around.
{% include 'MainBundle::manage.html.twig'%} // why ?
Don't do anything with main - just extend it in manage!!!!
I see other problems with your overall code also.
CONTROLLER:
// the more modern way instead of checking manually for request method
if ($form->isSubmitted()) {
if ($form->isValid()) {
// perform actions...
} else {
// was not valid...return error messages
}
}
FORM
<form action="" method="post"> // this is the oldschool way
{{ form_widget(manageForm) }}
<input type="submit" />
</form>
Better something like... (some of my recent coded examples)
{{ form_start(form, {'attr' : {'method' : 'post', 'enctype' : 'multipart/form-data', 'class' : 'test'}}) }}
{{ form_errors(form.resume) }}
{{ form_widget(form.resume, {'attr' : {'id' : 'file-upload-rec', 'class' : 'file-upload-rec js-file-upload-rec', 'accept' : '.pdf, .doc, .docx'}}) }}
{{ form_label(form.resume, 'select', {'label_attr' : {'class' : 'file-upload js-file-upload'}}) }}
{{ form_row(form._token) }}
{{ form_end(form, {'render_rest': false}) }}
ps. you might also want to read all of https://symfony.com/doc/current/forms.html
You are rendering main.html.twig instead of manage.html.twig. I guess the first one uses an importForm variable which is therefore not passed to the view.
If not, and you really need to render main.html.twig which includes manage.html.twig, then why is manage.html.twig extending main.html.twig? Either include one in another, or extend one from another, not both.
You need to pass the variable while including your template
{% include 'MainBundle::manage.html.twig' with {'manageForm': manageForm} %}
For more information see https://twig.symfony.com/doc/2.x/tags/include.html
Thanks!
I have a form controlling the search bar in the websites navbar:
public function searchFormAction()
{
$form = $this->createFormBuilder()
->setAction($this->generateUrl('search'))
->setMethod('GET')
->add("value", TextType::class, array('label' => false))
->add("Search", SubmitType::class)
->getForm();
return $this->render('components/search-form.html.twig', [
"form" => $form->createView()
]);
}
As you can see, the form has a specific action path to this function:
/**
* #Route("/search", name="search")
*/
public function searchAction(Request $request)
{
return $this -> render ("post/post-search.html.twig", [
"value" => $request->query->get('value')
]);
}
For now this shouldn't do much more than just display the value on the page.
The problem is that the website fails to redirect when the form is used
So when I put foo in the search, and click submit the path looks like this:
localhost:8000/page?form%5Bvalue%5D=foo&form%5BSearch%5D=&form%5B_token%5D=PsouIRAy2QaQ8j2XO_uYrs7PcaR6jyjQN3W3_xRMdgw
Moreover if I go to localhost:8000/search and try to put anything into the search bar, no value is printed.
Here is how the form is rendered:
//search-form.html.twig
<form class="navbar-form navbar-left">
{{ form_start(form) }}
<div class="form-group">
{{ form_row(form.value) }}
</div>
{{ form_row(form.Search) }}
{{ form_end(form) }}
</form>
And placed in the base navbar:
//base.html.twig
//...
{{ render(controller(
'AppBundle:Form:searchForm'
)) }}
//...
Inspecting the element shows that the form tag has no action and method attributes
What could be the issue here and how can I fix it?
Fixed! Made a simple mistake in the twig file.
Placed the form start inside html form tags, that way the submit button would send to an empty form.
I am trying to post data through Symfony form's button but it does not validate form.
Here is my controller file:
public function PurchaseProductAction(Request $request)
{
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
->setMethod('POST')
->add('CompanyName', 'text', array(
'label'=>false
))
->add('Address1', 'text', array(
'label'=>false
))
->add('Continue_to_Step_2', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid())
{
// It does not come here
$data = $form->getData();
$value = $data['CompanyName'];
echo $value;
}
}
Its my twig file:
{% block content %}
Company Name{{ form_row(form.CompanyName) }}
Address Line 1{{ form_row(form.Address1) }}
{{form_widget(form.Continue_to_Step_2)}}
{% endblock %}
Kindly guide me what I am doing wrong due to which my method does not call?
As explained in the Rendering a Form in a Template part of the documentation, you've to include the {{ form_start(form) }} and the {{ form_end(form) }} twig form helpers.
This will generate the appropriate <form> tags according to your form definition.
Also, keep in mind that Support for submit buttons was added in Symfony 2.3. Before that, you had to add buttons to the form's HTML manually.
Update,
form_end should be called with render_rest option set to false if you don't want it to show unrendered fields,
{# don't render unrendered fields #}
{{ form_end(form, {'render_rest': false}) }}