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

CollectionType Field

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.

Start your All-Access Pass
Buy just this tutorial for $8.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

The form system does a pretty good job guessing the correct field types... but nobody is perfect. For example, the genusScientists field is not setup correctly. Click the clipboard icon to open the form profiler.

Yep, genusScientists is currently an EntityType with multiple set to true. Thanks to EasyAdminBundle, it renders this as a cool, tag-like, auto-complete box. Fancy!

But... that's not going to work here: the GenusScientist entity has an extra field called yearsStudied:

... lines 1 - 17
class GenusScientist
{
... lines 20 - 38
/**
* @ORM\Column(type="integer")
* @Assert\NotBlank()
*/
private $yearsStudied;
... lines 44 - 83
}

When you link a Genus and a User, we need to allow the admin to also fill in how many years the User has studied the Genus.

In the Symfony series, we did a lot of work to create a CollectionType field that used GenusScientistEmbeddedForm:

... lines 1 - 20
class GenusFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
... lines 26 - 48
->add('genusScientists', CollectionType::class, [
'entry_type' => GenusScientistEmbeddedForm::class,
'allow_delete' => true,
'allow_add' => true,
'by_reference' => false,
])
;
... lines 56 - 57
}
... lines 59 - 106
}

Thanks to that, in the admin, we just need to update the form to look like this.

Change genusScientists to use the expanded syntax. From here, you can guess what's next! Set type: collection and then add type_options with the 4 options you see here: entry_type: AppBundle\Form\GenusScientistEmbeddedForm, allow_delete: true, allow_add: true, by_reference: false:

... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
Genus:
... lines 94 - 114
form:
fields:
... lines 117 - 125
-
property: 'genusScientists'
type: 'collection'
type_options:
entry_type: AppBundle\Form\GenusScientistEmbeddedForm
allow_delete: true
allow_add: true
by_reference: false
... lines 134 - 145

Let's see what happens! Woh! Ignore how ugly it is for a minute. It does work! We can remove items and add new ones.

But it looks weird. When we created this form for our custom admin area - we hid the user field when editing... which looks really odd now. Open the GenusScientistEmbeddedForm. We used a form event to accomplish this: if the GenusScientist had an id, we unset the user field. Comment that out for now and refresh:

... lines 1 - 14
class GenusScientistEmbeddedForm extends AbstractType
{
... lines 17 - 34
public function onPostSetData(FormEvent $event)
{
if ($event->getData() && $event->getData()->getId()) {
$form = $event->getForm();
// unset($form['user']);
}
}
... lines 42 - 50
}

Cool: this section at least makes more sense now.

The CollectionType Problems

But... there are still some problems! First, it's ugly! I know this is just an admin area... but wow! If you want to use the CollectionType, you'll probably need to create a custom form theme for this one field and render things in a more intelligent way. We'll do something similar in a few minutes.

Second... this only works because we already did a lot of hard work setting up the relationships to play well with the CollectionType. Honestly, the CollectionType is both the best and worst form type: you can do some really complex stuff... but it requires some seriously tough setup. You need to worry about the owning and the inverse sides of the relationship, and things called orphanRemoval and cascading. There is some significant Doctrine magic going on behind the scenes to get it working.

So in a few minutes, we're going to look at a more custom alternative to using the collection type.

Virtual Form Field

But first, I want to show you one more thing. Go to the User section and edit a User. We haven't touched any of this config yet. In config.yml, under User, add form then fields. Let's include email and isScientist:

... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
... lines 93 - 145

Right now, the form has firstName and lastName fields... which makes sense: there are firstName and lastName properties in User. But just like we did earlier under the list view, instead of having firstName and lastName, we could actually have, just fullName. And nope... there is not a fullName property. But as long as we create a setFullName() method, we can totally add it to the form:

... lines 1 - 16
class User implements UserInterface
{
... lines 19 - 225
public function setFullName($fullName)
{
$names = explode(' ', $fullName);
$firstName = array_shift($names);
$lastName = implode(' ', $names);
$this->setFirstName($firstName);
$this->setLastName($lastName);
}
... lines 235 - 279
}

Actually, this isn't special to EasyAdminBundle, it's just how the form system works!

Now... this example is a little crazy. This code will take everything before the first space as the first name, and everything after as the last name. Totally imperfect, but you guys get the idea.

And now that we have getFullName() and setFullName(), add that as a field: property: fullName, type: text and a help message:

... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
... lines 93 - 145

Keep going to add avatarUri and universityName:

... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
... lines 93 - 145

Try it out! Yes! It looks great... and... it even submits! Next up, let's add a field that needs custom JavaScript to work.

Leave a comment!

24
Login or Register to join the conversation
Default user avatar
Default user avatar rafaelmansilha | posted 3 years ago

Hi everyone,

I'm stating now with EasyAdmin, and I'm struggling with collection type, I see that you use a FormType to build the Collection in entry_type. How do I tell it to use another admin? I want to use a collection of a entity mapped with the admin, do I really need to create de FormType for it?

Reply

Hey rafaelmansilha!

Sorry for my extra slow reply! Yea, I think I understand what you're asking. The answer is: maybe :). y You probably do "really need to create a FormType for it"... but avoiding it may be possible.

When you render a form page in EasyAdmin, the form class is actually this one - https://github.com/EasyCorp/EasyAdminBundle/blob/1.x/src/Form/Type/EasyAdminFormType.php - it's a super-dynamic form type that builds itself based on the entity and your admin options. So, in theory, you could create a form with this, pass in whatever entity you want with an "entity" option, and it will build itself. And then you could embed it. It would look something like this inside of your "main" form type:


$builder
            ->add('genusScientists', CollectionType::class, [
                'entry_type' => EasyAdminFormType::class,
                'entry_options' => [
                    'entity' => GenusScientist::class
                ],
                'allow_delete' => true,
                'allow_add' => true,
                'by_reference' => false,
            ])

Will that work? I have no idea :). But now you have me interested ;). Let me know if you try this and whether it works or not.

Cheers!

1 Reply
Default user avatar
Default user avatar Edouard Pihet | weaverryan | posted 3 years ago

Hi !

I tried your method, but it missing the 'view' option in the 'entry_option'. What should we add in this field ?

Thank you in advance for your help !

Reply

Hey Edouard Pihet

By looking at this class https://github.com/EasyCorp... I understand that the view option contains the data you defined for such entity and view (create or edit). I find it difficult to extend so I suggest to create your own FormType, it would be cleaner and easier

Cheers!

Reply
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | posted 3 years ago

Hi everyone,
I am having an issue with easyadmin. It is a kind of issue I saw in Ryan tutorials, but I never saw this issue resolved. Basically, when I edit an entry and one particular field is a ManyToOne relation, EasyAdmin (by default) generate an autocomplete dropdown field. Now, assuming I have two entities User and Product, when I try to edit a product, I can see who is the user owing that product. The issue is that inside that dropdown, I can only see the name of the user as oppose to name and surname. I can see that easyadmin calls getName from the User entity, but I don't know how to tell easyAdmin to call a different method from inside the User Entity.

Any idea?

Reply

Hey Alessandro D.

EasyAdmin fully relies on the Symfony Form component, so you can do the exact same thing EasyAdmin forms as you would do with "normal" forms. What you need to do is to specify the "choice_label" option (Docs: https://symfony.com/doc/current/reference/forms/types/entity.html#choice-label)


easy_admin:
    entities:
        Genus:
            form:
                fields:
                    -
                        property: 'genusScientists'
                        type: 'collection'
                        type_options:
                            choice_label: 'some_property_field"

I hope it helps :)
Cheers!

Reply
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | MolloKhan | posted 3 years ago | edited

Hi Diego,
I have tried what you suggested, but I get the following error:
"The option "choice_label" does not exist".
Here is my code:

`

        form:
            fields:
                - title
                - revision
                - { property: 'createdAt', type: 'datetime', type_options: { widget: 'single_text' } }
                - author
                -
                    property: 'usedBy'
                    type: 'collection'
                    type_options:
                        choice_label: 'surname'
                - tags
                -
                    property: 'path'
                    label: 'Upload Script'
                    type: 'file_upload'
                    type_options: { upload_dir: public/screenplays }
                - active

`

If I change all the block for 'usedBy' from this to normal '-usedBy' I do get the dropdown, but only the name is displayed

Reply

Hey Alessandro D.

Oh, sorry my bad, instead of specifying type: collection, use entity
Docs about EntityType: https://symfony.com/doc/current/reference/forms/types/entity.html#basic-usage

Then, you will be able to specify its choice_label attribute. Let me know if it worked :)

Reply
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | MolloKhan | posted 3 years ago

Hi Diego,
That is great. It works like a charm.

Thanks a lot. I can't believe I actually spent an entire evening trying to figure that out. !!! :(

Thanks again.
Alessandro

Reply

I hear you man. Symfony Forms are complex but the documentation is great. Every time I'm in doubt I go to the docs and check for any option that may fit my needs.

Cheers!

Reply
Kaizoku Avatar
Kaizoku Avatar Kaizoku | posted 4 years ago | edited

Hi Team !

I faced a funny behaviour with embed form (collection).
So I have this in easy_admin.yaml


- { property: 'files',  type: 'collection', type_options: { entry_type: 'App\Form\ResourceFileFormType', allow_delete: true, allow_add: true , by_reference: false,  block_name: 'resource_file' }}

It works fine, except for the validation.
All fields are required, but there is no small red asterisk and the form can submit with all fields empty.
Even the html5 validation doesn't work.
Any clue about how to fix this ?

Here is the formType


namespace App\Form;

use App\Entity\Language;
use App\Entity\ResourceFile;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;

class ResourceFileFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add(
                'name',
                TextType::class,
                [
                    'required' => true,
                ]
            )
            ->add(
                'language',
                EntityType::class,
                [
                    'class'    => Language::class,
                    'required' => true,
                ]
            )
            ->add(
                'file',
                VichFileType::class,
                [
                    'required'     => true,
                    'allow_delete' => true,
                    'label'        => 'File',
                    'download_uri' => true,
                ]
            );
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(
            [
                'data_class' => ResourceFile::class,
            ]
        );
    }
}
Reply

Hey Kaizoku!

Hmm. So, it sounds like there are two things going on: the HTML5 validation doesn't work AND the server-side validation also does not work. Is that right? These sound like similar systems, but actually they are quite separate.

First, about the HTML 5 validation, I see that you have 'required' => true for your "name" and "language" fields. If you look in the HTML for those fields, do you see a required attribute or not?

Second, about the server-side validation, you may need to add the @Valid constraint above the files property on your entity so that the validation applies to this sub-entity.

Let me know if either of these things helps!

Cheers!

Reply
Kaizoku Avatar

First of all thank you so much Ryan for your answer.

> do you see a required attribute or not?

nope ! So maybe this an issue with the easy_admin form theme file ?

> you may need to add the @Valid constraint above the files property on your entity

yep ! I added validation constraint in the entity and it's now working. At least there is doctrine exception no more.

Don't bother about the HTML5 (unless you know why) since there is form validation it's good enough.

Reply

Hey Kaizoku!

> nope ! So maybe this an issue with the easy_admin form theme file ?

Hmm, maybe - I'm not sure. I would look click on the form icon on the web debug toolbar (the clipboard icon), select one of the fields that should be required, and see if the "required" option and "required" view variables are indeed set correctly.

That will at least give you something to check out ;).

Cheers!

Reply
Geoffrey M. Avatar
Geoffrey M. Avatar Geoffrey M. | posted 5 years ago

Hi,

Something cool in sonata is the ability to create a new entry directly from the form containing the relation:
By example: Posts have many tags. When I create a new Post, I select one or many tags. If a tag is missing I can create a new tag in a modal without leaving the post form.

Pretty easy to setup in sonata, is there a trick to do the same in easyAdmin ?

Reply

Hey Geoffrey M.!

Actually, this is also one of my favorite features of Sonata! Unfortunately, it's not something that exists in EasyAdminBundle :/. So, it would need to be added custom :/ - no easy way! If you have enough of these things, then using Sonata starts to make more sense.

Cheers!

Reply
Roberto S. Avatar
Roberto S. Avatar Roberto S. | posted 5 years ago

Hi! I'm following your course for my project but I've an issue.

I've two entities "shop" and "pictures" OneToMany relationship, one shop many pictures. Easy!

I can manage "shop" and "pictures" entites fine from EasyAdminBundle, the problem is when I try to delete or add new photos from "shop" entity, nothing happens it's so weird.

The error is:

Expected value of type "AppBundle\Entity\Shop" for association field "AppBundle\Entity\Picture#$shop", got "string" instead.

Any ideas?

Thanks!

Reply

Hey Roberto S.

When you try to add a new photo to a shop, don't you get any error message ? try checking the logs, or if you click in the profiler bar, there is a section of "Requests", where you can check older requests, I believe this "add photo action" is handled via AJAX, maybe you could find out what's going on in there.

Cheers!

Reply
Roberto S. Avatar

The error is:

Expected value of type "AppBundle\Entity\Shop" for association field "AppBundle\Entity\Picture#$shop", got "string" instead.

The request isn't via AJAX :(

Reply

Hey Roberto,

Hm, it's weird. Are you sure it's directly related to EasyAdminBundle? Please, make sure you don't call "$picture->setShop('some-string');" manually somewhere in your code. Also, could you show us your easy_admin configuration for Shop and Picture entities?

Cheers!

Reply
Roberto S. Avatar
Roberto S. Avatar Roberto S. | Victor | posted 5 years ago

Thanks Victor, I found the problem, check above.

Reply
Roberto S. Avatar

I've identified the problem.

My Entity "Picture" doesn't initialize the "Shop" field so I've changed the field "Shop" in my PictureEmbeddedForm to 'JavierEguiluz\Bundle\EasyAdminBundle\Form\Type\EasyAdminAutocompleteType' because I've more than 30.000 shops. But it doesn't work when I add new Pictures, only to update or delete.

How can I initialize the "Shop" field of "Picture" entity when I clic on "add new item" on the Collection?

I don't really need a select field for that, It's enough with a hidden field to save the relationship.

Thanks for your help.

Reply
Roberto S. Avatar

Problem solved!!!

I forgot to add $foto->setShop($this); on addPicture method.

public function addPicture(Picture $picture {
$foto->setShop($this);
return $this->pictures->add($picture);
}

Thanks!

Reply

Ohh, so the problem was that you forgot to setup the relationship when adding a picture. Good job debugging the problem, the part of the error message "got string instead" confused me.

Cheers!

Reply
Cat in space

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

This tutorial is built on an older version of Symfony & EasyAdminBundle. Many of the concepts are the same, but you can expect major differences in newer versions.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.3.*", // v3.3.18
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.7
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.17.0
        "sensio/distribution-bundle": "^5.0", // v5.0.25
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.4
        "knplabs/knp-markdown-bundle": "^1.4", // 1.7.1
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "stof/doctrine-extensions-bundle": "^1.2", // v1.3.0
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "javiereguiluz/easyadmin-bundle": "^1.16" // v1.17.21
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.7
        "symfony/phpunit-bridge": "^3.0", // v3.4.40
        "nelmio/alice": "^2.1", // v2.3.5
        "doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
    }
}
userVoice