easy - symfony 4 admin
Cómo implementar relaciones de muchos a muchos en Sonata Media Bundle (2)
Tienes que confiar en MediaBundle Gallery. En tu entidad tienes algo como:
/**
* @ORM/ManyToOne(targetEntity="Application/Sonata/MediaBundle/Entity/Gallery")
* @ORM/JoinColumn(name="image", referencedColumnName="id")
*/
private $images;
Y luego en tu formulario, podrás vincular una Galería a tu objeto con algo como:
->add(''images'', ''sonata_type_model_list'', array(''required'' => false), array(''link_parameters'' => array(''context'' => $context)))
Estoy tratando de relacionar SonataMediaBundle con otra Entidad: Productos con una relación ManyToMany.
El esquema y la relación están bien creados.
Sin embargo, cuando edito o creo un nuevo producto, trato de agregar un botón donde puedo buscar un archivo de medios a través de la biblioteca de medios y un botón para cargar un nuevo archivo.
Para una relación OneToMany, esto se hace fácilmente en Admin/ProductAdmin::configureFormFields
agregando:
->add(''image'', ''sonata_type_model_list'', array(
''required'' => false
), array(
''link_parameters'' => array(
''context'' => ''default'',
''provider'' => ''sonata.media.provider.image''
)
))
Así que obtengo los mismos 3 iconos que ya se usaron en la Galería de SonataMediaBundle ( agregar desde la biblioteca , cargar y eliminar )
¡PERO en la relación ManyToMany no es posible! Porque cada vez que elijo un medio, reemplaza al anterior. Así que no puedo seleccionar varios tipos de medios.
Pensé en usar de la misma manera que The Gallery ( galleryHasMedia
)
->add(''galleryHasMedias'', ''sonata_type_collection'', array(
''by_reference'' => false
), array(
''edit'' => ''inline'',
''inline'' => ''table'',
''sortable'' => ''position'',
''link_parameters'' => array(''context'' => $context)
))
Sin embargo, es realmente complejo. ¿Cómo puedo elegir o cargar varios archivos de medios en otra Entidad a través de una Relación ManyToMany?
Tuve el mismo problema que tú, pero lo he resuelto.
En primer lugar, es posible que desee elegir una relación de uno a muchos / muchos a uno (utilizando una entidad intermedia) en lugar de una relación de muchos a muchos. ¿Por qué? Porque esto permite columnas adicionales, como una columna de position
. De esta manera usted puede reordenar las imágenes de la forma que desee. En una relación de muchos a muchos, la tabla de enlaces solo tiene dos columnas: los ID de las tablas asociadas.
De la documentación de Doctrine :
(...) frecuentemente desea asociar atributos adicionales con una asociación, en cuyo caso usted introduce una clase de asociación. En consecuencia, la asociación directa de muchos a muchos desaparece y es reemplazada por asociaciones de uno a muchos / muchos a uno entre las 3 clases participantes.
Así que agregué esto a mi archivo de mapeo del producto: (como pueden ver, estoy usando YAML como mi formato de archivo de configuración)
oneToMany:
images:
targetEntity: MyBundle/Entity/ProductImage
mappedBy: product
orderBy:
position: ASC
Y creé un nuevo archivo de mapeo de ProductImage:
MyBundle/Entity/ProductImage:
type: entity
table: product_images
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
position:
type: integer
manyToOne:
product:
targetEntity: MyBundle/Entity/Product
inversedBy: images
image:
targetEntity: Application/Sonata/MediaBundle/Entity/Media
Usando la línea de comandos ( php app/console doctrine:generate:entities MyBundle
), creé / actualicé las entidades correspondientes ( Product
e ProductImage
).
A continuación, creé / actualicé las clases de Admin. ProductAdmin.php:
class ProductAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
// define other form fields
->add(''images'', ''sonata_type_collection'', array(
''required'' => false
), array(
''edit'' => ''inline'',
''inline'' => ''table'',
''sortable'' => ''position'',
))
;
}
ProductImageAdmin.php:
class ProductImageAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add(''image'', ''sonata_type_model_list'', array(
''required'' => false
), array(
''link_parameters'' => array(
''context'' => ''product_image''
)
))
->add(''position'', ''hidden'')
;
}
No olvides añadir ambos como servicios. Si no desea que se muestre un enlace al formulario de show_in_dashboard: false
en el tablero, agregue la etiqueta show_in_dashboard: false
. (La forma de hacerlo depende del formato de configuración (yaml / xml / php) que use)
Después de esto tuve el formulario de administrador funcionando correctamente, sin embargo, todavía tenía algunos problemas al intentar guardar productos. Tuve que realizar los siguientes pasos para solucionar todos los problemas:
Primero, tuve que configurar las operaciones de persistencia en cascada para la entidad Producto. Una vez más, cómo hacerlo depende de su formato de configuración. Estoy usando yaml, así que en la relación de images
uno a varios, agregué la propiedad en cascada:
oneToMany:
images:
targetEntity: MyBundle/Entity/ProductImage
mappedBy: product
orderBy:
position: ASC
cascade: ["persist"]
Eso lo hizo funcionar (o eso pensé), pero noté que el product_id
en la base de datos estaba configurado en NULL
. prePersist()
esto agregando los prePersist()
y preUpdate()
a la clase ProductAdmin
:
public function prePersist($object)
{
foreach ($object->getImages() as $image) {
$image->setProduct($object);
}
}
public function preUpdate($object)
{
foreach ($object->getImages() as $image) {
$image->setProduct($object);
}
}
... y agregó una sola línea al método addImages()
de la entidad Product
:
public function addImage(/MyBundle/Entity/ProductImage $images)
{
$images->setProduct($this);
$this->images[] = $images;
return $this;
}
Esto funcionó para mí, ahora puedo agregar, cambiar, reordenar, borrar, etc. imágenes a / desde mis Productos.