Symfony: Как отсортировать ArrayCollection в отношении OneToMany (3 объекта)?

У меня есть продукт, который может иметь один или несколько тегов.

По определенной причине у меня есть 3 объекта, которые соответствуют:

  • Product
  • Tag
  • ProductTag (отношение):

Я хочу упорядочить по типу мою коллекцию тегов ArrayCollection. Эта коллекция содержит ProductTag сущности.

Сложность в том, что у меня нет свойства type в ProductType, это свойство находится в Tag сущности.

Продукт

/**
 * @ORM\OneToMany(targetEntity="ProductTag", mappedBy="product", cascade={"persist", "remove"})
 * @Assert\Valid
 */
private $tags;

Тег

/**
 * @ORM\Column(type="string", length=15)
 */
private $type;

ProductTag

/**
 * @ORM\Column(name="Product_id", type="integer")
 * @ORM\Id
 */
private $product_id;

/**
 * @ORM\Column(name="Tag_id", type="integer")
 * @ORM\Id
 */
private $tag_id;

/**
 * @ORM\ManyToOne(targetEntity="Product", inversedBy="tags")
 * @ORM\JoinColumn(name="Product_id", referencedColumnName="Product_id")
 */
private $product;

/**
 * @ORM\ManyToOne(targetEntity="Tag")
 * @ORM\JoinColumn(name="Tag_id", referencedColumnName="Tag_id")
 */
private $tag;

Приведенное ниже решение не может работать, потому что ProductTag не имеет свойства type:

// Product entity

/**
 * @ORM\OneToMany(targetEntity="ProductTag", mappedBy="product", cascade={"persist", "remove"})
 * @ORM\OrderBy({"type" = "ASC"})
 * @Assert\Valid
 */
private $tags;

Я хотел бы сделать что-то подобное @ORM\OrderBy({"tag.type" = "ASC"}).

Любая идея ?

Спасибо.

--- ИЗМЕНИТЬ ---

В PhpMyAdmin я создал представление, которое получает данные тегов и идентификатор продукта для каждого тега.

Затем в Symfony я создал объект, соответствующий моему представлению. В репозитории этой сущности я создал запрос, который объединяет мои теги по их типу.

public function findByProductGroupByType($productId)
{
   return $this->getEntityManager()
        ->createQuery(
            'SELECT v.tagTypeCode, v.tagType,
                GROUP_CONCAT(v.tagCode) as tagCode,
                GROUP_CONCAT(v.tagName) as tagName,
                GROUP_CONCAT(v.picto) as tagPicto
                FROM AppBundle:ProductTagView v
                WHERE v.productId = :id
                GROUP BY v.tagTypeCode
                ORDER BY v.tagType ASC'
        )->setParameter('id', $productId)
        ->getResult();
}

Чтобы он работал, необходимо установить этот пакет, чтобы Symfony распознал GROUP_CONCAT. После установки добавьте это в config.yml:

doctrine:
    orm:
        dql:
            string_functions:
                group_concat: DoctrineExtensions\Query\Mysql\GroupConcat

Полный список находится здесь: https://github.com/beberlei/DoctrineExtensions/blob/master/config/mysql.yml

Запрос возвращает что-то вроде этого:

array (size=2)
  0 => 
    array (size=5)
      'tagTypeCode' => string 'KFEAT' (length=5)
      'tagType' => string 'Key Feature' (length=11)
      'tagCode' => string 'double_sanglage' (length=15)
      'tagName' => string 'double sanglage' (length=15)
      'tagPicto' => string 'double_sanglage.jpg' (length=19)
  1 => 
    array (size=5)
      'tagTypeCode' => string 'SIZE' (length=4)
      'tagType' => string 'Size' (length=4)
      'tagCode' => string 'h26_ceintures,h21_ceintures' (length=27)
      'tagName  => string 'ceintures h26cm,ceintures H21 cm' (length=32)
      'tagPicto' => string 'h26_ceintures.jpg,h21_ceintures.jpg' (length=35)

Теги типа SIZE объединяются разделителем ,.


person Eve    schedule 20.07.2016    source источник
comment
Добавьте getTagType() в свой ProductType, и тогда @ORM\OrderBy({"tagType" = "ASC"}) должно работать.   -  person Jarek Jakubowski    schedule 21.07.2016


Ответы (1)


Вы не можете сделать это напрямую. Но есть обходной путь.

  • Добавьте свойство заказанных тегов к сущности продукта:

    private $orderedTags;
    
    public function __construct()
    {
        //...
        $this->orderedTags = new \Doctrine\Common\Collections\ArrayCollection();
    }
    
  • Создайте новый прослушиватель событий doctrine и зарегистрируйтесь в services.yml

    services:
        my.listener:
            class: AppBundle\EventListener\OrderedTagsListener
            arguments: ["@doctrine.orm.entity_manager"]
            tags:
                - { name: doctrine.event_listener, event: postLoad }
    
    
    
    // src/AppBundle/EventListener/OrderedTagsListener.php
    namespace AppBundle\EventListener;
    
    use Doctrine\ORM\Event\LifecycleEventArgs;
    use AppBundle\Entity\Product;
    
    class OrderedTagsListener
    {
        private $em;
    
        public function __construct($em)
        {
            $this->em = $em;
        }
    
        public function postPersist(LifecycleEventArgs $args)
        {
            $product = $args->getEntity();
    
            // Retrieve your tags ordered with a query defined in your tags repository
            $orderedTags = $this->em->getManager()->getRepository('AppBundle:Tags')->getOrderedTags($product);
    
            $product->setOrderedTags();
        }
    }
    
person Alsatian    schedule 20.07.2016
comment
Спасибо за ответ, я не тестировал, но это правильное решение. Тем временем я создал представление в PhpMyAdmin, которое получает все необходимые мне данные. Затем в Symfony я создал объект, соответствующий этому представлению, и создал запрос в репозитории с GROUP_CONCAT, GROUP BY и ORDER BY для сортировки моих тегов. Я отредактирую свой пост этим решением. - person Eve; 21.07.2016
comment
Хорошо, не забудьте пометить ответ как «принятый», чтобы другие люди не стали искать решение. - person Alsatian; 21.07.2016