Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

AssociationField for a "Many" Collection

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 $12.00

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

Login Subscribe

There's one other AssociationField that I want to include inside this CRUD section and it's an interesting one: $answers. Unlike $topic and $answeredBy, this is a Collection: each Question has many answers:

... lines 1 - 13
class Question
{
... lines 16 - 41
#[ORM\OneToMany('question', Answer::class)]
private Collection $answers;
... lines 44 - 206
}

Back in QuestionCrudController, yield AssociationField::new('answers'):

... lines 1 - 12
class QuestionCrudController extends AbstractCrudController
{
... lines 15 - 19
public function configureFields(string $pageName): iterable
{
... lines 22 - 42
yield AssociationField::new('answers');
... lines 44 - 45
}
}

And.... let's just see what happens! Click back to the index page and... Awesome! It recognizes that it's a Collection and prints the number of answers that each Question has... which is pretty sweet. And if we go to the form, I'm really starting to like this error! The form is, once again, trying to get a string representation of the entity.

Configuring the choice_label Field Option

We know how to fix this: head over to Answer.php and add the __toString() method. But, there's actually one other way to handle this. If you're familiar with the Symfony Form component, then this problem of converting your entity into a string is something that you see all the time with the EntityType. The two ways to solve it are either to add the __toString() method to your entity, or pass your field a choice_label option. We can do that here thanks to the ->setFormTypeOption() method.

Before we fill that in, open up the AssociationField class... and scroll down to new. Behind the scenes, this uses the EntityType for the form. So any options EntityType has, we have. For example, we can set choice_label, which accepts a callback or just the property on the entity that it should use. Let's try id:

... lines 1 - 12
class QuestionCrudController extends AbstractCrudController
{
... lines 15 - 19
public function configureFields(string $pageName): iterable
{
... lines 22 - 42
yield AssociationField::new('answers')
->setFormTypeOption('choice_label', 'id');
... lines 45 - 46
}
}

And now... beautiful! The ID isn't super clear, but we can see that it's working.

by_reference => false

Let's... try removing a question! Remove "95", hit "Save and continue editing" and... uh. Absolutely nothing happened? Answer id "95" is still there!

If you're familiar with collections and the Symfony Form component, you might know the fix. Head over and configure one other form type option called by_reference set to false:

... lines 1 - 12
class QuestionCrudController extends AbstractCrudController
{
... lines 15 - 19
public function configureFields(string $pageName): iterable
{
... lines 22 - 42
yield AssociationField::new('answers')
->setFormTypeOption('choice_label', 'id')
->setFormTypeOption('by_reference', false);
... lines 46 - 47
}
}

I won't go into too much detail, but basically, by setting by_reference to false, if an answer is removed from this question, it will force the system to call the removeAnswer() method that I have in Question:

... lines 1 - 13
class Question
{
... lines 16 - 163
public function removeAnswer(Answer $answer): self
{
if ($this->answers->removeElement($answer)) {
// set the owning side to null (unless already changed)
if ($answer->getQuestion() === $this) {
$answer->setQuestion(null);
}
}
return $this;
}
... lines 175 - 206
}

That method properly removes the Answer from Question. But more importantly, it sets $answer->setQuestion() to null, which is the owning side of this relationship... for you Doctrine geeks out there.

orphanRemoval

Ok, try removing "95" again and saving. Hey! We upgraded to an error!

An exception occurred ... Not null violation: ... null value in column question_id of relation answer...

So what happened? Open Question.php back up. When we remove an answer from Question, our method sets the question property on the Answer object to null. This makes that Answer an orphan: its an Answer that is no longer related to any Question.

However, inside Answer, we have some code that prevents this from ever happening: nullable: false:

... lines 1 - 10
class Answer
{
... lines 13 - 23
#[ORM\JoinColumn(nullable: false)]
private ?Question $question;
... lines 26 - 92
}

If we ever try to save an Answer without a Question, our database will stop us.

So we need to decide what should happen when an answer is "orphaned". In some apps, maybe orphaned answers are ok. In that case, change to nullable: true and let it save. But in our case, if an answer is removed from its question, it should be deleted.

In Doctrine, there's a way to force this and say:

If an Answer ever becomes orphaned, please delete it.

It's called "orphan removal". Inside of Question, scroll up to find the $answers property... here it is. On the end, add orphanRemoval set to true:

... lines 1 - 13
class Question
{
... lines 16 - 41
#[ORM\OneToMany('question', Answer::class, orphanRemoval: true)]
private Collection $answers;
... lines 44 - 206
}

Now refresh and... yes! It worked! The "95" is gone! And if you looked in the database, no answer with "ID 95" would exist. Problem solved!

Customizing the AssociationField

The last problem with this answers area is the same problem we have with the other ones. If we have many answers in the database, they're all going to be loaded onto the page to render the select. That's not going to work, so let's add ->autocomplete():

... lines 1 - 12
class QuestionCrudController extends AbstractCrudController
{
... lines 15 - 19
public function configureFields(string $pageName): iterable
{
... lines 22 - 42
yield AssociationField::new('answers')
->autocomplete()
... lines 45 - 48
}
}

When we refresh, uh oh!

Error resolving CrudAutocompleteType: The option choice_label does not exist.

Ahhh. When we call ->autocomplete(), this changes the form type behind AssociationField. And that form type does not have a choice_label option! Instead, it always relies on the __toString() method of the entity to display the options, no matter what.

No big deal. Remove that option:

... lines 1 - 12
class QuestionCrudController extends AbstractCrudController
{
... lines 15 - 19
public function configureFields(string $pageName): iterable
{
... lines 22 - 42
yield AssociationField::new('answers')
->autocomplete()
->setFormTypeOption('by_reference', false);
... lines 46 - 47
}
}

You can probably guess what will happen if we refresh. Yup! Now it's saying:

Hey Ryan! Go add that __toString() method!

Ok fine! In Answer, anywhere in here, add public function __toString(): string... and return $this->getId():

... lines 1 - 10
class Answer
{
... lines 13 - 93
public function __toString(): string
{
return $this->getId();
}
}

Now... we're back! And if we type... well... the search isn't great because it's just numbers, but you get the idea. Hit save and... nice!

Next, let's dig into the powerful Field Configurators system where you can modify something about every field in the system from one place. It's also key to understanding how the core of EasyAdmin works.

Leave a comment!

32
Login or Register to join the conversation
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | posted 1 year ago | edited

Hi,
I am facing an related to this CollectionField I cannot find an answer for.
I have a project entity that has a OneToMany relationship with the entity Roles. In my ProjectCrudController. I have added the following:


            CollectionField::new('roles')
                ->setEntryType(RoleType::class)
                ->setTemplatePath('easyAdmin/role/add.html.twig').  // <= this line does not work

Basically, I created a RoleType inside my Form Folder so. that I can decide what items to display, but the issue I am facing is that I cannot find a way to specify which twig template to use so that I can customise how those fields get displayed in the collection.
I have tried setTemplatePath but that doesn't work (I did make sure that 'easyAdmin/role/add.html.twig' do exists inside my symfony templates directory, but this small thing is driving me crazy).

Any idea on how I could achieve the above?

Thanks,
Alessandro

1 Reply

Hey Alessandro,

Hm, could you share the exact error you see when you use that template path? The "setTemplatePath()" method is the correct method name for it, so most probably you misprinted the template path, or you read the error message incorrectly. First of all, let's double-check the template path, are you sure you have that "templates/easyAdmin/role/add.html.twig" template path? i.e. the template name "easyAdmin/role/add.html.twig" you set to that method should be in the templates/ folder. Also, make sure your letter case is correct, i.e. you should match the letter case in path with one in your file system. And if everything looks good - please, try to clear the cache, do "rm -rf var/cache/*" just in case and try again. If still does not work - share the exact error message please.

Cheers!

Reply
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | Victor | posted 1 year ago | edited

Hi Victor,
The problem is that I do not receive any error message. When I load the Project New Form, I can see the 'Roles' section with 'Add a new Item' and when I click on it it display whatever fields I have set in my RoleType custom form. My RoleType form class is as followed:


namespace App\Form\EasyAdmin;

use App\Entity\Role;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class RoleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('character_name', TextType::class, [
                'label' => 'Character Name <span class="text-danger">*</span>',
                'label_html' => true
            ])
            ->add('gender', ChoiceType::class, [
                'choices' => [
                    'Choose...' => '',
                    'Male' => 'male',
                    'Female' => 'female',
                ],
                'label' => 'Gender <span class="text-danger">*</span>',
                'label_html' => true
            ])
            ->add('type', TextType::class, [
                'label' => 'Type <span class="text-danger">*</span>',
                'label_html' => true
            ])
            ->add('playing_age')
            ->add('dialect')
            ->add('description')
            ->add('number_of_poses')
            ->add('height')
            ->add('appearance')
            ->add('fee')
            ->add('notes')
        ;
    }

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

I can confirm that easyAdmin/role/add.html.twig do exists inside my 'templates' folder and I also tried to empty my var/cache folder, but no luck there.

The way I thought logically it was going to work was:
CrudController -> EntryType -> Twig Template -> Front End

Although what I couldn't figure out was that, normally, when I load a custom FormType in a normal controller, I do set up the name of the Form Object (my_form) I am going to use from inside my twig template (to be used like {{ my_form.my_field }}, as oppose in here I do not seem to have the ability to do so, as I am going from the CrudController into the FormType. In the CrudController, if I am going to load my own custom twig template, how can I address the actual form object from within my FormType (I hope it makes sense)

Alessandro

Reply
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | Alessandro-D | posted 8 months ago | edited

Hi,
believe it or not, I still haven't figured out how to resolve this issue.
I have updated my symfony version to 6 and EasyAdmin 4, but I cannot seems to be able to set the twig template.

In My ProjectCrudController, I have the following:

        yield CollectionField::new('roles')
            ->setEntryType(ProjectRoleType::class)

My ProjectRoleType class is like this:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('sex')
            ->add('dialect')
            ->add('description')
            ->add('type')
        ;
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ProjectRole::class,
        ]);
    }

With the code above, I do get to see the list of fields for the ProjectRole class, but the way they display are a single raw for each field. I do want to control the look of that via the twig.
I have tried to change my ProjectCrudController CollectionField from ->setEntryType(ProjectRoleType::class) to ->useEntryCrudForm(ProjectRoleCrudController::class)

and then, in my ProjectRoleCrudController class, overwrite the template like this:

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('Role')
            ->setEntityLabelInPlural('Roles')
            ->overrideTemplates([
                'crud/new' => 'easyAdmin/project/role/new.html.twig'
            ])
            ;

but without avail. If I go to that specific CrudController directly, I can see that easyAdmin/project/role/new.html.twig Twig template is loaded correctly, but if I try to load it by going through the ProjectCrudController -> CollectionField, it does not load.

Any help will be highly appreciated. I seem to get stuck in this without a solution.

Thanks.
Alessandro

Reply

Hey Alessandro!

Sheesh! This is crazy! Let's see if we can sort it out :).

I think I know the "overarching" problem: the setTemplateName() or setTemplatePath() stuff only applies to how a field renders on list/show pages. It does NOT impact how your field renders on your form.

So how do you control how things are rendered on the form? For a hint, here is how EasyAdmin renders, for example, the "edit form" (this is from a core EasyAdmin template):

{{ form(edit_form) }}

Yup, they simply use the form() function internally. This means that the form is rendered 100% based on your form themes. And so, if you want to modify how a form renders, form themes are your answer.

In this case, you want to customize how ONLY this ONE collection type renders: you don't want to create a form theme and override how all collection types render. Fortunately, that is possible. Here's what you should do:

A) Load the page and click the form icon in the web debug toolbar
B) Once inside, you should be able to click onto the specific "roles" collection field to get info about it.
C) Inside, you can look at the "block prefixes" on the view variables - e.g. https://symfonycasts.com/screencast/symfony-forms/form-theme-create - the final one will start with an _. THIS is the block prefix you can use you override JUST this field on this form.
D) Create some new template and, for example, override the _whatever_the_prefix_was_roles_row block, which will allow you to control how the "row" renders for your field.
E) On your field, call ->setFormTheme('path/to/new/template.html.twig') so that your new form theme template is used when this template renders.

Let me know if this helps!

Cheers!

Reply
Alessandro-D Avatar
Alessandro-D Avatar Alessandro-D | weaverryan | posted 8 months ago | edited

Hi Ryan,
First of all, thanks a lot for your detailed explanation.
I have tried to follow your instructions, but I am still a bit confused on the execution of it (sorry :( )
First of all, in my ProjectCrudController, where I have the CollectionField for roles, I tried to add ->setFormThemebut there is no such a method for it, therefore I am not sure where should I add that method to (if inside my ProjectCrudController or in the ProjectRoleType class (even though there I only list the actual fields I would want to be displayed in the CollectionField.

I looked at the _profiler and I could see that the block prefixes is called _Project_roles, but I am confused on how I should use it in my twig template.

The ProjectCrudController uses a custom template for the crud/new (which I overwrote in the configureCrud). The template loads correctly, although I am not sure how should I manipulate that specific CollectionField, mainly because the roles is a collection of fields, but inside the block, it does not allow me to do things like "form_row(new_form.roles.dialect)".
I don't know how I can display the specific fields insides the roles collection from within the custom block.

    {% block new_form %}
        {{ form_start(new_form) }}
            <div class="row">
                <div class="col-12 col-md-3">{{ form_row(new_form.agency) }}</div>
                <div class="col-12 col-md-9">{{ form_row(new_form.title) }}</div>
                <div class="col-12 col-md-4">{{ form_row(new_form.castList) }}</div>
                <div class="col-12">{{ form_row(new_form.notes) }}</div>
            </div>
            <div class="row">
                <div class="col-12">
                    <div class="card">
                        <div class="card-header">{{ 'project.roles'|trans }}</div>
                        <div class="card-body">
                            <div class="row">
                                {% block _Project_roles_row %}
                                    {{ form_widget(form) }}
                                {% endblock %}
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        {{ form_row(new_form._token) }}
        {{ form_end(new_form, {render_rest: false}) }}
    {% endblock %}

I hope this make some sort of sense.
Alessandro

Reply

Hey Alessandro!

Excellent, detailed reply. Let's see if we can get you to the finish line!

First, about setFormTheme(), I think we actually don't need to worry about it :). What we need is a template where we can customize the form theme. Since you are already overriding the actual CRUD template, we can put the form theme there.

Here's the idea. Override the "form theme" for your collection field. This has 0% to do with EasyAdmin and 100% to do with Symfony's form theming system, which can definitely be quite tricky. Exactly how it should look will depend on what you need to do exactly. But, it would look something like this at the top of the template you already have:

{% form_theme form _self %}

{# this should be the block name - but I could be slightly off #}
{# Reference: https://symfony.com/doc/current/form/form_themes.html#fragment-naming-for-collections #}
{% block _Project_roles_entry_row %}
    {# inside, we're customizing the "row" for each embedded "role" form #}
    <div>
        {{ form_row(form.name') }}
        {{ form_row(form.dialect') }}
        ...
    </div>
{% endblock %}

Let me know if this helps!

Cheers!

Reply
Lukas-S Avatar
Lukas-S Avatar Lukas-S | posted 6 days ago

Hi,

please, I have a problem with CollectionField.

In configureFields method I added:

yield CollectionField::new('terms')
            ->useEntryCrudForm(
                TermCrudController::class
            )
            ->setEntryIsComplex();

But the embeded form does not respect the required property. There is even no asterisk. If the required fields are empty then this errow is thrown:
Expected argument of type "string", "null" given at property path "dateText".
The required property is lost in embeded form even if I try set required in the TermCrudController:
yield TextField::new('dateText')->setRequired(true);
Please, could you help me?

Reply

Hey Lukas,

My guess is that you need to add a #[Assert\Valid] constrain on that terms field in the entity to make the cascade validation of the relations, see https://symfony.com/doc/current/reference/constraints/Valid.html for more info.

I hope it helps!

Cheers!

Reply
Lukas-S Avatar

Hello Victor,

Thanks for your suggestion but it doesn't help.

Reply

Hm, Assert\Valid had to help. Are you sure you apply it to the correct field? Another workaround would be to try to add specific constraints to the form type, see: https://symfony.com/doc/current/reference/forms/types/form.html#constraints

Cheers!

Reply
Eric Avatar

Is there a way to render an AssociationField as checkboxes?

Reply

Hey Eric!

Try this:

AssociationField::new('...')->renderAsNativeWidget();

I haven't used that, but if I'm understanding things correctly, that tells the system to render this is in the "normal" way, instead of with the fancy autocomplete. If you do this and, instead, it renders as a "multi select" instead of checkboxes, also try adding:

AssociationField::new('...')
    ->renderAsNativeWidget()
    ->setFormTypeOption('expanded', true);

Let me know if that works!

Cheers!

1 Reply
Eric Avatar
Eric Avatar Eric | weaverryan | posted 9 months ago | edited

Hey Ryan, thanks for the reply!

After some headbusting I managed to achieve my goal by doing

        AssociationField::new('...')
            ->setFormType(EntityType::class)
            ->setFormTypeOptions([
                'expanded' => true,
                'multiple' => true,
                'choices' => ... // Not sure, but I think it'll work without explicit choices aswell
            ])

Only downside: If you pass a hierarchical array to choices (as in my case) it gets flattened on rendering. But you can provide a custom template to solve that aswell (Symfony Doc)

I haven't tried your solution, so I cannot say if it is maybe a better approach. I'm just happy it's working.... :D

Thanks for your work! Symfonycasts really is one of THE pillars of my coding! ... Right up there with Stackoverflow. :'D

Reply

Hey Eric!

Thanks for sharing the solution - and for the kind words. Right up there with Stackoverflow - HUGE honor :D.

Cheers!

Reply

Is there a simple way to make link for one-to-many associations?
We have this cute count info but how to make this badge number to became a link to all releted entities in index view?

Reply

Hey Kwolniak,

Unfortunately, there's no an easy way for this, like I don't see any options on that field that would make the number as a link. But I think you can try to override the template and wrap the number with a link - should be easy enough too. Or you can even try to create a universal template and make the link dynamic instead of hardcoding it getting the info from the data you have in that template, i.e. the controller's class. And this way you will just need to specify this template in the field where you want to have that link, or even do it globally in the dashboard and it will apply to all your controllers.

I hope it helps!

Cheers!

Reply
kwolniak Avatar kwolniak | Victor | posted 10 months ago | edited | HIGHLIGHTED

I did this:

        yield AssociationField::new('links')
            ->setTemplatePath('admin/field/my_custom_template.html.twig')
            ->setCustomOption('controller', LinksCrudController::class)
            ->setCustomOption('action', Action::INDEX)
            ->setCustomOption('menuIndex', 3);

and in template:

{% set url = ea_url({
        crudControllerFqcn: field.customOptions.get('controller'),
        crudAction: field.customOptions.get('action'),
        menuIndex: field.customOptions.get('menuIndex'),
        query: entity.instance().slug
    }) %}
    <a href="{{ url }}"><span class="badge badge-secondary">{{ field.formattedValue }}</span></a>

can I somehow get related controller from ea, field or entity variables available in template?
I want my template universal, so it's why i set it via custom options.

Notice: my_custom_template.html.twig is used in XXXCrudController, and i want links to related YYYCrudController, so it's why I set it in controller via custom options. I don't know if I can extract YYYCrudController from from ea, field or entity variables in template.

Also. Is it possible to get menuIndex dynamically? or have I hardcode it, like i did?

1 Reply

Hey Krzysztof,

Yeah, good job!

Did you try to dump those available variables? Actually, in a Twig template you can just do {{ dump() }} without arguments and it will print you all the available variables in that spot :)

I believe that {{ field.customOptions.get('crudControllerFqcn') }} is what you need, it should be the controller of the "links" in your case :)

I hope this helps! If not, just try to search in the dumped content for the data you need, e.g. "LinkCrudController" to see where you can get it from.

Cheers!

Reply

Thanks!
field.customOptions.get('crudControllerFqcn') did a trick!

1 Reply

Hey Kwolniak,

Awesome! Thanks for confirming it helps :)

Cheers!

Reply
Nick-F Avatar

NoSuchPropertyException
HTTP 500 Internal Server Error
Could not determine access type for property "id" in class "App\Entity\Question".
in \vendor\symfony\property-access\PropertyAccessor.php:533
when saving after removing one of the answers from the question.

Seemed to have solved it by calling hideWhenUpdating() on Question's IdField.

Reply

Hey Nick F.!

Hmm, that is super weird. I'm not sure what would cause this (it seems like something is trying to *write* to the id property of Question)... or why hideWhenUpdating() seems to help... but I'm glad you at least got a workaround :).

Cheers!

Reply
Colin-P Avatar
Colin-P Avatar Colin-P | posted 1 year ago

Hello Diego,

I want to thank you for creating Easy Admin. I really love using it, and I'm grateful for this tutorial.

I have a question about collection fields that I was hoping you could answer for me:

When the entry type for a collection field has an entity type field, is it possible to have the embedded form as a TomSelect (like the Association Field), rather than a regular 'select' element? I've tried searching for this but haven't been able to find the answer.

Thank you,
Colin

Reply

Hey Colin,

First of all, thank you for your kind feedback about this course and the bundle in general, that's really made our day!

About your question, hm, are you sure you want to use exactly TomSelect? Looks like behind the scene it uses a simple input field as I see form its docs. I'd suggest you to take a look at this library instead: https://github.com/select2/... - it's based on a select tag which is exactly what you need I think. And so, you will only need to render the list of entities as a simple select tag - Symfony already can do this for you, then add some CSS classes to it and some glue select2 JS code, and it should work as you want I suppose.

I hope this helps!

Cheers!

Reply
Colin-P Avatar

Hey Victor,

I don't actually want to exactly use TomSelect. I just want to know if I use the CollectionField in the crud controller of an entity, and the embedded form type contains an entity field, can Easy Admin create this field the same way it would an Association field?

In a more general sense, can the CollectionField wrap other Easy Admin field types?

In the documentation for example, you see a collection type where the entry type is some sort of User type (has the fields fullName, username, and email. https://symfony.com/doc/4.x...

If the User type also had a field called 'department' which is a relation to a Department entity, this gets rendered as an HTML select element in the embedded form. Can Easy Admin handle making this an Association field (so that I could use the autocomplete feature)?

SymfonyCasts makes my job so much easier. Thanks again to you and the team. Hope everyone is able to stay safe.

Colin

Reply

Hey Colin,

Hm, I see... I don't have a simple yes/no answer actually because I've never tried this before, did you try to do this? It's something I've never thought about, but it might work from the first sight. Please, try this. I suppose you need to take a look at the AssociationField. You're right, it's used "EntityType::class" behind the scene, but I suppose you will need to add more CSS classes to match the EasyAdmin render of that field. So, it might work, but I'm not sure, you need to try.

What about AssociationField instead of EntityType directly in the CollectionType - hm, you can try this too, but I suppose it won't work because AssociationField class is an EastAdmin's "field" when EntityType is a Symfony form type - so, nevertheless that AssociationField uses EntityType behind the scene, those are different concepts unfortunately. So, I don't think you can easily replace EntityType with AssociationField unfortunately.

I hope this helps!

Cheers!

Reply
Colin-P Avatar

Hey Viktor,

yes I have been trying this out but haven't found what I'm looking for. I guess I'm just hoping that a CollectionField in EasyAdmin could contain other EasyAdmin fields.

So if I had the following entities, Person, Occupation, and PersonOccupation
Person has a string field called 'name', and a one-to-many relation field to PersonOccupation called 'occupations',
Occupation has a string field called 'title'
PersonOccupation has a many-to-one relation to person, a many-to-one relation field to occupation and a date field called 'startDate'.

Now, if I made the crud controller for Person, and configured it so that occupations was a collection field, for each sub-form of PersonOccupation, I'd like the 'Occupation' field to be an autocomplete/Assocation field, just as if I created the crud controller for PersonOccupation, and configured the fields so that occupation is an Association field. But they just show up as a select field.

I'm sure I'm doing something incorrectly or there's a good reason for it not working this way. I just wanted to get my thoughts out there so that I can understand a bit better.

thanks,
Colin

Reply

Hey Colin,

Ah, I see. Well, I'm not sure how to easily achieve what you want fairly speaking. If it does not work out of the box with default configuration - I'm not sure. Well, you can try to look at the field where you have autocompletetion, and add the same CSS classes, etc. It may bring some JS functionality there I suppose, but I'm not sure it will work with AJAX autocompletion, probably only filter the select options.

Cheers!

Reply
PXLWIDGETS Avatar
PXLWIDGETS Avatar PXLWIDGETS | posted 1 year ago

Hi! Great tutorial so far :)

I have a question: I have a many to many relation, with additional data, like here ( https://symfonycasts.com/sc... )

I cannot (yet) figure out to edit that. In my case I have a product, which has a relation with a service. The additional data is a service fee. How would I go about making that editable on the product page? I've been messing around with a custom field, but no success so far.

Reply

Hey PXLWIDGETS

I'm afraid you'll have to refactor your ManyToMany approach because you're adding extra fields to the extra table that holds the relationship between your objects, you may want to watch this chapter https://symfonycasts.com/sc...

So, what you need to do is to create another entity that will work as an intermediate table for your other 2 entities, you can add as many fields you want to that new entity class, and then, create an EasyAdmin CRUD controller to handle it.

Cheers!

Reply
PXLWIDGETS Avatar
PXLWIDGETS Avatar PXLWIDGETS | MolloKhan | posted 1 year ago | edited

Hi Diego!

I did do that yes, the ease solution for me would have been to create an additional CRUD controller for the intermediate table. For my clients convenience however, I was trying to add it to the product page. Which did end up working!

`
if ($pageName === 'new') {

        $product = new Product();

        $serviceTypes = $this->doctrine->getRepository(ServiceType::class)->findAll();

        foreach ($serviceTypes as $serviceType) {
            $productServiceFee = (new ProductServiceFee())
                ->setProduct($product)
                ->setServiceType($serviceType);
            $product->addProductServiceFee($productServiceFee);
        }

        $this->getContext()->getEntity()->setInstance($product);
    }

    yield CollectionField::new('productServiceFees')
        ->allowAdd(true)
        ->allowDelete(false)
        ->setEntryIsComplex(true)
        ->renderExpanded()
        ->setEntryType(ProductServiceFeeType::class);

`

1 Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "doctrine/doctrine-bundle": "^2.1", // 2.5.5
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.2.1
        "doctrine/orm": "^2.7", // 2.10.4
        "easycorp/easyadmin-bundle": "^4.0", // v4.0.2
        "handcraftedinthealps/goodby-csv": "^1.4", // 1.4.0
        "knplabs/knp-markdown-bundle": "dev-symfony6", // dev-symfony6
        "knplabs/knp-time-bundle": "^1.11", // 1.17.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.5
        "stof/doctrine-extensions-bundle": "^1.4", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.1
        "symfony/console": "6.0.*", // v6.0.2
        "symfony/dotenv": "6.0.*", // v6.0.2
        "symfony/flex": "^2.0.0", // v2.0.1
        "symfony/framework-bundle": "6.0.*", // v6.0.2
        "symfony/mime": "6.0.*", // v6.0.2
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/runtime": "6.0.*", // v6.0.0
        "symfony/security-bundle": "6.0.*", // v6.0.2
        "symfony/stopwatch": "6.0.*", // v6.0.0
        "symfony/twig-bundle": "6.0.*", // v6.0.1
        "symfony/ux-chartjs": "^2.0", // v2.0.1
        "symfony/webpack-encore-bundle": "^1.7", // v1.13.2
        "symfony/yaml": "6.0.*", // v6.0.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.7
        "twig/twig": "^2.12|^3.0" // v3.3.7
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.1
        "symfony/debug-bundle": "6.0.*", // v6.0.2
        "symfony/maker-bundle": "^1.15", // v1.36.4
        "symfony/var-dumper": "6.0.*", // v6.0.2
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.2
        "zenstruck/foundry": "^1.1" // v1.16.0
    }
}
userVoice