How to extend product admin interface with grid of custom model

I extended the product model to include an extra list of key-value pairs per product.

Like this:

product A : extraEnt1 => floatvalue1, extraEnt2 => floatvalue2

product B : extraEnt1 => floatvalue3, extraEnt2 => floatvalue4

with extraEnt1 and extraEnt2 being for themselves objects of a separate (extra) entity. The values are simple float numbers. And the association between key and value is it’s own entity (ProductExtraAssoc) as well that is directly connected to the product as a unidirectional one-to-many.

Now I want to make this configurable through the admin interface under products.

The product extension:

/**
 * @ORM\Entity(repositoryClass="App\Repository\Extension\ProductRepository")
 * @ORM\Table(name="sylius_product")
 */
class Product extends BaseProduct
{
    /**
     * Unidirectional one-to-many:
     * @ManyToMany(targetEntity="App\Entity\ProductExtraAssoc")
     * @JoinTable(name="products_extraAssocs",
     *      joinColumns={@JoinColumn(name="product_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="productExtraAssoc_id", referencedColumnName="id", unique=true)}
     *      )
     */
    protected $productExtraAssocs;
...

The extra entity (I made it a sylius resource because it has it’s own grid configurable in the admin interface under it’s own main menu item):

/**
 * @ORM\Entity(repositoryClass="App\Repository\ExtraRepository")
 * @ORM\Table(name="extra")
 */
class Extra implements ResourceInterface
{

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @var string
     * @ORM\Column(type="string", length=255)
     */
    protected $name;
...

The association entity (does it need to be a resource? I don’t know but I suspect so because it needs it own grid as far as I see):

/**
 * @ORM\Entity(repositoryClass="App\Repository\ProductExtraAssocRepository")
 * @ORM\Table(name="productExtraAssoc")
 */
class ProductExtraAssoc implements ResourceInterface
{

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @OneToOne(targetEntity="Extra")
     * @JoinColumn(name="extra_id", referencedColumnName="id")
     */
    protected $extra;

    /**
     * @ORM\Column(type="float")
     */
    protected $value;
...

ProductExtraAssocType:

class ProductExtraAssocType extends AbstractResourceType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {

        $builder
            ->add('extra', EntityType::class, [
                'class' => Extra::class,
                'label' => 'Extra',
                'required' => true,
            ])
            ->add('value', TextType::class, [
                'label' => 'Value',
                'required' => true,
            ]);
    }


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

ProductExtraAssocListType:

class ProductExtraAssocListType extends AbstractResourceType
{

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {

        $builder->add('extras', CollectionType::class, array(
            // each entry in the array will be an "extra" field
            'entry_type'   => ProductExtraAssocType::class,
            // these options are passed to each "extra" type
            'entry_options' => ['label' => false],
            'data_class' => null,
        ))
        ;

    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix(): string
    {
        return 'product_extra_assoc_list';
    }

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

}

ProductFormExtension:

class ProductFormExtension extends AbstractTypeExtension
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('productExtraAssocs', ProductExtraAssocListType::class, [
                'label' => 'extras',
            ])
        ;
    }

    public function getParent(): string
    {
        return BaseProductType::class;
    }

    /**
     * {@inheritdoc}
     */
    public static function getExtendedType(): string
    {
        return ProductTypeExtension::class;
    }

    /**
     * {@inheritdoc}
     */
    public static function getExtendedTypes(): iterable
    {
        return [BaseProductType::class];
    }
}

services.yaml:

app.form.extension.type.product:
    class: App\Form\Extension\ProductFormExtension
    tags:
        - { name: form.type_extension, extended_type: Sylius\Bundle\CoreBundle\Form\Extension\ProductTypeExtension, priority: -5 }

app.form.type.product_extra_assoc_list:
    class: App\Form\ProductExtraAssocListType
    arguments: [ 'App\Entity\ProductExtraAssoc' ]
    tags:
        - { name: 'form.type' }

app.form.type.product_extra_assoc:
    class: App\Form\ProductExtraAssocType
    arguments: [ '%sylius.model.product.class%' ]
    tags:
        - { name: 'form.type' }

sylius_resource.yaml:

sylius_resource:
    resources:
        app.productExtraAssoc:
            classes:
                model: App\Entity\ProductExtraAssoc
        app.extra:
            classes:
                model: App\Entity\Extra

_sylius.yaml

sylius_grid:
    grids:                                                                              
        app_admin_productExtraAssoc:
            driver:
                options:
                    class: App\Entity\ProductExtraAssoc
            fields:
                extra:
                    type: app_admin_extra
                    label: extra
                value:
                    type: float
                    label: percentage
            actions:
               main:
                    create:
                       type: create                  
                    update:
                        type: update
                    delete:
                        type: delete
        app_admin_extra:
            driver:
                options:
                    class: App\Entity\Extra
            fields:
                name:
                    type: string
                    label: sylius.ui.name
                enabled:
                    type: twig
                    label: sylius.ui.enabled
                    options:
                        template: "@SyliusUi/Grid/Field/enabled.html.twig"
            actions:
                main:
                    create:
                        type: create
                item:
                    update:
                        type: update
                    delete:
                        type: delete 

The twig template (this is called already with a main menu listener through an extra button under Taxonomy - Attributes - Relations - Media - Locations where you edit the product)

{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationFormWithSlug %}
<div class="ui tab" data-tab="extras">
    <h3 class="ui top attached header">{# 'sylius.ui.details'|trans #}Extras</h3>

    <div class="ui attached segment">

        {{ form_errors(form) }}
        {{ form_start(form) }}

        {% for extraAssoc in product.productExtraAssocs %}
            {# This produces the error message beneath: #}
            {{ form_row(extraAssoc) }}


            {# I would need something like this here: #}
            {{call_grid(app_admin_productExtraAssoc, extraAssoc)}}

        {% endfor %}
        {{ form_end(form) }}

    </div>

</div>

So I want to be able to create and edit these extra entity associations there.

From the code in the twig above I get the error message:

Argument 1 passed to Symfony\Component\Form\FormRenderer::searchAndRenderBlock() must be an instance of Symfony\Component\Form\FormView, instance of App\Entity\ProductExtraAssoc given

I would need something like (as shown in the twig template above):

{{call_grid(app_admin_productExtraAssoc, extraAssoc)}}

Does this exist?

Is there a Sylius/twig call for this (under the comment of the twig) or is there maybe a different way of handling such a situation?