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 (под комментарием ветки) или, может быть, есть другой способ справиться с такой ситуацией?