原则orderBy注释。基于相关实体订购实体关联。 [英] Doctrine orderBy annotation. Ordering Entity associations based on an associated Entity.

查看:101
本文介绍了原则orderBy注释。基于相关实体订购实体关联。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Symfony应用程序说出有以下实体。

 类列表
{
/ * *
* @ 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类别
{
/ **
* @ ORM\OneToMany(targetEntity =ListItem,mappedBy =cateogory)
* /
protected $ listItems;

protected $ title;
}

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

 类列表
{
/ **
* @ ORM\ OneToMany(targetEntity =ListItem,mappedBy =list)
* @ ORM\OrderBy({categoryTitle=ASC})
* /
protected $ listItems;
}

class ListItem
{
// -

protected $ categoryTitle
}

这种方法的问题是保持这个 $ categoryTitle ,最新的方法和/或监听器,显然是数据库数据的非规范化。



有没有一种方法可以用来订购这个与原则的关联,没有降低我的数据库的质量?

解决方案

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



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

  / ** 
*此方法将根据给定的方法更改集合中元素的顺序。
*它保留数组键以避免任何直接访问问题,但将在数组中排列元素
*,以便迭代将按要求的顺序完成。
*
* @param string $ property
* @param array $ calledMethods
*
* @return $ this
* @throws \InvalidArgumentException
* /
public function orderCollection($ property,$ calledMethods = array())
{
/ ** @var Collection $ collection * /
$ collection = $这 - > $财产;

//如果我们有一个PersistentCollection,请确保它被初始化,然后打开它,所以我们
//可以编辑底层的ArrayCollection,而不会在
/ / PersistentCollection我们只会进入并改变底层ArrayCollection的顺序。
if($ collection instanceOf PersistentCollection){
/ ** @var PersistentCollection $ collection * /
if(false === $ collection-> isInitialized()){
$收藏 - >初始化();
}
$ collection = $ collection-> unwrap();
}

if(!$ collection instanceOf ArrayCollection){
throw new InvalidArgumentException('orderCollection的第一个参数必须引用$ this中的PersistentCollection | ArrayCollection)。
}

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

//循环通过$ calledMethods,直到找到有序的差异
foreach($ calledMethods as $ callMethod => $ order){

//如果没有设置顺序,则交换k => v值并将ASC设置为默认值。
if(false == in_array($ order,array('ASC','DESC'))){
$ callMethod = $ order;
$ order ='ASC';
}

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

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

} else {

//数值比较
$差异=($ first-> $ callMethod() - $ second-> $ callMethod() );
//这将将非零$结果转换为1或-1或零值为0
//即-22/22 = -1; 0.4 / 0.4 = 1;
$ result =(0!= $ difference)? $ difference / abs($ difference):0;
}

//如果DESC给定
,则'反向'结果if('DESC'== $ order){
$ result * = -1;
}

//如果我们有一个结果返回它,否则继续循环
if(0!==(int)$ result){
return int)$ result;
}
}

//没有结果,返回0
返回0;
};

//获取ArrayCollection的值并使用函数
$ values = $ collection-> getValues()进行排序;
uasort($ values,$ uaSortFunction);

//清除当前的集合值,并以新的顺序重新引入。
$ collection-> clear();
foreach($ values as $ key => $ item){
$ collection-> set($ key,$ item);
}

return $ this;
}

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

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


Say have the following entities with a Symfony app.

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;
}

The orderBy argument, category.title unfortunately will not work in doctrine. My understanding is that the most common solution is to store an extra property on the ListItem Entity such as $categoryTitle and using this new field in the orderBy annotation. For example;

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

class ListItem
{
    // --

    protected $categoryTitle
}

The problem with this approach is the extra overhead of keeping this $categoryTitle, up to date through set methods and/or listeners, and obviously the denormalization of database data.

Is there a method I can use to order this association with doctrine, without degrading the quality of my database?

解决方案

To solve this issue, I added the following method to the abstractEntity that all of our entities extend, and therefore all entities can sort their collections.

The following code has hasn't under gone any tests, but it should be a good starting point for anyone that might have this issue in future.

/**
 * 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;
}

This method then could then be called somewhat like the below to solve the original question

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

这篇关于原则orderBy注释。基于相关实体订购实体关联。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆