php - 学说顺序按注释。基于关联实体对实体关联进行排序。

标签 php symfony doctrine-orm

假设有以下带有 Symfony 应用程序的实体。

class List
{
    /**
     * @ORM\OneToMany(targetEntity="ListItem", mappedBy="list")
     * @ORM\OrderBy({"category.title" = "ASC"})
     */
    protected $listItems;
}

class ListItem
{
    /**
     * @ORM\ManyToOne(targetEntity="List", inversedBy="listItems")
     */
    protected $list;

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="listItems")
     */
    protected $category;
}

class Category
{
    /**
     * @ORM\OneToMany(targetEntity="ListItem", mappedBy="cateogory")
     */
    protected $listItems;

    protected $title;
}

不幸的是,orderBy 参数 category.title 在学说中不起作用。我的理解是,最常见的解决方案是在 ListItem 实体上存储额外的属性,例如 $categoryTitle 并在 orderBy 注释中使用这个新字段。例如;

class List
{
    /**
     * @ORM\OneToMany(targetEntity="ListItem", mappedBy="list")
     * @ORM\OrderBy({"categoryTitle" = "ASC"})
     */
    protected $listItems;
}

class ListItem
{
    // --

    protected $categoryTitle
}

这种方法的问题是通过设置方法和/或监听器保持此 $categoryTitle 最新的额外开销,以及显然数据库数据的非规范化。

有没有一种方法可以让我在不降低数据库质量的情况下订购与学说的关联?

最佳答案

为了解决这个问题,我将以下方法添加到我们所有实体扩展的 abstractEntity 中,因此所有实体都可以对其集合进行排序。

以下代码尚未经过任何测试,但对于将来可能遇到此问题的任何人来说,它应该是一个很好的起点。

/**
 * This method will change the order of elements within a Collection based on the given method.
 * It preserves array keys to avoid any direct access issues but will order the elements
 * within the array so that iteration will be done in the requested order.
 *
 * @param string $property
 * @param array  $calledMethods
 *
 * @return $this
 * @throws \InvalidArgumentException
 */
public function orderCollection($property, $calledMethods = array())
{
    /** @var Collection $collection */
    $collection = $this->$property;

    // If we have a PersistentCollection, make sure it is initialized, then unwrap it so we
    // can edit the underlying ArrayCollection without firing the changed method on the
    // PersistentCollection. We're only going in and changing the order of the underlying ArrayCollection.
    if ($collection instanceOf PersistentCollection) {
        /** @var PersistentCollection $collection */
        if (false === $collection->isInitialized()) {
            $collection->initialize();
        }
        $collection = $collection->unwrap();
    }

    if (!$collection instanceOf ArrayCollection) {
        throw new InvalidArgumentException('First argument of orderCollection must reference a PersistentCollection|ArrayCollection within $this.');
    }

    $uaSortFunction = function($first, $second) use ($calledMethods) {

        // Loop through $calledMethods until we find a orderable difference
        foreach ($calledMethods as $callMethod => $order) {

            // If no order was set, swap k => v values and set ASC as default.
            if (false == in_array($order, array('ASC', 'DESC')) ) {
                $callMethod = $order;
                $order = 'ASC';
            }

            if (true == is_string($first->$callMethod())) {

                // String Compare
                $result = strcasecmp($first->$callMethod(), $second->$callMethod());

            } else {

                // Numeric Compare
                $difference = ($first->$callMethod() - $second->$callMethod());
                // This will convert non-zero $results to 1 or -1 or zero values to 0
                // i.e. -22/22 = -1; 0.4/0.4 = 1;
                $result = (0 != $difference) ? $difference / abs($difference): 0;
            }

            // 'Reverse' result if DESC given
            if ('DESC' == $order) {
                $result *= -1;
            }

            // If we have a result, return it, else continue looping
            if (0 !== (int) $result) {
                return (int) $result;
            }
        }

        // No result, return 0
        return 0;
    };

    // Get the values for the ArrayCollection and sort it using the function
    $values = $collection->getValues();
    uasort($values, $uaSortFunction);

    // Clear the current collection values and reintroduce in new order.
    $collection->clear();
    foreach ($values as $key => $item) {
        $collection->set($key, $item);
    }

    return $this;
}

然后可以像下面这样调用该方法来解决原始问题

$list->orderCollection('listItems', array('getCategory' => 'ASC', 'getASecondPropertyToSortBy' => 'DESC')) 

关于php - 学说顺序按注释。基于关联实体对实体关联进行排序。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21034895/

相关文章:

forms - 交响乐 3 | CollectionType 实体的自定义约束

php - 如何在 PHP 中使用 Doctrine2 设置 SSL 加密的 MySQL 连接(不是 Symfony,不是 Doctrine1)

php - 我们可以在 symfony2 的多个包中使用通用( Doctrine )orm 文件吗?

php - Facebook 标签高度滚动条

php - 如何在 Laravel 中解析 "ErrorException in VerifyCsrfToken.php"

symfony - 在symfony2中通过IMAP检索邮件的正确方法

php - Ionic 应用程序将表单数据发送到 mysql

file-upload - 在Symfony中上载文件时"Access denied"调用出现'rename'错误

php - 如何在 Doctrine 中使用 CONVERT() 函数 MySQL

php - Doctrine2 基础;代理、存储库