Chapters
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
Handling the Form Submit
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Creating the form class and rendering was... easy! Now it's time to talk about handling the form submit. Notice: we haven't configured anything on our form about what URL it should submit to. When we rendered it, we used form_start()
and... that's it! Inspect element on the form. By default, form_start()
creates a form
tag with no action attribute. And when a form
tag has no action=
, it means that it will submit right back to this same URL.
That's the most common way of handling forms in Symfony: the same controller is responsible for both rendering the form on a GET request and handling the form submit on a POST request. The way you do this always follows a similar pattern.
The Form Submit Logic
First, get the $request
object by type-hinting Request
- the one from HttpFoundation
. Next, add $form->handleRequest($request)
and then if ($form->isSubmitted() && $form->isValid())
. Inside the if
, dd($form->getData()
.
Show Lines
|
// ... lines 1 - 13 |
class ArticleAdminController extends AbstractController | |
{ | |
Show Lines
|
// ... lines 16 - 19 |
public function new(EntityManagerInterface $em, Request $request) | |
{ | |
Show Lines
|
// ... lines 22 - 23 |
$form->handleRequest($request); | |
if ($form->isSubmitted() && $form->isValid()) { | |
dd($form->getData()); | |
} | |
Show Lines
|
// ... lines 28 - 31 |
} | |
Show Lines
|
// ... lines 33 - 41 |
} |
Okay, so... this requires a little bit of explanation. First, yea, the $form->handleRequest()
makes it look like the submitted data is being read and processed on every request, even the initial GET request that renders the form. But, that's not true! By default, handleRequest()
only processes the data when this is a POST
request. So, when the form is being submitted. When the form is originally loaded, handleRequest()
sees that this is a GET
request, does nothing, $form->isSubmitted()
returns false, and then the un-submitted form is rendered by Twig.
But, when we POST
the form, ah, that's when handleRequest()
does its magic. Because the form knows all of its fields, it grabs all of the submitted data from the $request
automatically and isSubmitted()
returns true
. Oh, and later, we'll talk about adding validation to our form. As you can guess, when validation fails, $form->isValid()
returns false.
So, wow! This controller does a lot, with very little code. And there are three possible flows. One: if this is a GET request, isSubmitted()
returns false and so the form is passed to Twig. Two, if this is a POST request but validation fails, isValid()
returns false and so the form is again passed to Twig, but now it will render with errors. We'll see that later. And three: if this is a POST request and validation passes, both isSubmitted()
and isValid()
are true, and we finally get into the if
block. $form->getData()
is how we access the final, normalized data that was submitted.
Phew! So, let's try it! Find your browser and create a very important article about the booming tourism industry on Mercury. Submit!
Yes! It dumps out exactly what we probably expected: an array with title
and content
keys. It's not too fancy yet, but it works nicely.
Saving the Form Data
To insert a new article into the database, we need to use this data to create an Article
object. There is a super cool way to do this automatically with the form system. But, to start, let's do it the manual way. Add $data = $form->getData()
. Then, create that object: $article = new Article()
, $article->setTitle($data['title']);
, $article->setContent($data['content'])
, and the author field is also required. How about, $article->setAuthor()
with $this->getUser()
: the current user will be the author.
Show Lines
|
// ... lines 1 - 19 |
public function new(EntityManagerInterface $em, Request $request) | |
{ | |
Show Lines
|
// ... lines 22 - 24 |
if ($form->isSubmitted() && $form->isValid()) { | |
$data = $form->getData(); | |
$article = new Article(); | |
$article->setTitle($data['title']); | |
$article->setContent($data['content']); | |
$article->setAuthor($this->getUser()); | |
Show Lines
|
// ... lines 31 - 35 |
} | |
Show Lines
|
// ... lines 37 - 40 |
} | |
Show Lines
|
// ... lines 42 - 52 |
To save this to the database, we need the entity manager. And, hey! We already have it thanks to our EntityManagerInterface
argument. Save with the normal $em->persist($article)
, $em->flush()
.
Awesome! The last thing we always do after a successful form submit is redirect to another page. Let's use return this->redirectToRoute('app_homepage')
.
Show Lines
|
// ... lines 1 - 24 |
if ($form->isSubmitted() && $form->isValid()) { | |
Show Lines
|
// ... lines 26 - 31 |
$em->persist($article); | |
$em->flush(); | |
return $this->redirectToRoute('app_homepage'); | |
} | |
Show Lines
|
// ... lines 37 - 52 |
Time to test this puppy out! Refresh to re-post the data. Cool! I... think it worked? Scroll down... Hmm. I don't see my article. Ah! But that's because only published articles are shown on the homepage.
Adding an Article List Page
What we really need is a way to see all of the articles in an admin area. We have a "new" article page and a work-in-progress edit page. Now, create a new method: public function list()
. Above it, add the annotation @Route("/admin/article")
. To fetch all of the articles, add an argument: ArticleRepository $articleRepo
, and then say $articles = $articleRepo->findAll()
. At the bottom, render a template - article_admin/list.html.twig
- and pass this an articles
variable.
Show Lines
|
// ... lines 1 - 52 |
/** | |
* @Route("/admin/article") | |
*/ | |
public function list(ArticleRepository $articleRepo) | |
{ | |
$articles = $articleRepo->findAll(); | |
return $this->render('article_admin/list.html.twig', [ | |
'articles' => $articles, | |
]); | |
} | |
Show Lines
|
// ... lines 64 - 65 |
Oh, and I'll cheat again! If you have the Symfony plugin installed, you can put your cursor in the template name and press Alt+Enter to create the Twig template, right next to the other one.
Because we're awesome at Twig, the contents of this are pretty boring. In fact, I'm going to cheat again! I'm on a roll! I'll paste a template I already prepared. You can get this from the code block on this page.
{% extends 'content_base.html.twig' %} | |
{% block content_body %} | |
<a href="{{ path('admin_article_new') }}" class="btn btn-primary pull-right"> | |
Create <span class="fa fa-plus-circle"></span> | |
</a> | |
<h1>All Articles</h1> | |
<table class="table"> | |
<thead> | |
<tr> | |
<th>Title</th> | |
<th>Author</th> | |
<th>Published?</th> | |
</tr> | |
</thead> | |
<tbody> | |
{% for article in articles %} | |
<tr> | |
<td>{{ article.title }}</td> | |
<td>{{ article.author.email }}</td> | |
<td> | |
<span class="fa fa-{{ article.isPublished ? 'check' : 'times' }}"></span> | |
</td> | |
</tr> | |
{% endfor %} | |
</tbody> | |
</table> | |
{% endblock %} |
And... yea! Beautifully boring! This loops over the articles
and prints some basic info about each. I also added a link on top to the new article form page.
Oh, there is one interesting part: the article.isPublished
code, which I use to show a check mark or an "x" mark. That's interesting because... we don't have an isPublished
property or method on Article
! Add public function isPublished()
, which will return a bool
, and very simply, return $this->publishedAt !== null
.
Show Lines
|
// ... lines 1 - 15 |
class Article | |
{ | |
Show Lines
|
// ... lines 18 - 127 |
public function isPublished(): bool | |
{ | |
return $this->publishedAt !== null; | |
} | |
Show Lines
|
// ... lines 132 - 253 |
} |
If you want to be fancier, you could check to see if the publishedAt
date is not null and also not a future date. It's up to how you want your app to work.
Time to try it! Manually go to /admin/article
and... woohoo! There is our new article on the bottom.
And... yea! We've already learned enough to create, render and process a form submit! Nice work! Next, let's make things a bit fancier by rendering a success message after submitting.
43 Comments
Hey Michael,
Why is the value not allowed? Can you show me the full error message please? Also, you may solve this by using data transformers. You can read more about them here https://symfony.com/doc/current/form/data_transformers.html
Cheers!
Dear MolloKhan
Thanks a lot for the fast reply!
I could solve the problem: in the SubmitEvent I do remove the task field again and set it back to the ChoiceType. Sometimes it's so easy;-)
Thanks a lot for your support and merry Christmas!
Michael
Hello :)
I want some help with a work project! I am an apprentice and I am making a quiz.
I have a user table.
I have a questions table. -> I need to render the questions in the symfony form.
I have an answers table. -> I need to render the answers in the symfony form.
I have a response table. -> the answers that the user selects become the response.
I have more tables however these are the tables that will be affected when I submit the quiz data.
And lastly for more information they already have their bidirectional associations established. (A user has many responses, A question has many answers etc).
How would I achieve this?
Hey Shane,
I'm not sure I completely understand your question. What do you want to achieve exactly? As I understand you already have all necessary relations. Do you want to create a Symfony form for this structure? If so - you need nested form probably. Or do you want to know how to store the data in your DB properly?
Cheers!
Hey Victor, thanks for getting back to me :)
So I will try my best to articulate my question!
I want to create a symfony form for this structure, please see example below:
In my database lives 10 questions. I want my form to display those ten questions!
I also want my form to display the possible answers for each of the questions (the bi-directional relations are already established)
Hey Shane,
Ah, ok... I think I got it, thanks! I'll try to give you some tips ) Yeah, in this case you will definitely need a nested form type, I'd recommend you to call the main form type as QuizFormType, inside you will need to add a single field called "questions" which will be a CollectionType of other type, in your case it will be QuestionType. The QuestionType custom form type in turn will have another field called "answers" that will be a CollectionType of AnswerType custom form type. In the last, i.e. AnswerType, I suppose you want to have a text field where users will write their answers.
So, as you can see it's a custom form type that is nested in another custom form type and so on. I'd recommend you to start QuizFormType and add one nested level at a time, render the form and see the result... if it works - then move next with another nested level, and so on. Do not try to create this complex form at once from scratch, you may get lost in it.
I hope this helps! Sorry I can't help with more details - unfortunately, we can answer only tutorial-related question, we don't have bandwidth to answer personal project questions but I hope my tips will help you a lot to start working on it.
Cheers!
Error Cannot use object of type App\Entity\Article as array (Or whatever 'Article' is named, I am using my own filenames.)
highlights: 28: $article->setTitle($data['title']);
EDIT: When I do: $article->setTitle($form['title']->getData());
Then it works. Is it okay this way?
Hey @Farry7!
Yes! Your edited way is totally fine. But... I think you don't even need that line :). The explanation is here: https://symfonycasts.com/sc...
Basically, you can use the form in 2 different ways:
1) (as is shown in this chapter): you create a form that is NOT bound to an object. Then, after you submit, $form->getData() is an associative array. In this situation, your original could would work just fine.'
2) But most of the time (as shown in the https://symfonycasts.com/sc... chapter) you will "bind" your form to an object. What that means is that when you submit, Symfony automatically takes the submitted data and puts it onto your object. Then, there is no need to say $article->setTitle(...) because the form system already did that for you.
Let me know if that helps!
Cheers!
I am creating a complex form with around 30 input fields. When binding them to a class, it starts guessing that input name Article should be a textarea, etc. I am not sure if I want the system to guess the fields for me. Is it possible to remove $form->getData() and not let the system guess the input types?
Hey @Farry7!
You're right - it starts to guess the field type based on the Doctrine "type" that the property has. But, you can absolutely override this in your form. Basically, I let the form system "guess", and when it guesses wrong/I need something else, I override it in the form. We talk about that here: https://symfonycasts.com/sc...
Cheers!
I want to make a pretty big form with about 30 input fields. I want to organize them nicely so that some fields are next to each other and some are underneath each other / adding <hr> lines in between some fields for better visibility and bootstrap themes.
How do I add classes to individual input fields? And how do I add some divs in between certain input fields? For example: seperating different categorie inputs into different Accordion fields so people can open / close certain parts of the form (because its a big form).
If I completely skip Symfony Forms and use Doctrine and custom html form markup. Will it be more work with validating, etc? Because I have made plenty of forms outside of Symfony and handled the validation etc by myself. But I don't know what the extra value is to use Symfony Forms instead of a custom one. And I don't have the time to go through the 4 hour tutorial. So if you can summarize it a bit, it would be great! :)
Hey @Farry&
A lot of good questions here. Let me see if I can help :).
How do I add classes to individual input fields?
This can be done by passing the attr variable to form_row or form_widget - like we do here - https://symfonycasts.com/screencast/symfony-forms/dynamic-select-js#codeblock-ab47270a79 - it's not the most "attractive" thing, but that's how you do it
And how do I add some divs in between certain input fields?
It depends on the exact markup you're looking for, but I usually use form_row
(which renders a div that's around the input, label and error message). And so, it would be:
{{ form_row(form.firstName) }}
<hr/>
{{ form_row(form.lastName) }}
That doesn't give you full control, as each field still renders inside a div with the input+label+error message in a certain spot, but it gives you some control. You can also go further and use the other form-rendering functions instead of form_row. Below is an example - not really a useful example - but an example of rendering the error, label and widget wherever/however you want.
{{ form_error(form.firstName) }}
<div>
{{ form_label(form.firstName) }}
{{ form_widget(form.firstName) }}
</div>
<hr/>
{{ form_error(form.lastName) }}
<div>
{{ form_label(form.lastName) }}
{{ form_widget(form.lastName) }}
</div>
If I completely skip Symfony Forms and use Doctrine and custom html form markup. Will it be more work with validating, etc? Because I have made plenty of forms outside of Symfony and handled the validation etc by myself. But I don't know what the extra value is to use Symfony Forms instead of a custom one.
You can definitely do that. The biggest things you would lose (and have to do by hand) would be:
A) Rendering the fields - you'll need to do that by hand, including checking to see if each field has an error (probably you would pass an array of validation errors from the controller to the template, then on each field, check to see if that field has an error). Plus, often you also want to add an "field-error" (or something) class to elements with an error. This is all the type of stuff the form component handles for you.
B) Pre-filling the fields: if you're updating an item, you'll need to pass the item into the template and make sure to set the value="" for all the fields to the existing value (or the "selected" on the right option element).
C) Setting the data back onto your object. When you submit, the form component automatically takes the data and sets it back onto your entity object. You'll have a lot of $product->setName($data['name');
type of manual code.
The form system is complex... but every time I think about not using it, I remember how much it gives me.
Btw, in Symfony 5.2, there is another way to render the fields for a form: completely manually - https://symfony.com/blog/new-in-symfony-5-2-form-field-helpers - that allows you to use the form system... but with even more control over rendering.
Cheers!
Hi SymfonyCasts-Team!
I've been folloing your screencasts from the very start of the Symfony 4 tutorial until now and while I had absolutely no problems following your guides (they are excellent btw), I am still having so much struggle with "$this". I don't know how often I read about it, how often I searched and studied tutorials, I just can't get my head around it for some reason. I'm struggling SO hard to visualize what "$this" means, or what it stands for WHILE coding. I know it's supposed to be a reference to the calling object, but my brain absolutely starts farting away as soon as I see that. Can you guys give me something to work with here, maybe a tutorial of yours, where you explain it? I would be really glad. Thank you in advance and greetings from Germany!
Hey denizgelion
Thanks for your kind words :)
About $this
in PHP. $this
is a reserved keyword that refers to the current object. It's defined inside of any of its declared non-static methods. So, by using $this
you have access to everything that's contained by such class (that's not static). When working with inheritance, the only thing you won't have access to is to private things (methods, attributes, constants) that live inside the parent class. Did this help you?
Cheers!
Ah, that clears the last missunderstanding. Thanks for the quick reply. I asked a few other fellow programmers and ultimately this example made me understand it: https://www.w3schools.com/j...
It might be a Javascript example, but I just needed a very simple example of what it does. As stupid as it sounds ;)
Can you briefly explain what an object / methodes are?
Hey @Farry7!
That is, unfortunately, not such an easy thing to briefly explain... or any brief explanation probably wouldn't be too helpful :). Object are instances of classes and methods are functions you can all on that object. For example, you might have a class called Dog, then you create two dog objects - $brownDog = new Dog(); $blackDog = new Dog();
. Then, you can call methods on each dog - like making one wag its tail and making another bark - $brownDog->wagTail(); $blackDog->bark();
That's the brief (and probably not too-helpful) description. For a proper explanation, we have a track with various courses, which I know is a bigger commitment, but it's the best way to explain :) https://symfonycasts.com/tracks/oo
Cheers!
Hello everyone. Does anyone know something about the behavior when you do not redirect after submitting a form and after refreshing the page it submits again? Tried to google, but can't find an exact information about why the data is submitting again (does a POST request at the time when I'm trying to make a GET request by just refreshing a page).
Hey horlyk
Yeah it's a pretty weird situation, and it totally depends on your browser, some of them shows confirm eg. resend POST request or not, but some just resend previous request completely. You can refresh page without resending POST with clicking on address bar and hitting enter key. Or use redirects after successful form submission.
Cheers!
<b>Goal:</b>
Hi SF Team!
I'm coding a "add a new comment to an article" via Ajax via SF Forms.
To save an comment, SF wants:
comment content
author entity
article entity
In my form I have one field:
comment content
The other two (author & article entity) are set inside my newComment() method:
/**
* This method creates a new comment
*
* @Route("/article/{slug}/comment/new", name="article_comment_new", methods={"POST"})
*/
public function newComment(Article $article, Request $request, EntityManagerInterface $em)
{
$articleCommentForm = $this->createForm(ArticleCommentFormType::class);
$articleCommentForm->handleRequest($request);
if ($articleCommentForm->isSubmitted() && $articleCommentForm->isValid()) {
/** @var articleComment $articleComment */
$articleComment = $articleCommentForm->getData();
$articleComment->setArticle($article);
$articleComment->setAuthor($this->getUser());
...
<b>Problem:
</b>isValid() always fails, because SF Forms want to have Article & Author to be populated.
But both of them are filled AFTER this check, like in the tutorials on SymfonyCasts.
<b>Possible solution:
</b>I think the problem relates to the new auto validation since SF 4.3:
<a href="https://symfony.com/blog/new-in-symfony-4-3-automatic-validation">Source</a>
I currently have change my annotation of "author" and "article" in my ArticleComment Entity like that:
/**
* Custom Code:
* Needed for SF Forms to ignore this field (Otherwise "data.field should not be null" error gets thrown
* @Assert\DisableAutoMapping()
*
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="recipeReviews")
* @ORM\JoinColumn(nullable=false)
*/
private $author;```
With that, isValid() returns true and everything works as expected.
What I don't like about this method is, if at any time "author" or "article" aren't set, SF throws an "insert into failed" exception instead of a "author should be set" one.
<b>Question:
</b>What is the proper way to handle this situation?
Is it correct to disableAutoMapping() and leaving the error handling as it is?
Or is there any "@Assert()" rule which I can apply (That isValid() still returns true, but when $em->persist it throws a error that author have to be set)?
<u><b>//UPDATE:</b></u>
I think Ive found the correct solution: Just create a new ArticleComment Entity and prefill this with the data I want:
/**
* This method creates a new comment
*
* @Route("/article/{slug}/comment/new", name="article_comment_new", methods={"POST"})
*/
public function newComment(Article $article, Request $request, EntityManagerInterface $em)
{
$articleComment = new ArticleComment();
$articleComment->setArticle($article);
$articleComment->setAuthor($this->getUser());
$articleCommentForm = $this->createForm(ArticleCommentFormType::class, $articleComment);
$articleCommentForm->handleRequest($request);
if ($articleCommentForm->isSubmitted() && $articleCommentForm->isValid()) {
/** @var articleComment $articleComment */
$articleComment = $articleCommentForm->getData();
...
<u><b>The DisableAutoMapping Annotation is now no longer necessary.
Is this the correct & best solution?</b></u>
Hey Mike P.
I like to only add validations to fields that get populated via a form (or from the body of a POST request), so in this case, since your code is in charge of setting the user on the comment, I would disable the validation for that field and I would create a "manager" service that would be in charge of creating new comments
Cheers!
I've added a new Function to Twig called "icon" using the AppExtension class created in a previous lesson, just for fun:
public function getFunctions()
{
return [
new TwigFunction('icon', [$this, 'processFontAwesomeIcon'], ['is_safe' => ['html']]),
];
}
public function processFontAwesomeIcon($icon)
{
return sprintf('<span class="fa fa-%s"></span>', $icon);
}
And it can be used in this way:
<td>{{ icon(article.published ? 'check' : 'times') }}</td>
I guess I could pass a prefix (green-check or red-times) or add a second argument to pass a custom color or class to the icons.
Also I think it could be a good idea to either escape or validate the icon parameter.
Hey Duilio,
Nice job! I really like it ;) Anyway, a good way to practice with Twig extensions! And nice discovering that "is_safe" option, though be careful about it - it's OK while you understand how it works and that users does not have access to pass those "$icon" argument. Otherwise it might be an XSS injection from malicious users. But as far as users can't affect on the argument value it should be good, i.e. when you explicitly passing arguments like in your example: "{{ icon(article.published ? 'check' : 'times') }}" but NOT sometimes like "{{ icon(user.customField) }}" where customField might be any string that user may set. Otherwise, you need a really good validation and escaping
Cheers!
Thank you, victor. Could you please help me out with the following 2 questions:
- What do you recommend/use to insert data in the database when writing Functional Tests or tests that interact with the database (i.e. unit testing a repository)?
- Do you recommend going to Behat/Mink or using the built in test capabilities in Symfony or it really depends on the project?
Hey Duilio,
> What do you recommend/use to insert data in the database when writing Functional Tests or tests that interact with the database (i.e. unit testing a repository)?
If you interact with DB layer - it's not a unit test anymore, it's integration test. Most of the time I use entity manager to fill the DB with some data for testing. Like create a few entities, persist() them and then do flush(). After this I test my queries and check if they return the correct data. of course, not all the queries, just important ones are tested this way. As an alternative, you can look at https://github.com/liip/Lii... to fill in the DB with some data, depends on your case, but most of the time doing it manually is enough.
> Do you recommend going to Behat/Mink or using the built in test capabilities in Symfony or it really depends on the project?
Hm, it's probably more matter of taste. We love Behat and we use it a lot to tests things in SymfonyCasts. It allows to test even javascript features using Selenium server. And we even have a tutorial about it: https://symfonycasts.com/sc... - so, it is a great tool for testing. But mostly we do functional/integration testing with it. For unit testing, we, of course, using PHPUnit - it's kinda standard. But no matter what tools you use in your project, you just need to be confident in your features and know that those features work because tests are green.
Also, I've not used it a lot yet, but Symfony has a new great tool for testing called Panther: https://github.com/symfony/... - it's new, but you can give it a try because it's cool.
Cheers!
Any Idea why the list page would not be returning all the articles in the database? The last article added is not displayed on the page if I do dump and die on articles before the return statement in the list function all articles are listed in the array Doing a dump inside the twig template results with all articles being dumped but the last one doesn't display in the list.
Yo Neal O.!
Ha! Wow. No, that IS weird. You did a good job debugging to dump() the articles in the controller. So... doing a <code{{ dump(articles) }}` in Twig DOES correctly show all articles? But then, when you loop, suddenly one is missing?
Let me know. Cheers!
Thanks for the reply Ryan. Found out that the last article was hiding behind the made by knp footer so it was listing just hiding.
Phew! That's the best possible news :). Thanks for the update.
Any idea how to move the handleRequest() and saving in the database into a service? What's the best approach?
Hey ian
I wouldn't move the logic for handling the request into a service, that's a Controller job. But you can move the rest of the logic into a service. It's easy, you just have to create a new class, add a new method with all the desired logic, and use DI (dependency injection) for injecting all the dependencies that your service needs
Cheers!
How do we handle multiple different forms on the same page? I couldn't find anywhere a documentation.
I have a page with a category name and article list where I want to submit the edit/change category title, and also for each article to add it to another category.
Hey Laura M.
How are you planning to manage those submits? As a traditional website, or you will have like an API where you can submit POST/PUT requests?
Cheers!
At first it was a POST request though a form and actually I found a way to do it. Now I am wondering how you can submit through AJAX that form, process it in the controller and return an appropriate response, is it the same as a normal submit form?
If you don't want to change your backend code, what you can do is to add some JS code that finds the form element, and then triggers the submit action
$('#some-button').on('click', function() {
const myForm = $('#some-form');
myForm.trigger('submit');
});
On the reload of the POST request, it posted the unpublished new article. (5:39 Minutes)
But shouldnt validation fail instead, because the request was posted with the same csrf token as the request before?
Isn't that the whole point of CSRF, to create new strings on every page reload to prevent duplicated posting and XSS (Attackers which used old CSRF Tokens)
Hey Mike,
Haha, good catch :) Well, CSRF token should not be unique for each page load, it should be unique for forms and for user session. I think that's how CSRF protection works in Symfony, though I don't know internal details 100%, just saying at the first sight. The idea of CSRF is not protect from double submit, but protect from malicious user sending a request on behalf of user. For this, it's enough to generate a token that is known to server and user only, even if it's not unique per page load.
I hope it helps.
Cheers!
Hey, what about if we do not submit the form tho the same end point? I got this case: I have a Sonata admin class, which renders a form. That form submits to a controller. I use Symfony 3.4
<b>EDIT:</b> Sorry, I can't figure out why indentations are wrong. Code is in a code block :/
`
class MySuperAwesomeAdmin extends BaseAdmin
{
public function myForm()
{
$form = $this->formFactory
->createBuilder(MyFormType::class)
->setAction(
$this->getRouteGenerator()->generate('my_route',['id' => $entity->getId()])
)
->setData(['status' => 'new'])
->getForm()
;
return $form->createView();
}
}
`
In my controller I got:
`
class TransferManagementController extends Controller
{
public function foo($id, Request $request)
{
$form = $this->createForm(FailTransferType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
dump($form->getData);
}
}
`
But data is empty. I can't figure out why. Oh, yeah, and my form has just a single submit button that POSTs to my controller. Any idea what am I missing?
Hey Tonislav A.!
No worries about the indentation - Disqus has a way to do code blocks - but it's honestly kind of annoying ;).
Ok, I think I know your problem. I noticed that you did ->setData(['status' => 'new'])
on your form when rendering it. That is totally legal and fine. However, when you do this, all you're doing is setting the data onto the Form object during that request. Typically the data would then be used by the form to pre-fill some of your fields (e.g. you set 'firstName' => 'Ryan'
and then the first name input by has "Ryan" in it). But, in your case, it sounds like you're not rendering any fields - just a submit button. So, when your form submits... it really is submitting absolutely no data.
If you want to send some sort of "flag" to the controller that's handling the submit, I'd recommend either (A) putting it in the URL - e.g. /my/custom/controller/{status}
or (B) possibly as a `HiddenType</code field.
Let me know if this helps!
Cheers!
Hi,
How can we use knp-paginator & knp-markdown-bundle for listing ... a list ?
i have this
public function AdminAdvertList(Advert $advert, EntityManagerInterface $em, PaginatorInterface $paginator, Request $request, MarkdownInterface $markdown)
{
$q = $request->query->get('q');
$repository = $em->getRepository(Advert::class);
$queryBuilder = $repository->getWithSearchQueryBuilder($q);
$mk = $markdown->transform($advert->getContent());
$pagination = $paginator->paginate(
$queryBuilder,
$request->query->getInt('page', 1),
10
);
return $this->render('admin/advert/index.html.twig', [
'pagination' => $pagination,
]);
}
doesn't seem to work anymore after updating symfony
Unable to guess how to get a Doctrine instance from the request information for parameter "advert".
Hey Ad F.!
Ah! I think the problem isn't the paginator, but Doctrine (well really SensioFrameworkExtraBundle) is unable to figure out how to query for your "Advert" entity. My guess is that you have a route that includes a {advert}
wildcard, right? When SensioFrameworkExtraBundle sees the Advert $advert
type-hint, it tries to query for this Advert
object. Unless you have extra configuration, it tries to see if there is a property on your entity called "advert". If there is not, then it can't figure out how to query for your entity object. This is why I often us {id}
in my route - because the property is called "id", this "param converter" feature from SensioFrameworkExtraBundle knows how to query.
But also... this page looks like it is listing many adverts. Did you intend/want to query for a single Advert? I see you'r using it to transform some markdown... but then you don't use that $mk variable. Let me know!
Cheers!
"Houston: no signs of life"
Start the conversation!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.10.2
"doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.7.2
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.8.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.1
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.1.6
"symfony/cache": "^3.3|^4.0", // v4.1.6
"symfony/console": "^4.0", // v4.1.6
"symfony/flex": "^1.0", // v1.21.6
"symfony/form": "^4.0", // v4.1.6
"symfony/framework-bundle": "^4.0", // v4.1.6
"symfony/property-access": "^3.3|^4.0", // v4.1.6
"symfony/property-info": "^3.3|^4.0", // v4.1.6
"symfony/security-bundle": "^4.0", // v4.1.6
"symfony/serializer": "^3.3|^4.0", // v4.1.6
"symfony/twig-bundle": "^4.0", // v4.1.6
"symfony/validator": "^4.0", // v4.1.6
"symfony/web-server-bundle": "^4.0", // v4.1.6
"symfony/yaml": "^4.0", // v4.1.6
"twig/extensions": "^1.5" // v1.5.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.6
"symfony/dotenv": "^4.0", // v4.1.6
"symfony/maker-bundle": "^1.0", // v1.8.0
"symfony/monolog-bundle": "^3.0", // v3.3.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.6
"symfony/stopwatch": "^3.3|^4.0", // v4.1.6
"symfony/var-dumper": "^3.3|^4.0", // v4.1.6
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.1.6
}
}
Hi
I created a form with a field "task" where I want to choses one from the entity task.
First the form field task was a EntityType. But with thousand of tasks the form load slower and slower.
So I build the field task as a ChoiceType with a JSONResponse and select2 for searching for the task I want to select.
So far it works. ;-)
But when I submit the form, the validation fails, because the value is not allowed.
So I created this PreSubmitEvent:
And I can save the form with the selected task.
But when there is error in the for validation, the PreSubmitEvent already worked and the field "task" is now an EntityType instead of the ChoiceType for the select2 search.
I searched for the possibility to validate the form in the PreSubmitEvent, but this doesn't seem to be possible?
How can I solve the problem?
Thx a lot an Greeting from Switzerland!
Michael