Buy

Hey guys! You're back! Awesome! Because this tutorial is all about forms: the good, the bad, and the ugly.

Quit Hatin' on the Forms

The truth is: the form component is super controversial: some people love it, some people hate it - and a lot of people have trouble learning it. In fact, its documentation on symfony.com is read far more than any other section.

Why? Because honestly, it is complex - too complex sometimes. But you know what? The form component is going to allow you to get a lot of work done really quickly. And when I do see someone suffering with forms, most of the time, it's their fault. They create situations that are far more complicated than they need to be.

So let's not do that - let's enjoy forms, and turn them into a weapon.

Sing Along Code along!

Ok, you guys know the drill: download the course code and unzip it to code along with me. Inside, you'll find the answers to life's questions and a start/ directory that has the same code I have here. Make sure to check out the README for all the setup details.

Once you're ready, start the built-in web server with:

./bin/console server:run

I made a few changes to the site since last time. For example, go to localhost:8000/genus. It looks the same, but see the "Sub Family"? Before, that was a string field on Genus. But now, I've added a SubFamily entity:

40 lines src/AppBundle/Entity/SubFamily.php
... lines 1 - 6
/**
* @ORM\Entity
* @ORM\Table(name="sub_family")
*/
class SubFamily
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $name;
... lines 24 - 38
}

And created a ManyToOne relation from Genus to SubFamily:

119 lines src/AppBundle/Entity/Genus.php
... lines 1 - 11
class Genus
{
... lines 14 - 25
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\SubFamily")
* @ORM\JoinColumn(nullable=false)
*/
private $subFamily;
... lines 31 - 117
}

So every Genus belongs to one SubFamily.

I also started a new admin section - see it at /admin/genus. But, it needs some work - like the ability to add a new genus. That'll be our job. And the code will live in the new GenusAdminController.

Creating a new Form

To create a form, you'll add a class where you'll describe what the form looks like.

Tip

Actually, you can build a form directly in the controller if you want to.

In PhpStorm, select anywhere in your bundle and press command+N, or right click and select "New". Find "Form" and call it GenusFormType:

21 lines src/AppBundle/Form/GenusFormType.php
... lines 1 - 2
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
public function configureOptions(OptionsResolver $resolver)
{
}
}

Cool! This just gave us a basic skeleton and put the class in a Form directory. Sensible! These classes are called "form types"... which is the worst name that we could come up with when the system was created. Sorry. Really, these classes are "form recipes".

Here's how it works: in buildForm(): start adding fields: $builder->add() and then name to create a "name" field:

25 lines src/AppBundle/Form/GenusFormType.php
... lines 1 - 8
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
... lines 15 - 16
;
}
... lines 19 - 23
}

Keep going: add('speciesCount') and add('funFact'):

25 lines src/AppBundle/Form/GenusFormType.php
... lines 1 - 12
$builder
->add('name')
->add('speciesCount')
->add('funFact')
;
... lines 18 - 25

Right now, those field names can be anything - you'll see why in a second.

And that's it! The form is built after writing about 4 lines of code! Let's go render it!

Leave a comment!

  • 2017-07-19 deskema

    TY Ryan!
    Yes, i already tried to install that bundle via composer but seems there are several huge problems with Symfony Framework 3.
    I quit almost immediately...
    Actually I was exactly looking for the 'official' solutions :)

  • 2017-07-19 weaverryan

    Hey deskema!

    Hmm, I've never seen that bundle before, but that doesn't mean it's good or bad :). Though, it only has 1 start on GitHub... so at the very least, it is not a common bundle! So, beware.

    The more official solution would be to use DoctrineExtension's Tree (https://github.com/Atlantic... via StofDoctrineExtensionsBundle (https://symfony.com/doc/cur.... That is a very robust solution... though the documentation sometimes kinda sucks :).

    Let us know what you find out!

    Cheers!

  • 2017-07-19 deskema

    Hi guys.
    I was wondering if you have ever used this bundle to create a nested "whatever" data set:

    https://packagist.org/packa...

    Few years ago I wrote a class to generate 'generic' trees...
    With term "Generic" I mean a class to generate hierarchical structure independently from the context (kind of OTB) for example:

    - e-commerce => Categories/SubCategories/...
    - CMS => Pages/SubPages/...
    - Country selectors => Country/Nation/Region/City/Venue/...

    and so on..

    In other words a very generic class which was serviceable to different contexts.
    The entity class of the example in README is 'Category' and it's referred to the table acme_category.
    I suppose it's enough to rename the Entity to something else like Pages, extend it with BaseCategory and associate it with a new table like for example acme_page.
    I will try to play a bit with this Bundle but I would appreciate some extra suggestions (and eventually remarks if I said something wrong) from your side.

    TY in advance :)

  • 2017-06-14 Victor Bocharsky

    Hey Darkfaythe,

    No problem ;) I'm glad you had found the mistake before I answered you.

    Cheers!

  • 2017-06-13 Darkfaythe

    Ummmmmm, duh!, my bad.... If I had expanded the view of admin in storm I would have seen the file you are referring to. Oh god, what a newbie mistake.....

    Thanks for your awesome courses.

    Cheers

  • 2017-06-13 Darkfaythe

    Just downloaded and setup the updated code for the forms tutorial and its all working fine BUT in the code you have a GenusController.php and in the vids you have GenusAdminController.php. Is this someting we will be changing or do I just go with it for now?

    Cheers.

  • 2017-06-05 Diego Aguiar

    Hey Robert Went!

    Thanks for the ping, it is already fixed :)

    Have a nice day

  • 2017-06-04 Robert Went

    There seems to be an issue with the current code download:

    /start/src/AppBundle/Entity/GenusNote.php

    The GetId method is declared twice; once at the top of the class and again at the bottom. Removing one fixes the error.

  • 2017-04-29 Julia Shishik

    You inspire me! Thank you!

  • 2017-04-28 Victor Bocharsky

    Hey Julia,

    Haha, I'm glad you had got it working before I replied to you - good investigation! ;)

    Cheers!

  • 2017-04-27 Julia Shishik

    Thank you very very much! I understood everything!!! The problem was in fixtures.yml I did not register the data !!! Thanks again!! Hooray!

  • 2017-04-27 Julia Shishik

    In the console i type '$ php bin/console doctrine:fixtures:load' and then i have an error
    > purging database
    > loading AppBundle\DataFixtures\ORM\LoadFixtures

    [UnexpectedValueException]
    Entity with Id Culpa consequatur. and Class AppBundl
    e\Entity\SubFamily not found

  • 2017-04-26 Victor Bocharsky

    Hey Julia,

    Could you try to reload fixtures? Does it fix your problem?

    Cheers!

  • 2017-04-25 Julia Shishik

    When I open the page 'http://localhost:8000/genus' the colums of Genus, # of species and Last updste are empty!

  • 2017-04-25 Victor Bocharsky

    Hey Julia,

    What do you mean? Are you sure you have the information in your DB? Could you explain us a bit what are you doing to get this information?

    Cheers!

  • 2017-04-25 Julia Shishik

    I solved the problem, but now I have no information from the database how to return it? Sorry(((

  • 2017-04-25 Julia Shishik

    Good morning!
    I have an error:
    Class AppBundle\Form\GenusFormType does not exist in D:\OpenServer\OpenServer\domains\My_site\my_project_name\src\AppBundle/Controller/ (which is being imported from "D:\OpenServer\OpenServer\domains\My_site\my_project_name\app/config\routing.yml").
    500 Internal Server Error - FileLoaderLoadException
    1 linked Exception: ReflectionException »

    [2/2] FileLoaderLoadException: Class AppBundle\Form\GenusFormType does not exist in D:\OpenServer\OpenServer\domains\My_site\my_project_name\src\AppBundle/Controller/ (which is being imported from "D:\OpenServer\OpenServer\domains\My_site\my_project_name\app/config\routing.yml"

    I dont't understend why? I have created the GenusFormType.php
    Help me please!

  • 2017-04-22 weaverryan

    Yo Chantha Ben!

    Can you give me a bit more information about what you're trying to do? Do you want to translate a form label in your controller? Or do you want to get a specific submitted value from your form and then translate it? Let me know and we'll see if we can help :).

    Cheers!

  • 2017-04-22 Chantha Ben

    hi guys , how to get value from Formtype(translation) in controller

  • 2017-01-25 Victor Bocharsky

    Yeah, I see what you mean. Actually, it works too! IMO, I just think a custom Twig function will be more "elegant" solution here.

    Cheers!

  • 2017-01-25 Dan Costinel

    Thanks for replying.

    Yes, someone else suggest me to use a Twig filter too, and I really want to replace my current solution I've found, after I posted this question. And the solution was to create a service which returns the number of unread messages, and then make it available in twig(as gloabal variable), in app/config/config.yml, under twig: - globals: - - count_messages: '@app.count_messages'. Then I was able to call {{ count_messages.getMessagesNumber() }} method from my service.

  • 2017-01-25 Victor Bocharsky

    Hey Dan,

    The `render(controller('...'))` solution is great when you use ESI cache on your website, otherwise better avoid it since it sends a sub-request, which takes some time. I'd recommend you to write a custom Twig function (or filter) for your case, e.g. `count_messages()`, then you could easily call it from `_includes/header.html.twig` like "{{ count_messages() }}" and that's it! It's a nice and effective solution of your issue I think. Here's an example how to write custom Twig filter: https://knpuniversity.com/s... - but the same for custom Twig functions, just use "function" instead of "filter". Also you might want to look at Symfony docs: http://symfony.com/doc/curr... .

    Let me know if it makes sense for you or if you have some problems with writing custom function ;)

    Cheers!

  • 2017-01-24 Dan Costinel

    Hi guys!

    I'm having a little twig inheritance like: _includes/header.html.twig which contains all the bootstrap navbar html. This template is included in each and every template for each pages. So index.html.twig, about.html.twig, and contact.html.twig will have this {% include '_includes/header.html.twig' %} at the top.

    Now, my problem is that I want to count the unread messages from the database, and send that count number to that header.html.twig. If there are 3 unread messages, in header I want to have an anchor like Messages ({{ messages }}). So this will render like Messages(3).

    I've tried a lot of options I've found on various articles (like render(controller('AppBundle:Message:getMessages')), or return $this->renderView('_includes/header.html.twig', ['messages'=>count($messages)]) --- this might work, but I really don't know how to use it), but none worked or didn't applied for my case.

    Any suggestion?

    Thank you!

  • 2016-11-23 Victor Bocharsky

    Anyway, if you start updating your schema with "doctrine:schema:update --force" - then you can't use "doctrine:migrations:migrate" further because it will always fail. You need manually skip some migrations you already don't need with "doctrine:migrations:execute", or just drop your DB, create it again and run migrations from scratch.

    Cheers!

  • 2016-11-22 somecallmetim27

    I'd already run that (several times, actually) to no avail. But it didn't work, sadly :(

  • 2016-11-21 Victor Bocharsky

    Hey somecallmetim27 ,

    I'm glad you found solution yourself quicker than I answered it ;) BTW, we have Doctrine migrations in this course, so `$ bin/console doctrine:migrations:migrate` should help in this case too.

    Cheers!

  • 2016-11-20 somecallmetim27

    Nvm. Needed to run bin/console doctrine:schema:update --force

  • 2016-11-20 somecallmetim27

    Put the updated downloaded code into my project, but now I keep getting the following error when I try to load fixtures.

    SQLSTATE[42S02]: Base table or view not found: 1146 Table 'aqua_note.genus_note' doesn't exist.

    I've tried dropping the database, creating, migrating then attempting to load fixtures several times to no avail.

    The GenusNote.php class absolutely exists and is appropriately named @ORM\Table(name="genus_note")

    So I'm not sure what's going wrong. Everything worked before putting in the downloaded code (I've done the tutorials in order).

  • 2016-11-18 Terry Caliendo

    Ah! Thanks much!

  • 2016-11-18 Victor Bocharsky

    Hi Terry,

    haha, it's a tricky error - in short, you should assign value which returned by getForm() to the $form variable:


    $form = $form->getForm();

    If you look closely, you will see that you do it in the second example ;)

    Cheers!

  • 2016-11-18 Terry Caliendo

    I want to conditionally add fields but don't seem to be able to break the form creation down.

    I was hoping to do the following:

    $form = $this->createFormBuilder($MyObject);
    $form->add('Name');
    if ($testFlag){
    $form->add('MyExtraObjectField');
    }
    $form->getForm();

    But I get the error:
    Attempted to call an undefined method named "handleRequest" of class "Symfony\Component\Form\FormBuilder

    Why can't I break this (which works) down into the pieces above:

    $form = $this->createFormBuilder($MyObject)
    ->add('Name')
    ->add('MyExtraObjectField')
    ->getForm();

  • 2016-11-14 Victor Bocharsky

    Well, I can only suggest you to look to the side of third party bundles, probably someone already done something similar, but I don't know though. But ask yourself the question: is it worth efforts to do that? Do you really want that new fields appear automatically in your form? I think it will bring more problems than it solves. What about annotations, what will you do if you need to build more than one form type for the same entity? Will these annotations work in this case? It's my opinion at first glance, but I could be wrong! Anyway, if you see some profit in it and you can't find a bundle with the similar functionality - feel free to create your own. ;)

    Cheers!

  • 2016-11-14 Terry Caliendo

    Thanks for the reply. My next question if it was possible would have been is there a way to use annotations to not render one or two of the fields.

    In some cases it seems like it could save a lot of work. Say you have an entity with 10 fields and only one of those is internal. It would be a lot less work to tell Symfony to generically render all the fields and exclude the one internal field, as opposed to having to write the "add" code for 9 fields.

    Just a thought I had in trying to understand the fastest ways to get work done.

    Thanks again!
    tc

  • 2016-11-14 Victor Bocharsky

    Hey Terry,

    There's no such feature out-of-the-box. The problem is that in the most cases you don't need to render all properties of your class, some of them could be internal properties, some of them should be customized when you build a form. So it's a very rare case when you render all fields of a class. And what about the new field, which you could want to add in the future? Will you really need to add this field automatically and don't customize it? So I think it's not a good idea to do so.

    Cheers!

  • 2016-11-12 Terry Caliendo

    Not sure where to ask this and I may have missed it... but it seems like you rely on this manual addition of the individual fields throughout the this course.

    I'm using the createFormBuilder() function within a controller:

    $form = $this->createFormBuilder($MyEntity)
    ->add('myProperty1')
    ->add('myProperty2')
    ->add('myProperty3')
    ->getForm();

    Instead of calling ->add(...) on the $builder object for each of the properties, is there a way I can just ask the form builder to return all the properties of the form? something like:

    $form = $this->createFormBuilder($MyEntity)->addAll();

  • 2016-10-30 weaverryan

    Yo Daan!

    Thanks! Really happy the courses are useful :).

    About your error: sometimes these can be tricky with the Alice fixtures, because you can't fully see what's going on :). But, here's what the error tells me: a SubFamily object is probably being set onto a property of some other object (e.g. Genus) via the whole Alice '@' symbol reference thingy. But, that property (on Genus) is mapped as a *string*, not a relationship. So Doctrine is trying to convert the SubFamily into a string to save it to the database. This isn't the easiest error to debug, but there it is :). Btw, it wouldn't help here a TON, but if you pass -vvv to any command, you can see the full stacktrace of the error.

    The cause is that I also changed the Genus.subFamily property between tutorials (it makes for a better example in this tutorial). So just go copy that change from Genus as well, and you should be fine :).

    Cheers!

  • 2016-10-28 Daan Hage

    He Weaverryan,
    So far I love everything! Great courses. keep it up! :)

    I got a problem with this startup. I copied the new files, dropped, created, and migrated doctrine.
    Now I wanted to load the fixtures (I copied your file). However I get the following:

    bin/console doctrine:fixtures:load
    Careful, database will be purged. Do you want to continue y/N ?y
    > purging database
    > loading AppBundle\DataFixtures\ORM\LoadFixtures

    [Symfony\Component\Debug\Exception\ContextErrorException]
    Catchable Fatal Error: Object of class AppBundle\Entity\SubFamily could not be converted to string

    doctrine:fixtures:load [--fixtures [FIXTURES]] [--append] [--em EM] [--shard SHARD] [--purge-with-truncate] [--multiple-transactions] [-h|--help] [-q|--quiet] [-v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-e|--env ENV] [--no-debug] [--] <command>

    Could you help me with this error? :S

  • 2016-10-24 weaverryan

    Hey Stephane!

    Hmm, that's odd - but I think we can figure it out :). First, the {{ genus.subFamily.name }} is not a mistake - that's the correct starting code in the template. The reason this should work is that the src/AppBundle/Entity/Genus.php entity class has a subFamily field that is not a string field, but is a ManyToOne relationship to the SubFamily entity. If you were using some existing code from an earlier tutorial, did you update the Genus.php file also from the start directory? I'm asking because in the earliest tutorials, the subFamily property *is* a string, but I updated it to be a proper relationship before this tutorial so that the form tutorial could be more interesting (it's one of the few changes that I make *between* tutorials - so there's no video of it).

    Let me know if this helps! Cheers!

  • 2016-10-22 Stephane

    Ok, I think there is a mistake in the "show.html.twig" file in the start directory.
    I modified the line 13 to : <dd>{{ genus.subFamily }}</dd>. And now it works.
    Can anyone explain why it works like above and when I write : <dd>{{ genus.subFamily.name }}</dd>, it gives the error message.

    Stephane

  • 2016-10-22 Stephane

    Hello, I am starting this course, and I've copied the start directory in my test project.
    When I click on a name in the list to show details about one genus, I've got the following Twig error :

    Impossible to access an attribute ("name") on a string variable ("West") in genus/show.html.twig at line 13

    Stephane

  • 2016-07-12 叶朱丽

    Hi Ryan!
    Now I see why....
    Thanks a lot for your explanation!

  • 2016-07-12 weaverryan

    Hi Giulietta!

    Actually, this error is ok! When we create the form in this tutorial, the URL is actually /admin/genus/new. When you go to /genus/new, you're executing a different controller - and this controller (at this point in the tutorial) is old and outdated (I actually forgot it was there - you should ignore it). I think the missing "New" button is the same problem - try going to /admin/genus (instead of /genus) - and you should see that button. The idea is that we have a public "frontend" for our site (e.g. /genus) and an admin section (e.g. /admin/genus).

    Cheers!

  • 2016-07-09 叶朱丽

    Hi!
    I keep on getting this error when trying to access the /genus/new page:

    Type error: Argument 1 passed to AppBundle\Entity\Genus::setSubFamily() must be an instance of AppBundle\Entity\SubFamily, string given, called in C:\xampp\htdocs\symf3\finish\src\AppBundle\Controller\GenusController.php on line 23

    I have tried to solve the problem, but I am afraid this goes beyond my knowledge :(

    Also, I don't see the 'New' blue button anywhere (tried with both the 'start' and the 'finish' files provided).

    Am I missing something?

    Thanks a lot!

    Giulietta

  • 2016-06-15 JLChafardet

    weaverryan Glad to have been of help

  • 2016-06-15 weaverryan

    Awesome, thank you for checking! I'll push the fix now :)

  • 2016-06-15 JLChafardet

    Fixed indeed.

    below is diff of what it had, and what it has now.

    diff file: http://pastie.org/private/j...

    as you requested, and I said, created a new branch (as youll see in my terminal) reset --hard to the initial point (as i did a git commit with the error)
    changed parameters.yml to a new database (aqua_note_2)

    console doctrine:database:create
    console doctrine:migrations:migrate
    console doctrine:fixtures:load

    here is the whole output:

    Terminal output text: http://goo.gl/9yzKky
    Terminal output images:
    part 1: http://goo.gl/UzoRmw
    part 2: http://goo.gl/qt51c4
    Browser output (without the non existing methods ofc basic starting point)
    genus list: http://goo.gl/Oe9qOZ
    genus detail: http://goo.gl/KTbWex
    admin area: http://goo.gl/PeD5a9

  • 2016-06-15 JLChafardet

    weaverryan going to create a git branch for that point, and attempt your suggestion and let you know the results in a minute.

    --update--

    fixed, check latest message above this one.

  • 2016-06-15 weaverryan

    Hey JLChafardet!

    Since you're able to get this migrations bug, and I can't repeat it, would you be willing to test out a fix for me?

    Here's what you would do:

    1) Replace your Version20160207092254.php file with this one: https://gist.github.com/wea...

    2) Drop everything and reload the fixtures from scratch:


    bin/console doctrine:database:drop --force
    bin/console doctrine:database:create

    ./bin/console doctrine:migrations:migrate

    If this works, then I'll push that fix up for everyone :).

    Thanks!

  • 2016-06-15 JLChafardet

    found a workarround.

    console doctrine:migrations:status

    this gave me the next in line migration version. which was 20160207092254

    checked in phpstorm which was the next in line, which was 20160412160008

    opened Version20160412160008.php got the version number(without Version) and performed:

    console doctrine:migrations:execute 20160412160008

    that worked.

    then did

    doctrine:migrations:execute 20160412165619

    which is the latest version

    then loaded the fixtures and bam, worked.

  • 2016-06-15 JLChafardet

    weaverryan need a hand here! your code is giving me a plethora of errors.

    http://goo.gl/ipSR6s <- take a peak