Sylius: Как расширить интерфейс администратора продукта сеткой пользовательской модели

Я расширил модель продукта, включив дополнительный список пар ключ-значение для каждого продукта.

Как это:

продукт A : extraEnt1 => floatvalue1, extraEnt2 => floatvalue2

продукт B : extraEnt1 => floatvalue3, extraEnt2 => floatvalue4

причем extraEnt1 и extraEnt2 сами по себе являются объектами отдельной (дополнительной) сущности. Значения представляют собой простые числа с плавающей запятой. И связь между ключом и значением также является собственной сущностью (ProductExtraAssoc), которая напрямую связана с продуктом как однонаправленная связь «один ко многим».

Теперь я хочу сделать это настраиваемым через интерфейс администратора в продуктах.

Расширение продукта:

      /**
 * @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;
...

Дополнительный объект (я сделал его ресурсом sylius, потому что у него есть собственная сетка, которую можно настроить в интерфейсе администратора в своем собственном пункте главного меню):

      /**
 * @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;
...

Объект ассоциации (должен ли он быть ресурсом? Я не знаю, но подозреваю, что это так, потому что, насколько я вижу, ему нужна собственная сетка):

      /**
 * @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 

Шаблон ветки (это вызывается уже с прослушивателем главного меню с помощью дополнительной кнопки в разделе Таксономия - Атрибуты - Отношения - Медиа - Места, где вы редактируете продукт)

      {% 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>

Поэтому я хочу иметь возможность создавать и редактировать эти дополнительные ассоциации сущностей.

Из кода в ветке выше я получаю сообщение об ошибке:

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

Мне нужно что-то вроде (как показано в шаблоне ветки выше):

      {{call_grid(app_admin_productExtraAssoc, extraAssoc)}}

Это существует?

Есть ли для этого вызов Sylius/twig (под комментарием ветки) или, может быть, есть другой способ справиться с такой ситуацией?

0 ответов

Другие вопросы по тегам