Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

The Form Type Class

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

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:

... 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:

... 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:

... 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:

... 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'):

... 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!

80
Login or Register to join the conversation
Default user avatar
Default user avatar Matias Rouaux | posted 4 years ago

Hi, I have tried to create de SubFamily Entity manually and a got this when I try bin/console doctrine:fixtures:load.
My error:
[Doctrine\ORM\ORMInvalidArgumentException]
Expected value of type "AppBundle\Entity\SubFamilia" for associati
on field "AppBundle\Entity\Animal#$subFamilia", got "string" instead.
Help!

2 Reply

Hey Matias Rouaux

I would like to see your fixture and entity code, so I can help you debugging :)

Cheers!

2 Reply
Default user avatar
Default user avatar Matias Rouaux | MolloKhan | posted 4 years ago

Of course! Here is:

***ENTITY SUBFAMILIA CODE:***

id;
}

/**
* @return mixed
*/
public function getNombre()
{
return $this->nombre;
}

/**
* @param mixed $nombre
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
}

}

***ENTITY ANIMAL CODE:***

setNotasAnimal(Array($notasAnimal)); no se podria hacer ).
# ESTE LADO SOLO ES PARA LEER INFORMACION (NOTAS EN ESTE CASO),
# POR ESO NO SERVIRIA DE NADA CREAR UN "setNotasAnimal"
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\NotasAnimal", mappedBy="animal")
* @ORM\OrderBy({"createdAt"="DESC"})
*/
private $notasAnimal;

public function __construct()
{
$this->notasAnimal = new ArrayCollection();
}

/**
* @return mixed
*/
public function getNombre()
{
return $this->nombre;
}

/**
* @param mixed $nombre
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
}

/**
* @return mixed
*/
public function getSubFamilia()
{
return $this->subFamilia;
}

/**
* @param mixed $subFamilia
*/
public function setSubFamilia($subFamilia)
{
$this->subFamilia = $subFamilia;
}

/**
* @return mixed
*/
public function getContadorDeEspecies()
{
return $this->contadorDeEspecies;
}

/**
* @param mixed $contadorDeEspecies
*/
public function setContadorDeEspecies($contadorDeEspecies)
{
$this->contadorDeEspecies = $contadorDeEspecies;
}

/**
* @return mixed
*/
public function getFunFact()
{
return '**PRUEBA** '.$this->funFact;
}

/**
* @param mixed $funFact
*/
public function setFunFact($funFact)
{
$this->funFact = $funFact;
}

/**
* @return mixed
*/
public function getisPublished()
{
return $this->isPublished;
}

/**
* @param mixed $isPublished
*/
public function setIsPublished($isPublished)
{
$this->isPublished = $isPublished;
}

/**
* @return ArrayCollection | NotasAnimal[]
*/
public function getNotasAnimal()
{
return $this->notasAnimal;
}

}

***FIXTURE CODE:**

AppBundle\Entity\Animal:
animales_{1..10}:
nombre: <animal()>
subFamilia: <text(20)>
contadorDeEspecies: <numberbetween(100,10000)>
funFact: <sentence()>
isPublished: <boolean(75)>

AppBundle\Entity\NotasAnimal:
animales.notas_{1..100}:
userName: <username()>
userAvatarFileName: '50%? leanna.jpeg : ryan.jpeg'
note: <paragraph()>
createdAt: <datetimebetween('-6 months',="" 'now')="">
animal: '@animales_*'

I think i got the error when i try to Join Subfamily with Genus

1 Reply
Default user avatar

I have already run bin/console doctrine:migrations:migrate and it is all ok. The problem appears when i try to load fixture to recover the fake data.
Cheers!

Reply

Oh, I see your problem, you defined a "text" type to your "subFamilia" property on Animal entity, instead of a relation to SubFamilia.


// your code
AppBundle\Entity\Animal:
...
subFamilia: <text(20)>
...


Just change it to:


AppBundle\Entity\Animal:
...
subFamilia: '@subfamilia_*'
...

Saludos!

Reply
Default user avatar
Default user avatar Matias Rouaux | MolloKhan | posted 4 years ago

Yess, it was just that! Thanks for your help Diego! :D
Saludos!

2 Reply

Great!

No hay problema, eres bienvenido :)

Reply

In `if ($form->isSubmitted() && $form->isValid())` if you look in the source code of `isValid()` function, the first thing it does is checking if the form was submitted. Why to call `isSubmitted()` at all then? Is there any reason behind it, or we can just use `if ($form->isValid())` instead?

Reply

Hey Serge,

Calling isValid() on a Form instance before submitting it is not supported anymore and raises an exception in Symfony 4, see: https://github.com/symfony/...

Cheers!

1 Reply
Hristo H. Avatar
Hristo H. Avatar Hristo H. | posted 4 years ago

Hi,

At 2:20, my auto-complete does not give me a result for 'form'. I have the Symfony plugin installed for PHPStorm v0.16.165.

Any suggestions?

Reply

Hey Hristo H.

You are using PHPStorm v0.16.165? Is that a beta version or something? Probably you should try using the newest one "2018.1" and upgrade the Symfony's plugin as well if possible

Cheers!

Reply
Hristo H. Avatar
Hristo H. Avatar Hristo H. | MolloKhan | posted 4 years ago | edited

Hey MolloKhan , thanks for your reply. I am on PHP Storm 2018.2 and Symfony Plugin 0.16.165.

Everything is up to date from my perspective, yet the Symfony plugin will not inject any "Form" options in search with "Control + N" (following the video exactly at 2:20).

Any ideas?

Reply

Hmm, interesting, I don't have those templates either... probably things has changed since the video was recorded, but, you can create your own file templates very easily. Just open PhpStorm settings and search for "File and Code Templates", in there you will see a list of templates that you can make use of, and you can create your own.

Cheers!

Reply
Default user avatar
Default user avatar Chantha Ben | posted 4 years ago

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

Reply

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!

Reply

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

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

Reply

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.

Reply

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!

1 Reply

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.

Reply

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

Reply

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

1 Reply

weaverryan Glad to have been of help

Reply
Default user avatar
Default user avatar Julia Shishik | posted 4 years ago

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!

Reply
Default user avatar

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

Reply

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!

Reply
Default user avatar
Default user avatar Julia Shishik | victor | posted 4 years ago

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

Reply

Hey Julia,

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

Cheers!

Reply
Default user avatar
Default user avatar Julia Shishik | victor | posted 4 years ago

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

Reply
Default user avatar

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

Reply

Hey Julia,

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

Cheers!

Reply
Default user avatar
Default user avatar Julia Shishik | victor | posted 4 years ago

You inspire me! Thank you!

Reply
Default user avatar
Default user avatar Robert Went | posted 4 years ago

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.

Reply

Hey Robert Went!

Thanks for the ping, it is already fixed :)

Have a nice day

Reply
Default user avatar
Default user avatar Terry Caliendo | posted 4 years ago

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();

Reply

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!

Reply
Default user avatar
Default user avatar Terry Caliendo | victor | posted 4 years ago

Ah! Thanks much!

Reply
Default user avatar
Default user avatar Daan Hage | posted 4 years ago

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

Reply

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!

Reply

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).

Reply

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

Reply

Hey somecallmetim ,

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!

Reply

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

Reply

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!

Reply
Default user avatar
Default user avatar Gavin Erickson | posted 4 years ago

Hi, Really enjoying this course - but am wondering if there is a section where the ability to add multiple "notes" to the form is kicking around somewhere on the site?

Reply

Yo Gavin Erickson!

Woo, awesome! Are you maybe looking for this tutorial? https://knpuniversity.com/s... We talk there about ManyToMany relationships, and also using complex embedded forms to, for example, allow the user to add multiple "scientists" to a Genus in one form (so, similar to adding multiple notes to the form). My only word of warning is that - even though we explain everything - embedded collection forms are tough. And in some cases, it's better to build simpler forms and use AJAX with API requests to add things (i.e. instead of having one giant form where you can add multiple forms, use JavaScript to allow the user to add a note, and as soon as they are finished, send an AJAX request to save that *one* note. Then the form is a bit easier to use and much less complex to manage - you probably would not even use the form component for the "notes" in this case).

Let me know if this helps or not!

Cheers!

Reply
Default user avatar
Default user avatar Gavin Erickson | weaverryan | posted 4 years ago | edited

weaverryan Thanks - Interesting! I was thinking AJAX + API method would be the way to go, so look forward to learning this technique. and developing patience. ;-)

Reply
Default user avatar
Default user avatar 叶朱丽 | posted 4 years ago

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

Reply

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!

Reply
Default user avatar

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

Reply
Default user avatar
Default user avatar Jan Zioło | posted 4 years ago

Hey, I am trying to set up a project for this course. That is what I see, after using db create command:

[Doctrine\DBAL\Exception\ConnectionException]
An exception occured in driver: SQLSTATE[HY000] [2002] Connection refused

[Doctrine\DBAL\Driver\PDOException]
SQLSTATE[HY000] [2002] Connection refused

[PDOException]
SQLSTATE[HY000] [2002] Connection refused

I have no experience with Symfony yet. What may be the reason? How should I proceed with the installation to make it work?

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// 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
    }
}