Подтвердить что ты не робот

Как реализовать отношения "многие-ко-многим" в пакете "Соната-медиа"

Я пытаюсь связать SonataMediaBundle с другим Entity: Продукты с отношением ManyToMany.

Схема и отношение хорошо созданы.

Однако, когда я редактирую или создаю новый продукт, я пытаюсь добавить кнопку, где я могу искать файл мультимедиа через медиабиблиотеку и кнопку для загрузки нового файла.

Для отношения OneToMany это легко сделать в Admin\ProductAdmin::configureFormFields, добавив:

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

Итак, я получаю те же 3 значка, которые уже были использованы в Галерее SonataMediaBundle (добавление из библиотеки, загрузка и удаление)

НО по отношению ManyToMany это невозможно! Поскольку каждый раз, когда я выбираю носитель, он заменяет предыдущий. Поэтому я не могу выбрать несколько типов мультимедиа.

Я думал о том, как использовать "Галерея" (galleryHasMedia)

->add('galleryHasMedias', 'sonata_type_collection', array(
            'by_reference' => false
        ), array(
            'edit'     => 'inline',
            'inline'   => 'table',
            'sortable' => 'position',
            'link_parameters' => array('context' => $context)
        ))

Однако это действительно сложно. Как я могу выбрать или загрузить несколько медиафайлов в другой Entity через ManyToMany Relation?

4b9b3361

Ответ 1

У меня была такая же проблема, как у вас, но я понял это.

Прежде всего, вы можете выбрать для отношения "один-ко-многим/много-к-одному" (используя промежуточный объект) вместо отношения "многие ко многим". Зачем? Потому что это позволяет использовать дополнительные столбцы, такие как столбец position. Таким образом вы можете изменить порядок изображений по своему желанию. В отношении "многие ко многим" таблица ссылок имеет только два столбца: идентификатор связанных таблиц.

Из Документация Doctrine:

(...) часто вы хотите связать дополнительные атрибуты с ассоциацией, и в этом случае вы вводите класс ассоциации. Следовательно, прямая ассоциация "многие-ко-многим" исчезает и заменяется ассоциациями "один-много-много-много-один" между 3 участвующими классами.

Итак, я добавил это в файл сопоставления продукта: (как вы можете видеть, я использую YAML в качестве формата файла конфигурации)

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

И я создал новый файл сопоставления 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

Используя командную строку (php app/console doctrine:generate:entities MyBundle), я создал/обновил соответствующие объекты (Product и ProductImage).

Далее я создал/обновил классы 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')
        ;
    }

Не забудьте добавить оба из них в качестве сервисов. Если вы не хотите, чтобы ссылка на форму ProductImage отображалась на панели управления, добавьте тег show_in_dashboard: false. (как вы это делаете, зависит от используемого вами формата конфигурации (yaml/xml/php))

После этого у меня была правильная форма администратора, однако у меня все еще были проблемы с попыткой сохранить продукты. Я должен был выполнить следующие шаги, чтобы исправить все проблемы:

Во-первых, мне пришлось настраивать операции кэширования для объекта Product. Опять же, как это сделать, зависит от вашего формата конфигурации. Я использую yaml, поэтому в отношениях images one-to-many я добавил свойство cascade:

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

Это заставило его работать (или так я думал), но я заметил, что product_id в базе данных был установлен в NULL. Я решил это, добавив методы prePersist() и preUpdate() в класс 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);
    }
}

... и добавил одну строку к методу addImages() объекта Product:

public function addImage(\MyBundle\Entity\ProductImage $images)
{
    $images->setProduct($this);
    $this->images[] = $images;

    return $this;
}

Это сработало для меня, теперь я могу добавлять, изменять, изменять порядок, удалять и т.д. изображения в/из моих продуктов.

Ответ 2

Вам нужно полагаться на галерею MediaBundle. В вашей организации вам что-то нравится:

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

И затем в вашей форме вы сможете связать Галерею с вашим объектом с чем-то вроде:

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