If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Adding edit was quick! But the entire template is now duplicated. This includes the code to render the form and the other blocks that include the needed CSS and JS files.
First, copy the form rendering code and move that into a new file: _form.html.twig
:
{{ form_start(genusForm) }} | |
{{ form_row(genusForm.name) }} | |
{{ form_row(genusForm.subFamily) }} | |
{{ form_row(genusForm.speciesCount, { | |
'label': 'Number of Species' | |
}) }} | |
{{ form_row(genusForm.funFact) }} | |
{{ form_row(genusForm.isPublished) }} | |
{{ form_row(genusForm.firstDiscoveredAt) }} | |
<button type="submit" class="btn btn-primary" formnovalidate>Save</button> | |
{{ form_end(genusForm) }} |
Paste it here.
In edit, just include that template: include('admin/genus/_form.html.twig')
:
... lines 1 - 22 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Edit Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Copy that, open new.html.twig
, and paste it there:
... lines 1 - 22 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>New Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Ok, I'm feeling better. Refresh now: everything still looks good.
And by the way, if there were any customizations you needed to make between
new and edit, I would pass a variable in through the second argument of the include
function and use that to control the differences.
So let's fix the last problem: the duplicated block overrides.
To solve this, we'll need a shared layout between these two templates. Create a new
file called formLayout.html.twig
. This will just be used by these two templates.
Copy the extends
code all the way through the javascripts
block and delete it
from edit.html.twig
:
... lines 1 - 2 | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>Edit Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Paste it in formLayout.html.twig
:
{% extends 'base.html.twig' %} | |
{% block stylesheets %} | |
{{ parent() }} | |
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/css/bootstrap-datepicker.css"> | |
{% endblock %} | |
{% block javascripts %} | |
{{ parent() }} | |
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.6.0/js/bootstrap-datepicker.min.js"></script> | |
<script> | |
jQuery(document).ready(function() { | |
$('.js-datepicker').datepicker({ | |
format: 'yyyy-mm-dd' | |
}); | |
}); | |
</script> | |
{% endblock %} |
So this template itself will extend base.html.twig
, but not before adding some
stylesheets and some JavaScripts. In edit, re-add the extends
to use this template:
admin/genus/formLayout.html.twig
:
{% extends 'admin/genus/formLayout.html.twig' %} | |
... lines 2 - 14 |
Copy that, open new.html.twig
and repeat: delete the javascripts
and stylesheets
and paste in the new extends:
{% extends 'admin/genus/formLayout.html.twig' %} | |
{% block body %} | |
<div class="container"> | |
<div class="row"> | |
<div class="col-xs-12"> | |
<h1>New Genus</h1> | |
{{ include('admin/genus/_form.html.twig') }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Try it! Cool! We're using our Twig tools to get rid of duplication!
Congrats team - that's it for our first form episode. You should feel dangerous. Most of the time, forms are easy, and amazing! They do a lot of work for you.
Let me give you one last word of warning: because this is how I see people get into trouble.
Right now, our form is bound to our entity and that makes this form super easy to use. But eventually, you'll need to build a form that does not look exactly like your entity: perhaps it has a few extra fields or is a combination of fields from several entities.
When you run into this: here's what I want you to do. Don't bind your form to your
entity class. Instead, create a brand new class: I usually put these classes inside
my Form
directory. For example, GenusModel
. This class will have the exact
properties that your form needs.
Bind this class to your form and add all your validation rules like normal.
After you submit your form, $form->getData()
will return this other object.
Then, it'll be your job to write a little bit of extra code that reads this data,
updates your entities - or whatever else you need that data for - and saves things.
If you have questions, let me know in the comments.
There's certainly more to learn, but don't wait! Get out there and build something crazy cool!
Seeya guys next time!
Hi!! First i want to thank you for all this tutorials, they saved me!!
i'm facing a problem and i cannot find the solution anywhere.Just because i wanted to have the buttons save, back and delete in the same row, i'm now trying to get the delete method in the edit form. I tried many things like changing the edit methods in the controller but even when i don't get error, the delete button don't delete anything. Could you tell me if it 's possible to do that or if there is another solution? (i don't use the show template, from the index, i go directly to the edit form) I've tried also to include de delete form from the index, same problem...
I hope you'll have something to propose me!
Cheers!
Ethel
Hey Ethel,
Hm, I think you can achieve it with some CSS rules, for example you can change some block elements to inline elements, see "display: inline-block;" CSS property. You just need to apply this rule to the block elements (wrappers) around your buttons. That's one of possible solutions.
Another solution would be probably to render all your buttons manually with form_widget() Twig function, e.g. "{{ form_widget(form.saveButton) }}". This may render only the button HTML code without block wrappers around that will place your buttons in one line. But if the 2nd solution does not work - probably the 1st solution would be easiest.
The 3rd solution would be to override the way how buttons renders for your templates, see form themes: https://symfony.com/doc/cur... - but it's more complex though correct way to do this.
I hope this helps!
Cheers!
Hi Victor, thank you for your answer! I have tried with your second solution as it seems to be the appropriate but i still have a problem to apply the delete method to the button. I'll try to explain you better:
In my template _form, i've added an <a> before the form_widget so that i could call the delete method :
//...
<div class="row info__buttonRow">
<a href="{{ path('profil_index') }}">
<button type="button" class="btn btn-group btn-dark info__button">Retour</button>
</a>
<button class="btn btn-group btn-dark info__button">{{ button_text }}</button>
<a href="{{ path('profil_delete', {'id': profil.id}) }}">{{ form_widget(profilForm.delete, { 'label': 'Supprimer', 'attr': {'class': 'btn-dark info_button'} } ) }}</a>
</div>
{{ form_end(profilForm) }}```
but i have this error message:
<blockquote>No route found for "GET /moncompte/mestailles/1": Method Not Allowed (Allow: DELETE)</blockquote>
it seems that I cannot call another method from the edit form.
i hope you'll can find the solution!
Thank you! Cheers!
Ethel
Hey Ethel,
Looks like your endpoint where you send your form requires it to be sent as DELETE method request, but you send it as a different method. And I probably know the reason why :) I suppose you're trying add delete button to your edit form - it won't work this way unfortunately, and it's not quite correct. All submit buttons that are inside your form will send the form. But since you're talking about completely different actions and endpoints - you should have completely different forms: e.g. edit form and delete form. In edit form you need all the fields you want to be able to edit... but in delete form it's enough to have only one hidden field with the entity ID you're going to delete. And the problem is that you just cannot put form tag inside another form tag - that's invalid HTML code and it won't work. As a workaround, you can use a simple button (not submit button, or even a link that looks like a button) for deleting where add a JS listener on it and on click send an AJAX request to the DELETE endpoint, e.g. via jQuery. This way you can put both Save edit form button and delete button in the same form.
Well, you can change your delete endpoint to allow post requests for deleting, but that's not quite correct, and it's not necessary to send all the form edit fields to that endpoint just to remove the entity.
I hope this is clear to you now!
Cheers!
I am trying to make a subscribe form which has to be rendered in the base twig so it can be shown in every page. I have made the view part but I am not reaching the controller.
This is the view:
<div class="form-row text-center">
<div class="col-12">
<form name="{{" path('subscribe')="" }}="" method="post">
<input type="text" class="input-field-subscribe" name="subscriber_email" placeholder="Your email" aria-label="Your email" required="" aria-describedby="basic-addon2">
<input class="btn btn-sm btn-outline-dark my-0" type="submit" value="Subscribe">
</form>
</div>
</div>
this is the controller:
/**
* Persists the subscriber.
* Sends email to verify it.
* @Route("/subscribe", name="subscribe")
* @param Request $request represents an HTTP request.
* @param \Swift_Mailer $mailer is the object that makes possible the delivery of the email.
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Exception
*/
public function subscribe(Request $request, \Swift_Mailer $mailer)
{
$subscriber = new Subscribers();
$email = $request->request->get('subscriber_email');
$length = 16;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$random = '';
for ($i = 0; $i < $length; $i++) {
$random .= $characters[rand(0, $charactersLength - 1)];
}
if ($request->request->get('POST')){
if (!filter_var($email, FILTER_VALIDATE_EMAIL)){
$this->addFlash('exception', 'Invalid email format.');
} else {
$subscriber->setEmail($email);
$subscriber->setIsVerified(0);
$subscriber->setToken($random);
$em = $this->getDoctrine()->getManager();
$em->persist($subscriber);
$em->flush();
$this->addFlash('success', 'Verify your email. An email has been sent to you.');
}
}
return $this->render('Subscriber/subscribe.html.twig', [
//'subForm' => $subscriberForm->createView(),
]);
Hey @dave
I think I found your problem. You set up the URL action to a "name" property on your form, it should be set on the "action" attribute
<form action="{{ path('subscribe') }}" ... >
Cheers!
If you inspect your form what's the value of the "action" property? Also, try adding some debug information in your controller method so can double check that the route is being hit
Hello, I did not read the discussion. But for me this what you have showed in this course looks basic and like 80 % I knew. But the problem is that I still struggle with forms. For example recently struggled with form types. Getting various errors and not knowing how to fix, how to debug. Reading symfony documentation also did not help. Like here: https://symfony.com/doc/3.4...
I recently spent like 5 hours and was not able to pass custom variables to template of custom form type. Passed them as html data attribues, but there is options array, so it should be possible to pass them thought that array.
I think you should do a tutorial on this. Or maybe there exists one but I just did not find?
Plus you could make a course of how to debug forms. You can make intentionally various errors which developers have and show a way how we should debug - from the moment when we have no idea how to do it and googling does not help, because there are too many various situations and what you triy from stack overflow answers does not help.
Hi Lijana Z.!
That's all very fair feedback :). We actually *might* have a course that covers what you are talking about - it is https://symfonycasts.com/sc...
It talks about form view variables, how they interact with form type "options" and more. Check it out - let me know if it helps :).
Cheers!
Watched already 11 videos and after this video its clear how it works ! wow. I am sick of symfony writing so bad documenation and so I often hate it for that.
But I know that many companies and good developers use it, so I need to learn it, and I myself often work in companies which use it. You are the example how the documentation should look like.
I do not get why developers have to waste so much time for doing simple stuff like passing a variable to template. Your video should be in symfony documentation. I am also surprised that there are stack overflow questions where people like me struggle and why nobody posts a link to your videos. Maybe its a problem that too little amount of people know it - seo problems? When I searched on stack overflow, I really should have been pointed to watch those videos first and then I believe many problems would have been solved. Now those comments might be useful for seo maybe, but I heard that discuss somehow does not help for seo. And really - tried to search for "Hey guys! My Symfony app is growing in content and my users are requesting a search bar. " which was commented 2 months ago and google does not give the link to this video. I am not sure which fault is that - google or disqus. I really like disqus platform, its so easy to see all your comments, but it really sucks when it does not add to google search. Maybe there is some similar alternative and you should consider?
Good job. Btw, how much time if its not secret you spend to learn those things which you make on those videos with no information? Or you have some secret source of good tutorials for yourself ?:D
Maybe now I should myself update the documentaion which I hate by adding link to your video. But I am not even sure if they accept and I think bit too much beurocracy to contribute, at least it was my first impression when some time ago I tried, so not feeling motivated to do so.
Hey Lijana Z.!
SO glad you're finding the answers you need. And this was a VERY insightful comment - lot's of interesting things here for me! Let me answer a few things:
> I am sick of symfony writing so bad documenation and so I often hate it for that.
I'm glad you like SymfonyCasts. But I'm also the lead contributor to the docs :D. We're actually investing a bit more in it right now, so that we can make it better - I agree that it needs big work! The fun thing about SymfonyCasts is that I can be a bit more opinionated about how I think you *should* do things... instead of showing all the possibilities. The docs need to cater to everyone - it's a tough challenge - but we're working on it :).
> Your video should be in symfony documentation. I am also surprised that there are stack overflow questions where people like me struggle and why nobody posts a link to your videos. Maybe its a problem that too little amount of people know it - seo problems?
This is very interesting :). Of course, I would also like more people to link to SymfonyCasts! But, there may be a simple explanation for the SEO problem - about 3 weeks ago, we completely renamed our domain. Yes, we have 301 permanent redirects, etc. But, I think that our SEO has temporarily (hopefully temporarily) suffered. We've noticed that the site is now coming up way lower in certain search terms, after the domain change. I'm hoping that will correct itself soon, as we've done everything (that I know of) that you're supposed to do when renaming a domain, for a smooth transition.
> Good job. Btw, how much time if its not secret you spend to learn those things which you make on those videos with no information? Or you have some secret source of good tutorials for yourself ?:D
Ha! A lot of time :p. But actually, it's because I work on Symfony itself... so I already spend a lot of time learning it... by contributing to it. However, sometimes when I'm not an absolute expert on something, we'll partner with someone that is. It's all about finding GREAT people who know some technology well, then explaining in simple terms to the rest of us (which often includes me!)
> Maybe now I should myself update the documentaion which I hate by adding link to your video. But I am not even sure if they accept and I think bit too much beurocracy to contribute,
I'm happy to say that there are already links to SymfonyCasts on the documentation. But, there could be more! If there is a spot in the docs where one of our screencasts would fit in nicely, it would be AMAZING if you made a PR. About 3 weeks ago (the domain rename) we became an official partner with Symfony - adding appropriate links is not a problem.
Thanks for the interesting chat!
Cheers!
Oh, hopefully you did not die after my criticism about symfony documentation :) maybe I could have used different words, but that would not express so good how I and probalby other developers feel then :)
I think it is much better to have opinionated information which teaches you to make it work than not opinionated information which causes wasting precious time by trying to figure out how to do a simple thing.
But I agree that it is to show all posibilities.
>it would be AMAZING if you made a PR.
I like more giving ideas what needs to be in the documentation. But I do not writing beatiful texts so that it would fit the documentation :)
At least if there was a mechanism how to earn money from that, I would maybe consider and others probablty would consider. Now I already have lot of fun things to do in my life, I do not feel that much value of using my time for doing something for free. I understand that I could write to my CV that I contributed to documentation, but unless I contribute a ton, it will not raise my value much.
Hey Coder,
I bet Ryan is still alive :) It's always good to get a feedback from developers like YOU, and we have heard you, thanks!
> I like more giving ideas what needs to be in the documentation.
Well, of course, that's much easier :p But actually doing things, and doing it good requires some time, you know ;)
> But I do not writing beatiful texts so that it would fit the documentation...
Ha, that's exactly the problem of many good developers who think so. But we think you're wrong here. Most important in docs is a good example, and developers like you may see such good examples, that will help other developers a lot. Don't think you need to be an excellent writer for contributing to Symfony docs! Anyway, if something sounds to so good as it could be - someone will reword the text or point you how you can improve it. Btw, I'd recommend you to watch our new screencast about contributing to Symfony: https://symfonycasts.com/sc... - it's probably much easier than you think right now :)
> At least if there was a mechanism how to earn money from that, I would maybe consider...
Well, I see your point, and that's fair, we all need to get money someway for our life, but we're talking about Open Source. Symfony IS an open source software, and a lot of people contribute to it for free, and that's exactly what makes Symfony so popular and awesome! But sure, nobody is forced to contribute to open source, so don't worry about it too much, I bet a lot of people like doing so and we'll continuously improve Symfony and its docs little by little.
Once again, thanks for the good conversation!
Cheers!
Hey guys! My Symfony app is growing in content and my users are requesting a search bar. So, i have some questions:
- How can I have a form in every page? Do I need to put the form and logic in a service?
- If the search form is in every page, in some pages I will have more than one form. For example, in my contact page. How can I manage two forms in the same page?
- Maybe you know a good bundle for this? I think this is a common request.
I hope you can help me. I would appreciate any tip.
Cheers!
Hey Cesar
For that task you don't actually need to use Symfony's form system, you can render and process one manually, you only need a text input and a way to submit (by pressing enter key). So, in your template just add a block with that HTML that will be included in your base template (so it will be shown on every page), then the submit action will be handled by a custom controller's action where you will be able to add all the logic that you need.
If you still have doubts about how to implement it, just let us know!
Have a nice day.
Hi Ryan,
I am creating an application and I need to upload an image.
For the moment I did not used a form to get all the other inputs, and I would like to keep it like this, is there an easy way to have a simple file input field and get the file selected (image in my case) in my controller for editing and saving?
Thanks a lot in advance
Hey James!
So, you'd like to process a file upload, but not using the Form component, correct? No problem :). Suppose you have this:
<input type="file" name="myImage" />
Then, in your controller, you can fetch it like this:
public function editAction(Request $request)
{
// an instance of UploadedFile
$uploadedFile = $request->files->get('myImage');
if (!$uploadedFile->isValid()) {
// will by one of the constants like UPLOAD_ERR_INI_SIZE or UPLOAD_ERR_FORM_SIZE
$error = $uploadedFile->getValue();
// ...
}
// note getClientOriginalName may not be safe to trust - user could upload files with any extension
$uploadedFile->move('/some/dir', $uploadedFile->getClientOriginalName());
}
Obviously, you could do a bit more with validation, etc - but this is the basic idea. Let me know if it helps!
Cheers!
I was sure it was that simple, I was really trying too hard.
I tried the solution above and got the error below:
Error: Call to a member function isValid() on a non-object
It does not seem to want to take my picture when I request it, am I missing a "use" statement?
I tried with very small files to be sure it was not php.ini the problem.
And when you valid your file, is it taking the validation parameters from the product entity where the image is?
Also you add a comment above the ->move saying "user could upload files with any extension" but is that not something I would check as part of my validation, ie:
/**
* @ORM\Column(type="string")
*
* @Assert\NotBlank(message="Please, upload the product image as a .JPEG or .JPG.")
* @Assert\File(mimeTypes={ "image/jpeg" })
*/
private $image;
?
Thanks
Hey Connect,
Does your file's input HTML tag have the name attribute (like name="myImage")? Try to dump files to see what you receive:
dump($request->files->all());
You need to find UploadedFile instance there.
Cheers!
Yes, array(0) { }, so I believe nothing.
Twig code:
<input type="file" name="myImage"/>
Then a simple,
<button id="save-all" type="button" class="btn btn-success">Save Product Changes</button>
OK then, are you sending your form with POST method? Does you form has enctype="multipart/form-data"
? Use "multipart/form-data" when your form includes any <input type="file">
elements. I bet the problem with enctype :)
Cheers!
Hey James!
Ok, let's see here. I agree with Victor in the first part! Probably just a forgotten enctype - I do this all the time :).
Second, when you validate your image in this way, it is NOT using the validation rules from your entity - because your file object is never placed on it. In fact, let's back up a little bit - I see that you have an $image
field on your entity, which will probably hold the filename of the file (this is perfect). But, if you were doing a bit more of a traditional, form-based upload, you would actually need a second, non-persisted field - e.g. imageFileObject - and this is where you would set the UploadedFile object into. And more importantly, THIS is where your @Assert constraints would go onto: it's the Uploaded file that you want to validate. But, you're not doing it that way (and totally fine - just wanted clear that part up). So, what I would do is (A) remove the validation constraints from your entity - as they simply won't be used - technically the NotBlank is ok, depending on your setup - as it would be a reminder to actually upload a file - but the Assert\File definitely won't work. Then, (B), validate manually in your controller:
use Symfony\Component\Validator\Constraints\File;
$constraint = new File();
$constraint->mimeTypes = ["image/jpeg"];
// btw, there is also an Image constraint, which is built to only accept images
// $constraint = new Image(); // use statement is same as File, just end with \Image
// now, use the validator service directly - this is basically what the form normally does for you
$validator = $this->container->get('validator');
$errors = $validator->validateValue($uploadedFile, [$constraint]);
if (!empty($errors)) {
// you have validator errors! You can loop over them to see the problem
// https://symfony.com/doc/current/validation.html#using-the-validator-service
}
And finally, you're right that as long as you validate the incoming file as a jpeg, then you should be ok... though technically the user could still upload a valid jpeg... but have named it foo.exe, which is a little odd. It depends on how much you trust your users to not do weird things :).
Cheers!
Is it something that would be available in the JavaScript for PHP Geeks sessions? I would really like to make it work with Ajax.
Thanks
James
Yo Connect,
Yes, you're right! Check the latest released chapters on JavaScript for PHP Geeks - they were released a few days ago: https://knpuniversity.com/s...
Cheers!
Just a quick one as I found the solution to my problem, thanks to a var_dump($request->files->all()); as suggested I did a normal form in my template and the following in javascript:
handleNewFormSubmit: function(e) {
e.preventDefault();
var $form = $(e.currentTarget);
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: new FormData(e.currentTarget),
processData: false,
contentType: false
}).success(function (data) {
var place = '#js-image-detail-preview';
var result = $(data);
$(place).html(result);
});
}
and the following in my controller:
$uploadedFile = $request->files->all()['form']['image'];
if (!$uploadedFile->isValid()) {
// will by one of the constants like UPLOAD_ERR_INI_SIZE or UPLOAD_ERR_FORM_SIZE
$error = $uploadedFile->getValue();
var_dump($error);
// ...
}
$fileName = md5(uniqid()) . '.' . $uploadedFile->guessExtension();
// $cacheManager = $this->get('liip_imagine.cache.manager');
// $cacheManager->remove('assets/images/products/tmp/' . $fileName);
$uploadedFile->move(
$this->getParameter('product_images_preview_directory'),
$fileName
);
$pathImagePreview = 'assets/images/products/tmp/' . $fileName;
All works perfectly now!
Thanks again guys for all your help.
Hey James,
Nice work! I'm glad you got it working! And thanks for sharing this - I just tweaked your message a bit to highlight the code blocks.
P.S. You better don't use IDs for selecting elements like '#js-image-detail-preview' one - use CSS classes for this. ;)
Cheers!
I do not use a form, I wanted to get the file without using the form. I explained it in the first comment.
Thanks
Hey James!
I assumed you meant that you're not using a *Symfony* form. But you must at least use an HTML form... unless you're sending the file via AJAX or something fancy?
Cheers!
Hey weaverryan!
After watching this series I am a little confused about getName method. I was trying to find the moment when you have added this, but there is no one (or I've missed it). Can you please explain it to me briefly what is the purpose of:
public function getName()
{
return 'app_bundle_genus_form_type';
}
@Edit
I have found out that this method was added by Symfony Plugin. Anyway is there any reason for it?
Hey,
Yes, Symfony plugin adds this method for backward compatibility, but you should avoid using it and since Symfony 2.8, the name defaults to the fully-qualified class name, i.e. Symfony generates this name for you behind the scene based on your FQCN. This class name is internal and used for generate unique form element names and CSS IDs. You could find it if inspect your form HTML code.
Cheers!
Hi
I was working on a form with symfony and i now wan't to save data to a table but i wan't to get the data from another table,
ex: Table1 is where i wan't to save main data and i have Table2 related with Table1 but i wan't to get The data from Table3 and save those to Table2, does anyone had this issue and how could this be solved ?
Thanks :)
Hey diarselimi92 ,
You can make relations first and then pass the new object to the createForm() as a second argument, i.e. build form on a object which already has relations with other objects. Look at the next example:
// Use entity manager and repository to fetch any stored data from any tables
$category = $em->getCategoryRepository()->find($categoryId);
$post = new Post();
// Make relations fetched data
$post->setCategory($category);
$form = $this->createForm(PostType::class, $post)
// handle form...
In the example above, the new Post object will be attached to the existent category.
I hope I understand you right and this example helps you. Let me know if you still have any questions.
Cheers!
Hey victor thanks for your response, but i think i can't do that !
i have a table where i have some banks in it but the problem is that i don't have to relate anything to that table i only have to get the banks and save them in another table that is related to the Slide Table
the stucture : | BANK | i will get the data from this bank table, then i have | SLIDE | table where i will save the some slides here , then i have a table | SLIDE_BANK | where i will save every bank that i selected for a | SLIDE | here , so one SLIDE have multiple BANKS. note: I will not save the bank's id but the name in it.
I hope it is understandable.
I know how to do it in the PHP Way but i wan't to go by the symfony's rules.
OK, so speaking in terms, you have many-to-many relationship between Slide and Bank entities. Here's a link about this implementation using Doctrine ORM: https://knpuniversity.com/s... . Is it appropriate for you?
But with this approach, the SlideBank entity will be able to store only bank_id and slide_id. If you want to store a bit more information, like also a bank's name - you probably interested in a bit more complex relationships: Slide entity relates one-to-many to the SlideBank entity, which in turn relates many-to-one to the Bank entity, i.e. as a result you'll have the same many-to-many relationship between Bank and Slide, but you also will have ability to add more fields to the SlideBank entity except required bank_id and slide_id. This way you need 3 entities: Slide, Bank and SlideBank. I hope it makes sense for you.
Cheers!
Hi,
Another question on another subject, I have my login form and register form, all good until I try to create the forgot password form. When the user click on forgot password, an email is sent to his email address with a link, the link has got his email address and a token created just before the email was sent. When the user clicks the link he is redirected to a page with a form to renew his password, same password part of the form than in the register form. But when the user enters the password and the repeat password (I changed the encoder to plaintext):
Code:
$userPassword = $formForgotPassword->getData();
var_dump($userPassword["plainPassword"]);
$user->setPassword($userPassword);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
var_dump($user->getPassword());
die();
Result:
"password" NULL
What is the problem?
Hey James,
I think I see the problem ;). You dump $userPassword["plainPassword"]
string value but then set $userPassword
array to the user object. So try to change 3rd line to:
$user->setPassword($userPassword["plainPassword"]);
Cheers!
I already tried it also, same result:
string(8) "password" NULL
Which I really can't get it...
$userPassword = $formForgotPassword->getData();
var_dump($userPassword["plainPassword"]);
$user->setPassword($userPassword["plainPassword"]);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
var_dump($user->getPassword());
die();
Ah, OK. So the correct way is pass the string value instead of array to the setter.
The best practices is to use a "$plainPassword" property on User entity and add an event listener to encode the plain password to the password hash before update its value in DB. In the event listener you should encode plain password if only the $plainPassword property is NOT null. The only notice, that you should also change any mapped field of User entity, otherwise you will not get in the listener (it's because the $plainPassword is not mapped to the DB usually). So when you handle your form and set the new password to the $planPassword, you also should manually change $updatedAt field too (or any other mapped field, but usually, it's a $updatedAt field). It will be enough to trigger your event listener which in turn encode plain password and set its hash to the mapped $password property. But, first of all ensure you get the password inputed by user when handle the form.
BTW, do you have any listeners which could update user entity after you? Check them all and ensure they do not update password field.
P.S. You could see how to handle (encode) plain password with an entity listener in the next course Symfony Security: Beautiful Authentication, Powerful Authorization. Please, take a look at it.
Cheers!
Hi guys. I am using the form component and it is working really well. But now I need to render 3 times the "same" form in the same page. The only difference between those three are the options in the EntityType because it depends of what the user choose. Any suggestion that how I can do it? I don't want to duplicate code and I don't know how to manage different request in the controller. I hope you can oriented me.
Hey Cesar!
Hmm. I will say 2 things :).
1) You can create a single Form class that is capable of creating all 3 forms. The tricky part is: inside buildForm
, you need to know which form you are building so that you can configure the EntityType correctly. To do that, you can add a custom option to your form - e.g. entity_user_option
(that's a terrible name, but I don't know anything about the differences between the 3 forms... so I just invented this name). How do you add a custom option to your form? It's something like this: https://gist.github.com/weaverryan/e8a6d3c3a9ecf2b6d46e937e8dd98ec4. There is a minor problem still: you will have duplicated HTML id's. If that's real problem, I think we could hack around it :).
2) For how to handle the submit in the controller, can you tell me why you have 3 forms on the page? Will the user complete all 3 forms? Or will they only complete one of the forms based on some input?
Cheers!
Hi Ryan. Thanks for answer me. Imagine there are three buttons on the page and when you clic on each of them it will appear a form in a pop-up (modal). The only difference between those three are the options in one field. So, the url can not change (it's the same page) and the user only need to save one form depends in which button he chose. Do you have any idea how to solve this? I will appreciate your help.
Hey @Cesar
How familiar are you with Javascript? Because what I would do is, request that form (via AJAX) whenever a user clicks on a button, then you could add a data attribute to the button to distinguish which button was pressed, so you can choose correctly which form to render or process.
Cheers!
Hi Diego. Even if I use Javascript, how I can handle the Request in the Controller? Do you have an example of this? It will be really helpful. Also, there is no way to render more than one form in the same page using Twig or the Form Component?
Don't worry anymore Diego. I found this https://symfony.com/doc/3.3... and I have used it. I am not sure that I understood everything but it's working in my application. Thanks for your help.
Hey @Cesar!
Sorry for the late response, I'm happy to hear that you managed to overcome your problem. About rendering multiple forms in a Twig template, that's totally possible and is ok to do it.
If you have more doubts you can contact us any time :)
Cheers!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
"doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0", // v3.1.3
"nelmio/alice": "^2.1", // 2.1.4
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}
Gosh I love these tutorials. How come I never knew about them before... D: In 2 days, I'm almost at the end (only the next course is left). Be proud of yourself. And be proud of what you're doing Ryan! I'll pass on the word about these tutorials.