2012-07-23 16 views
18

Estoy tratando de relacionar SonataMediaBundle con otra entidad: Productos con una relación ManyToMany.Cómo implementar relaciones de varios a varios en Sonata Media Bundle

El esquema y la relación están bien creados.

Sin embargo, cuando edito o creo un producto nuevo, trato de agregar un botón donde puedo buscar un archivo multimedia a través de la biblioteca multimedia y un botón para cargar un archivo nuevo.

Para una OneToMany relación, esto se hace fácilmente en Admin\ProductAdmin::configureFormFields añadiendo:

->add('image', 'sonata_type_model_list', array(
        'required' => false 
       ), array(
        'link_parameters' => array(
         'context' => 'default', 
         'provider' => 'sonata.media.provider.image' 
        ) 
       )) 

por lo que obtener los mismos 3 iconos como ya se ha utilizado en la Galería de SonataMediaBundle (añadir a la biblioteca, cargar y eliminar )

PERO sobre la relación de muchos a muchos, no es posible! Porque cada vez que elijo un medio, reemplaza al anterior. Entonces no puedo seleccionar múltiples tipos de medios.

pensé sobre el uso de la misma manera que La Galería (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 compleja. ¿Cómo puedo elegir o cargar varios archivos multimedia en otra entidad a través de una relación ManyToMany?

+0

¿Qué pasa con '-> agregar ('imagen', 'sonata_type_model', matriz ('required' => false, 'multiple' => true))'? –

+0

Puede encontrar ejemplos de muestra [** '@ sonata-media-upload-multiple-images' **] (https://github.com/dianuj89/sonata-media-upload-multiple-images) –

Respuesta

18

tuve el mismo problema que tú , pero lo he descubierto.

En primer lugar, es posible que desee elegir una relación uno a muchos/varios a uno (utilizando una entidad intermedia) en lugar de una relación muchos a muchos. ¿Por qué? Porque esto permite columnas adicionales, como una columna position. De esta manera puede reordenar las imágenes de la forma que desee. En una relación muchos a muchos, la tabla de enlaces solo tiene dos columnas: los id de las tablas asociadas.

Desde el Doctrine documentation:

(...) frecuencia con la que desea asociar atributos adicionales con una asociación, en cuyo caso se introduce una clase de asociación. En consecuencia, la asociación directa de muchos a muchos desaparece y es reemplazada por asociaciones uno a muchos/muchos a uno entre las 3 clases participantes.

por lo que añade esto a mi archivo de asignación del producto: (como se puede ver que estoy usando YAML como mi formato de archivo de configuración)

oneToMany: 
    images: 
     targetEntity: MyBundle\Entity\ProductImage 
     mappedBy: product 
     orderBy: 
      position: ASC 

y yo creamos un nuevo archivo de asignación 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) he creado/modificado las entidades correspondientes (Product y ProductImage).

A continuación, creé/actualicé las clases de administración. 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 se olvide de añadir tanto de ellos como de servicios. Si no desea que se muestre un enlace al formulario ProductImage en el tablero, agregue la etiqueta show_in_dashboard: false. (cómo lo hace depende del formato de configuración (yaml/xml/php) que usa)

Después de esto, tenía 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 Product. De nuevo, cómo hacer esto depende de su formato de configuración. Estoy usando yaml, por lo que en el images relación uno-a-muchos, he añadido la propiedad en cascada:

oneToMany: 
    images: 
     targetEntity: MyBundle\Entity\ProductImage 
     mappedBy: product 
     orderBy: 
      position: ASC 
     cascade: ["persist"] 

Que tengo trabajo (o así que pensé), pero me di cuenta que el product_id en la base de datos se estableció en NULL. He resuelto esto añadiendo prePersist() y preUpdate() métodos 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 añadí una sola línea con el método de la entidad addImages()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, eliminar, etc. imágenes a/desde mis Productos.

+0

Hermosa respuesta, su el uso de las funciones prePersist y preUpdate es perfecto. – marijnz0r

3

Debe confiar en la Galería de MediaBundle. En su entidad que algo como:

/** 
* @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Gallery") 
* @ORM\JoinColumn(name="image", referencedColumnName="id") 
*/ 
private $images; 

Y luego en su forma, podrás vincular una galería a su objeto con algo como:

->add('images', 'sonata_type_model_list', array('required' => false), array('link_parameters' => array('context' => $context))) 
Cuestiones relacionadas