Buy

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:

85 lines src/AppBundle/Entity/GenusScientist.php
... 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:

108 lines src/AppBundle/Form/GenusFormType.php
... 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:

178 lines app/config/config.yml
... 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 - 178

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:

52 lines src/AppBundle/Form/GenusScientistEmbeddedForm.php
... 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:

185 lines app/config/config.yml
... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
... lines 93 - 168
User:
... lines 170 - 177
form:
fields:
- email
- isScientist
... lines 182 - 185

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:

281 lines src/AppBundle/Entity/User.php
... 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:

185 lines app/config/config.yml
... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
... lines 93 - 168
User:
... lines 170 - 177
form:
fields:
- email
- isScientist
- { property: 'fullName', type: 'text', help: 'First then Last' }
... lines 183 - 185

Keep going to add avatarUri and universityName:

185 lines app/config/config.yml
... lines 1 - 80
easy_admin:
... lines 82 - 91
entities:
... lines 93 - 168
User:
... lines 170 - 177
form:
fields:
- email
- isScientist
- { property: 'fullName', type: 'text', help: 'First then Last' }
- avatarUri
- universityName

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!

  • 2017-08-14 weaverryan

    Hey Geoffrey Monté!

    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!

  • 2017-08-14 Geoffrey Monté

    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 ?

  • 2017-07-18 Roberto Santana

    Thanks Victor, I found the problem, check above.

  • 2017-07-18 Victor Bocharsky

    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!

  • 2017-07-17 Diego Aguiar

    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!

  • 2017-07-17 Roberto Santana

    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!

  • 2017-07-17 Roberto Santana

    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.

  • 2017-07-17 Roberto Santana

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

  • 2017-07-17 Diego Aguiar

    Hey Roberto Santana

    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!

  • 2017-07-17 Roberto Santana

    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!