Chapters
-
Course Code
Subscribe to download the code!Compatible PHP versions: >=5.5.9
Subscribe to download the code!Compatible PHP versions: >=5.5.9
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
The Form Type Class
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
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:
Show Lines
|
// ... 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; | |
Show Lines
|
// ... lines 24 - 38 |
} |
And created a ManyToOne
relation from Genus
to SubFamily
:
Show Lines
|
// ... lines 1 - 11 |
class Genus | |
{ | |
Show Lines
|
// ... lines 14 - 25 |
/** | |
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\SubFamily") | |
* @ORM\JoinColumn(nullable=false) | |
*/ | |
private $subFamily; | |
Show Lines
|
// ... 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
:
Show Lines
|
// ... 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:
Show Lines
|
// ... lines 1 - 8 |
class GenusFormType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder | |
->add('name') | |
Show Lines
|
// ... lines 15 - 16 |
; | |
} | |
Show Lines
|
// ... lines 19 - 23 |
} |
Keep going: add('speciesCount')
and add('funFact')
:
Show Lines
|
// ... lines 1 - 12 |
$builder | |
->add('name') | |
->add('speciesCount') | |
->add('funFact') | |
; | |
Show Lines
|
// ... 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!
80 Comments
Hey Matias Rouaux
I would like to see your fixture and entity code, so I can help you debugging :)
Cheers!
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
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!
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!
Yess, it was just that! Thanks for your help Diego! :D
Saludos!
Great!
No hay problema, eres bienvenido :)
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?
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!
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?
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!
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?
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!
hi guys , how to get value from Formtype(translation) in controller
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!
there need a hand here! your code is giving me a plethora of errors.
http://goo.gl/ipSR6s <- take a peak
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.
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/weaverryan/cda3b87f6ab8d2878dd044d269ec6c90
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!
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.
Fixed indeed.
below is diff of what it had, and what it has now.
diff file: http://pastie.org/private/jmtsfljqqryktaxwze9ha
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
Awesome, thank you for checking! I'll push the fix now :)
weaverryan Glad to have been of help
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!
I solved the problem, but now I have no information from the database how to return it? Sorry(((
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!
When I open the page 'http://localhost:8000/genus' the colums of Genus, # of species and Last updste are empty!
Hey Julia,
Could you try to reload fixtures? Does it fix your problem?
Cheers!
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
Thank you very very much! I understood everything!!! The problem was in fixtures.yml I did not register the data !!! Thanks again!! Hooray!
Hey Julia,
Haha, I'm glad you had got it working before I replied to you - good investigation! ;)
Cheers!
You inspire me! Thank you!
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.
Hey Robert Went!
Thanks for the ping, it is already fixed :)
Have a nice day
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();
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!
Ah! Thanks much!
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
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!
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).
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!
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!
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?
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!
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. ;-)
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
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!
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?
"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
}
}
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!