Chapters
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
EntityType: Drop-downs from the Database
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOn submit, we set the author
to whoever is currently logged in. I want to change that: sometimes the person who creates the article, isn't the author! They need to be able to select the author.
ChoiceType: Maker of select, radio & checkboxes
Go to the documentation and click back to see the list of form field types. One of the most important types in all of Symfony is the ChoiceType
. It's kind of the loud, confident, over-achiever in the group: it's able to create a select drop-down, a multi-select list, radio buttons or checkboxes. It even works on weekends! Phew!
If you think about it, that makes sense: those are all different ways to choose one or more items. You pass this type a choices
option - like "Yes" and "No" - and, by default, it will give you a select drop-down. Want radio buttons instead? Brave choice! Just set the expanded
option to true. Need to be able to select "multiple" items instead of just one? Totally cool! Set multiple
to true
to get checkboxes. The ChoiceType
is awesome!
But... we have a special case. Yes, we do want a select drop-down, but we want to populate that drop-down from a table in the database. We could use ChoiceType
, but a much easier, ah, choice, is EntityType
.
Hello EntityType
EntityType
is kind of a "sub-type" of choice - you can see that right here: parent type ChoiceType
. That means it basically works the same way, but it makes it easy to get the choices
from the database and has a few different options.
Head over to ArticleFormType
and add the new author
field. I'm calling this author
because that's the name of the property in the Article
class. Well, actually, that doesn't matter. I'm calling this author
because this class has setAuthor()
and getAuthor()
methods: they are what the form system will call behind the scenes.
Show Lines
|
// ... lines 1 - 10 |
class ArticleFormType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder | |
Show Lines
|
// ... lines 16 - 22 |
->add('author') | |
Show Lines
|
// ... line 24 |
} | |
Show Lines
|
// ... lines 26 - 32 |
} |
As soon as we add this field, go try it! Refresh! Hello drop-down! It is populated with all the users from the database... but... it might look a little weird. By default, the EntityType
queries for all of the User
objects and then uses the __toString()
method that we have on that class to figure out what display value to use. So, firstName
. If we did not have a __toString()
method, we would get a huge error because EntityType
wouldn't know what to do. Anyways, we'll see in a minute how we can control what's displayed here.
Set the Type, Options are Not Guessed
So... great first step! It looks like the form guessing system correctly sees the Doctrine relation to the User
entity and configured the EntityType
for us. Go team!
But now, pass the type manually: EntityType::class
. That should make no difference, right? After all, the guessing system was already setting that behind the scenes!
Show Lines
|
// ... lines 1 - 23 |
->add('author', EntityType::class) | |
Show Lines
|
// ... lines 25 - 35 |
Well... we're programmers. And so, we know to expect the unexpected. Try it! Surprise! A huge error!
The required option
class
is missing
But, why? First, the EntityType
has one required option: class
. That makes sense: it needs to know which entity to query for. Second, the form type guessing system does more than just guess the form type: it can also guess certain field options. Until now, it was guessing EntityType
and the class
option!
But, as soon as you pass the field type explicitly, it stops guessing anything. That means that we need to manually set class
to User::class
. This is why I often omit the 2nd argument if it's being guessed correctly. And, we could do that here.
Show Lines
|
// ... lines 1 - 24 |
->add('author', EntityType::class, [ | |
'class' => User::class, | |
]) | |
Show Lines
|
// ... lines 28 - 38 |
Try it again. Got it!
Controlling the Option Display Value
Let's go see what else we can do with this field type. Because EntityType's parent is ChoiceType
, they share a lot of options. One example is choice_label
. If you're not happy with using the __toString()
method as the display value for each option
... too bad! I mean, you can totally control it with this option!
Add choice_label
and set it to email
, which means it should call getEmail()
on each User
object. Try this. I like it! Much more obvious.
Show Lines
|
// ... lines 1 - 24 |
->add('author', EntityType::class, [ | |
Show Lines
|
// ... line 26 |
'choice_label' => 'email', | |
]) | |
Show Lines
|
// ... lines 29 - 39 |
Want to get fancier? I thought you would. You can also pass this option a callback, which Symfony will call for each item and pass it the data for that option - a User
object in this case. Inside, we can return whatever we want. How about return sprintf('(%d) %s')
passing $user->getId()
and $user->getEmail()
.
Show Lines
|
// ... lines 1 - 24 |
->add('author', EntityType::class, [ | |
Show Lines
|
// ... line 26 |
'choice_label' => function(User $user) { | |
return sprintf('(%d) %s', $user->getId(), $user->getEmail()); | |
} | |
]) | |
Show Lines
|
// ... lines 31 - 41 |
Cool! Refresh that! Got it!
The "Choose an Option" Empty Value
Another useful option that EntityType
shares with ChoiceType
is placeholder
. This is how you can add that "empty" option on top - the one that says something like "Choose your favorite color". It's... a little weird that we don't have this now, and so the first author is auto-selected.
Back on the form, set placeholder
to Choose an author
. Try that: refresh. Perfecto!
Show Lines
|
// ... lines 1 - 24 |
->add('author', EntityType::class, [ | |
Show Lines
|
// ... lines 26 - 29 |
'placeholder' => 'Choose an author' | |
]) | |
Show Lines
|
// ... lines 32 - 42 |
With all of this set up, go back to our controller. And... remove that setAuthor()
call! Woo! We don't need it anymore because the form will call that method for us and pass the selected User
object.
We just learned how to use the EntityType
. But... well... we haven't talked about the most important thing that it does for us! Data transforming. Let's talk about that next and learn how to create a custom query to select and order the users in a custom way.
56 Comments
Hey Yahya E.
You almost have it, just add all other attributes into the attr
array:
$resolver->setDefaults(array(
'attr' => [
'class' => 'select2'
'data-something' => 'some value'
]
));
Cheers!
"(... ) we do want a select drop-down, but we want to populate that drop-down from a table in the database. We could use ChoiceType, but a much easier, ah, choice, is EntityType."
Is it possible to use ChoiceType with a list of options from db without using EntityType? For example, instead of creating entities and relationships, simply provide the name of the table or prepare a simple querybuilder - the entire ChoiceType could then be created in one place (instead of an additional entity and relationship - we do not always need entities and relationships and everything that is needed is in the database.)
Or maybe there is an example somewhere to use ChocieType with db?
Hey @Nataniel-Z
Yes, you can specify what items to show in your choice type field. I'll point you to the documentation https://symfony.com/doc/current/reference/forms/types/choice.html#choices
Let me know if you have any doubts. Cheers!
Hey!,
maybe you can help me understand this createForm behaviour.
I have simple form:
$builder->add('id', EntityType::class, ['required' => false, 'class' => Categories::class, 'choice_label' => 'name', ]);
I want to display this separate list in <select>, not as part of another form or entity. Just simple separate select.
(it should also be selected depending on the request param).
If in controller I have:
#[Route(path: '/categories/{id}', name: 'categories')]
public function showCategoriesAction(Categories $category): Response
{
$form = $this->createForm(CategoriesType::class, $category);
...
}
then error:
Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader::getIdValue(): Argument #1 ($object) must be of type ?object, int given,
but if the second parameter of createForm is like this:
$form = $this->createForm(CategoriesType::class, ['id' => $category]);
then everything is fine and <select> also show the selected field accordingly.
Why is this happening? Why does this need to be put in an array? In other forms there is no such need -
(it seems to me (I don't know if it's correct) that if the EntityType Field is part of another entity, it's ok.)
And what does this "child" field in $builder->add() actually mean - whose child is it?
Hey @Nataniel-Z
You need to bind the CategoryType
form to the Categories
entity to be able to send a $category
object as second argument, otherwise as you already noticed, you need to specify the input value of the id
field of the form
And what does this "child" field in $builder->add() actually mean - whose child is it?
I don't see any child
field. Can you explain more?
Cheers!
I suspect it needs to 'wait' a bit for me to fully understand it, but at least I have a reference - thank you :)
I was asking about the first parameter of the builder->add() method.
interface FormBuilderInterface extends \Traversable, \Countable, FormConfigBuilderInterface
{
/**
* Adds a new field to this group. A field must have a unique name within
* the group. Otherwise the existing field is overwritten.
*
* If you add a nested group, this group should also be represented in the
* object hierarchy.
*
* @param array<string, mixed> $options
*/
public function add(string|self $child, string $type = null, array $options = []): static;
Oh, I got you now. It's the name of the field, or if it's a form bound to a class it would be the name of a property from the class.
Symfony forms work like a tree, you can add another form as a field of the "parent" form. That's useful when you have a complex form which is composed of many pieces (User data, address, company details, etc)
When I try to use the "Symfony\Bridge\Doctrine\Form\Type\EntityType" namespace, it says that the class does not exist and I'm using Symfony 6.2. Is there a way to start using EntityType fields in Symfony 6.2 forms?
Hey @Jeff
I think there's something wrong with your app because that class still exists in Symfony 6.2. You can check it on the docs https://symfony.com/doc/current/reference/forms/types/entity.html
I believe you don't have Doctrine installed, or for some reason the problem it's in your vendors, you could try reinstalling them
Cheers!
Hi Ryan,
I've a concern whith EntityType, ChoiceType field rendering.
How to customize the rendering of each field, or each element of the list?
I've already set : 'expanded'=>true and 'multiple'=>true, but don't know how to give each element a specific style.
Is it possible from the form component to get access to each individual field or item ? Or required to style through JS and CSS by selecting all the items from stimulus ?
Thank's.
Hey @discipolat ,
Depends on your needs. If you're fine to do it in JS - you could try with Stimulus for example, but that would mean that before the page is fully loaded and handled by your JS you will still see the unmodified HTML. Though you may do some tricks and hide the field completely and show it with Stimulus only after you handled it. But you're ok with this - sounds fine :)
But yes, you're right, Symfony forms also can be customized with templates, i.e. you can tweak template of each field if you need (though that might be complex / challenging ). You can read docs about form theme - that's what you need: https://symfony.com/doc/current/form/form_customization.html#form-themes
I hope this helps! :)
Cheers!
Hi @Victor, Thank's i'll RTM and give you some feedback.
Hello,
I am experimenting with the code and have made my own Form, Repository and Controller. For adding new articles, everything works fine. But when I try to edit an article, I am getting 'Entity of type "Doctrine\ORM\PersistentColletion" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?'.
This seems to be rather clear, but nevertheless I am stuck. Any idea why or is additional information needed?
Thx
Oliver
Hey Oliver W.
That's interesting, I think you forgot to assign a field somewhere in your Article entity. What it's basically saying is that you forgot to call persist
on a new entity object but when you edit an entity you don't need to call persist on it again (that's the odd part). I think I'd need to see your code to spot the error
Hi Diego Aguiar,
thx for your hint concerning the droping.
But I had no luck with my problem. My Articles.php:
`<?php
namespace App\Entity;
use App\Repository\ArticlesRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
@ORM\Entity(repositoryClass=ArticlesRepository::class)
/ class Articles { /*- @ORM\Id
- @ORM\GeneratedValue
- @ORM\Column(type="integer")
*/
private $id;
/**
- @ORM\Column(type="string", length=100)
*/
private $name;
/**
- @ORM\Column(type="datetime")
- @Gedmo\Timestampable (on="create")
*/
private $boughtAt;
/**
- @ORM\ManyToOne(targetEntity="App\Entity\Warehouses", inversedBy="articles")
- @ORM\JoinColumn(nullable=false)
*/
private $warehouse;
/**
- @ORM\Column(type="string", length=50)
*/
private $location;
/**
- @ORM\Column(type="integer")
*/
private $onstock;
/**
- @ORM\Column(type="float")
*/
private $primecost = 0;
/**
- @ORM\Column(type="float")
*/
private $retail = 0;
/**
- @ORM\OneToMany(targetEntity=Images::class, mappedBy="article")
*/
private $images;
/**
- @ORM\Column(type="text", nullable=true)
*/
private $comment;
/**
- @ORM\ManyToMany(targetEntity=Descriptions::class, inversedBy="articles")
*/
private $category;
public function __construct()
{$this->images = new ArrayCollection(); $this->category = new ArrayCollection();
}
public function getId(): ?int
{return $this->id;
}
public function getName(): ?string
{return $this->name;
}
public function setName(string $name): self
{$this->name = $name; return $this;
}
public function getBoughtAt(): ?\DateTimeInterface
{return $this->boughtAt;
}
public function setBoughtAt(\DateTimeInterface $boughtAt): self
{$this->boughtAt = $boughtAt; return $this;
}
public function getWarehouse(): ?Warehouses
{return $this->warehouse;
}
public function setWarehouse(?Warehouses $warehouse): self
{$this->warehouse = $warehouse; return $this;
}
public function getLocation(): ?string
{return $this->location;
}
public function setLocation(string $location): self
{$this->location = $location; return $this;
}
public function getOnstock(): ?int
{return $this->onstock;
}
public function setOnstock(int $onstock): self
{$this->onstock = $onstock; return $this;
}
public function getPrimecost(): ?float
{return $this->primecost;
}
public function setPrimecost(float $primecost): self
{$this->primecost = $primecost; return $this;
}
public function getRetail(): ?float
{return $this->retail;
}
public function setRetail(float $retail): self
{$this->retail = $retail; return $this;
}
/**
- @return Collection|Images[]
*/
public function getImages(): Collection
{
return $this->images;
}
public function addImage(Images $image): self
{if (!$this->images->contains($image)) { $this->images[] = $image; $image->setArticle($this); } return $this;
}
public function removeImage(Images $image): self
{if ($this->images->contains($image)) { $this->images->removeElement($image); // set the owning side to null (unless already changed) if ($image->getArticle() === $this) { $image->setArticle(null); } } return $this;
}
public function getComment(): ?string
{return $this->comment;
}
public function setComment(?string $comment): self
{$this->comment = $comment; return $this;
}
/**
- @return Collection|Descriptions[]
*/
public function getCategory(): Collection
{
return $this->category;
}
public function addCategory(Descriptions $category): self
{if (!$this->category->contains($category)) { $this->category[] = $category; } return $this;
}
public function removeCategory(Descriptions $category): self
{if ($this->category->contains($category)) { $this->category->removeElement($category); } return $this;
}
}`
Do you need anything else? And thx for your assistance.
Oliver
Hey Oliver W.
Hmm, I can't spot the error. Could I see the full constructor code? It got cut, also the FormType code, please.
Hi Diego Aguiar,
I repeatedly tried to post the whole code, but there are ALWAYS several lines at the beginning, that are cut off!?!?!? This happens for every listing. For the FormType too. When I click von Edit, the lines are shown. I've got the impression, everything until the constructor is omitted. May be a bug in this application?
Are there other ways to get the code to you? Find it here: http://cloud.ad-libitum.inf.... Valid until next monday.
I found that the problem is related to the ordering of my categories alphabetically in ArticleFormType. If I only use "->add('category')" it works fine. I also noticed that my new-form is displayes, but I get an error when trying to save: The property "category" in class "App\Entity\Articles" can be defined with the methods "addCategory()=", "removeCategory()" but the new value must be an array or an instance of \Traversable. Futher more it is NOT possible to select more than one category. So probably there is missing some array thing.
HTH and thx
Oliver
Hey Oliver W.
I'm not sure if I'm missing something but your code looks good to me. I'd need to play a bit with your application to spot the problem. But, there's something you can try before that. It's likely that you're not assigning the other side of the relationship. In your Descriptions
entity, double-check that the addArticle()
method sets both sides (I couldn't check that because you didn't upload that entity :p)
BTW, this video may help you out understanding a bit better how to manage ManyToMany relationships https://symfonycasts.com/screencast/doctrine-relations/many-to-many
If you still have problems, upload your project to Github or wherever you want and give me access so I can play with it. Cheers!
Hi MolloKhan
find the app at the known place. I still couldn't manage to chose more than one Category during the creation of new articles (Artikel). And editing from the list of articles throws the error.
Have a nice weekend
Hey Oliver W.
I finally discovered what's the problem. We forgot to make the category field a multi-select! You only have to pass 'multiple' => true
into the options array of the field. It's a detail to keep in mind when working with ManyToMany relationships
https://symfony.com/doc/current/reference/forms/types/choice.html#multiple
I felt a bit dump when I realized that... Oh programming, you always surprise me :D
Hi MolloKhan
you're the greatest. That was the missing link for both problems.
Thx so lot and have a nice week.
Oliver
Hi MolloKhan,
II found a probaly suspicious. But I can't figure out how to remove a field from a database. Adding is done via php bin/console make:entity. But how can I remove a field from a existing database and update my migrations at the same time?
Thx
Oliver
If I understood you correctly, what you want is to only remove a field from a database table which is not mapped to an entity? Because if it's mapped, then you can just remove the property on the entity and generate a migration. What you can do is to create manually a migration, just copy-paste an old migration, tweak its name and write the SQL you need to *drop* a field from the table you want. That should do the trick
Cheers!
I've got the feeling, I missed something. How does Symfony know that it should use the field user.first_name to populate the dropdown to fill article.author using ->add('author')??? I understand the target, but not the source!
Hey Oliver W.
That's because we have the __toString()
method on the User class, and that method calls and returns getFirstname()
method. You can rely on that method or define the choice_label
option as shown in the video
Cheers!
Thx Diego.
I've made one in tag.php too and still get the users. So there has to be more!? May be a setting from Mastering Doctrine Relations! that I haven't watched yet.
With choice_label it is the same. Why uses Symfony the table users?
Why uses Symfony the table users?
Because theauthor
field is related to the User class, when you add a field to your form this way$builder->add('author');
Symfony uses an interfal "guesser" feature that reads the metadata of such field. Then, if you don't specify a choice label option, it will invoke the__toString()
method of the object.
In this case, the user class has implemented the__toString()
method, and it returns the first name
Does it makes more sense now?
Cheers!
Thx Diego. I'm afraid we were talking at cross-purposes.
Meanwhile I've been looking at the Desginer in phpMyAdmin and found that there are relations between the tables of the database. One is from article.author_id to user.id. Thats exactly the missing link. I suppose these relations have been set in the course " Mastering Doctrine Relations". Till now I did not make this one. I guess I should catch up ;-)
That's the explanation why my __toString() in the table tag has no effect on the "guessing" of Symfony. Since there is no relation between article.author_id and tag.name.
Ohh I get it now. Sorry for the confusion. Yes, the relationship with the user class is already set up at this point of the tutorial. I can't remember exactly in what chapter we add the relationship but if you download the course code from this page you can see what to do, or watch the "Mastering Doctrine Relations" course to dive deep into relationships
If you still have doubts, pleaes let me know.
Hello!
I got a performance issue when trying a more "sophisticated" choice_label.
Here is my code:
$builder->add('postalCode', EntityType::class, [<br /> 'class' => PostalCode::class,<br /> 'choice_label' => function(PostalCode $postalCode) {<br /> return sprintf('%d (%s)', $postalCode->getPostalCode(), $postalCode->getCity());<br /> },<br /> 'label' => $this->translator->trans('form.postalCode.label',[], 'users'),<br /> 'placeholder' => $this->translator->trans('form.postalCode.placeholder', [], 'users'),<br />])
The PostalCode entity is a translatable entity and the city is one of the translated field.
`
use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
class PostalCode {
use Translatable;
...
}`
use Knp\DoctrineBehaviors\Model\Translatable\Translation;
class PostalCodeTranslation
{
use Translation;
...
}`
If I just print the postalCode field, the performance is good.
Any idea?
Thx!
Hey Lydie,
I'd recommend you to look at Symfony's web debug profile. What do you mean about performance hit? Most probably you execute a lot of queries to the DB, but would be good to know this for sure like how much queries are executed behind the scene when you create this form, that's where Symfony profiler could help.
I suppose that "$postalCode->getCity()" query for related entity for each postal code, so if you have hundreds of them - that might be the problem. If it's the exact reason - it would be good if you can pre-load all those translatable entities, I think you can do it with "query_builder" option: https://symfony.com/doc/current/reference/forms/types/entity.html#using-a-custom-query-for-the-entities - something like this:
$builder->add('postalCode', EntityType::class, [
'class' => postalCode::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('postalCode')
->select('postalCode')
->addSelect('translations')
->innerJoin('postalCode.translations', 'translations');
},
// ...
]);
But it still might be issue with memory. Anyway, it's important to understand the exact reason of performance degradation. Otherwise, maybe look for a way to translate that field with Symfony Translation component instead?
I hope this helps!
Cheers!
Thx Victor! This was indeed the issue (N+1 issue if I am not wrong). I had 2782 queries (so it took time ...) and after doing your suggested modification, the number decreased to 17. Good progress, isn't it ? :) I will check the other queries I made on this site to see if I can improve some others too.
Thx again!!
Hey Lydie,
Thanks for your feedback! Sounds like a great optimization ;) I'm glad it helped you!
Cheers!
Trying to create a form which has one unmapped choices field and dynamic values.
public function buildForm(FormBuilderInterface $project, array $options)
{
$project->add('users', EntityType::class, [
'choice_label' => 'name',
'label' => 'Select Users',
'expanded' => true,
'multiple' => true,
'class' => 'App:User',
'mapped' => false,
'choices' => $options['users'],
]);
}
List of choices comes from an array of User type objects which is passed in options while trying to create form.
$form = $this->createForm(ProjectFormType::class, $project, array('users' => $users));
How do I show already selected Users whlie trying to edit the form? It takes automatically, if it is mapped field.
Also, is there a better way to pass users data for choices?
Hey Bhagwandas P.
How do I show already selected Users whlie trying to edit the form?
The second argument you pass to$this->createForm()
represent the initial data of the form, so in this case you should pass an array with a "users" key, or even better if you create a DataModel class and bind it to the form.
is there a better way to pass users data for choices?
I don't see anything wrong on how you are currently doing it
Cheers!
How can SF handle new dynamic form fields?
By example, I have a "new recipe" form with ingredients.
Name of ingredient, amount of ingredient, type of amount (g, mg, etc.)
Now I have a "+" button, to add more of these 3 fields per ingredient via JS.
How can I let SF handle this type of the "same" field multiple times?
Any tips what/how I should use SF forms?
Thanks in advance, your sf tutorials are superb :)
Hey Mike P.
There is a feature in Symfony forms called "propotype" that allows you to do specifically that. Here is a video where Ryan implements it: https://symfonycasts.com/sc...
or you can check the docs: https://symfony.com/doc/cur...
Cheers!
Hi,
One question: if I have 2 dropdowns on my form, for example:
`
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('region', EntityType::class, [
'class' => Products::class,
'choice_label' => 'region'
])
->add('city', EntityType::class, [
'class' => Products::class,
'choice_label' => 'city'
])
;
}
<br />than __toString() returns just one value <br />for example: <br />
public function __toString() {
return $this->city;
}
`
and saves in db city name that was chosen.
But how to save both values.
City and Region?
Hey Vesna
Is there any particular reason on using the EntityType for such fields? What you can do is to create a ProductFormType and just add those fields as ChoiceType or whatever other type that fit your needs
Cheers!
Hi @Diego
Thanks for quick reply.
I'm still figuring out how this works. :)
I will try with CustomFormType.
Thanks!!!
Vesna
One more question.
How to solve this error "You cannot add children to a simple form. Maybe you should set the option "compound" to true?"
I'm using easyadminbundle, and trying to add ProductFormType, but constantly getting this error :(
Hmm, how are you telling EasyAdmin to use your form type? Also, let me see your CustomFormType code
Hi MolloKhan
in easy_admin.yaml:
form:
fields:
- { property: 'name', css_class: 'large', label: 'Product Name' }
- { property: 'product', type: 'App\Form\ProductFormType', columns: 4 }
and in CustomFormType:
class ProductsFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', EntityType::class, array(
'class' => Product::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('p')
->orderBy('p.city', 'ASC');
},
));
}
public function getParent()
{
return ChoiceType::class;
}
}
Am I missing something? :(
I think you only have to remove the "getParent" method, but you can achieve the same result with almost pure config. What you want is to specify the "query_builder" option, and you can do it like so:
form:
fields:
- property: 'product'
- type_options: { query_builder: 'App\Repository\ProductRepository::CustomRepoMethod'}
Cheers!
I have an issue while trying to save the user role(s) to the database. I get the famous <b>Expected argument of type "array", "string" given at property path "roles"</b>. The issue occurs when I actually hit the save button. The initial form rendering works as expected.
While setting the 'multiple' => true
, as many suggest, this is not the way I need my option to be set. I just want to be able to pick an option from the dropdown.
I thought of creating an Entity for the roles but this would not make sense since S4 has all the basic roles in the core already.
My code looks like this:
the FormType: OrganizationUserType.php
->add('roles', ChoiceType::class, [
'label' => 'Role',
'placeholder' => 'Choose an option',
'required' => 'false',
'choices' => [
'Member' => 'ROLE_USER',
'Admin' => 'ROLE_ADMIN',
]
])
the Entity: OrganizationUser.php
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
Any thoughts on how could I handle this one?
Cheers!
Hey radone
So, the case is that you are passing a String when what you really want is to pass an Array, in this case what you need is to apply a DataTransformer into the desired field.
You can see how Ryan implements his own DataTransformer here: https://symfonycasts.com/sc...
Cheers!
Return value of App\Entity\Article::getAuthor() must be an instance of App\Entity\User, null returned
Hey Roman!
Interesting! This tells me that the Form system is calling getAuthor() on your User object... which is odd. Are you passing an Article object when you call createForm()?
Cheers!
This is very nice, thank you. I am using the 'choice_label' now to combine two attributes of an entity I want to have in that label. I wonder (and I don't know if this is even possible or has anything to do with symfony forms in particular), would it be possible to have the names of both attributes in the label, but only have one of them visible? You might wonder why I would want such a thing, but this is because I also use Select2 to search for the options, and I would like to be able to search for my second attribute, but not show it. Thx!
"Houston: no signs of life"
Start the conversation!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.10.2
"doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.7.2
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.8.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.1
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.1.6
"symfony/cache": "^3.3|^4.0", // v4.1.6
"symfony/console": "^4.0", // v4.1.6
"symfony/flex": "^1.0", // v1.21.6
"symfony/form": "^4.0", // v4.1.6
"symfony/framework-bundle": "^4.0", // v4.1.6
"symfony/property-access": "^3.3|^4.0", // v4.1.6
"symfony/property-info": "^3.3|^4.0", // v4.1.6
"symfony/security-bundle": "^4.0", // v4.1.6
"symfony/serializer": "^3.3|^4.0", // v4.1.6
"symfony/twig-bundle": "^4.0", // v4.1.6
"symfony/validator": "^4.0", // v4.1.6
"symfony/web-server-bundle": "^4.0", // v4.1.6
"symfony/yaml": "^4.0", // v4.1.6
"twig/extensions": "^1.5" // v1.5.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.6
"symfony/dotenv": "^4.0", // v4.1.6
"symfony/maker-bundle": "^1.0", // v1.8.0
"symfony/monolog-bundle": "^3.0", // v3.3.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.6
"symfony/stopwatch": "^3.3|^4.0", // v4.1.6
"symfony/var-dumper": "^3.3|^4.0", // v4.1.6
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.1.6
}
}
One question: I want to extend ChoiceType to MySelect2Type. In MySelect2Type;
It works but in Form if I add
attr
for example to add a data-attribute, it overrides'attr' => ['class' => 'select2']
.How to overcome this?