将带有教义查询的虚拟字段添加到 symfony 实体

Posted

技术标签:

【中文标题】将带有教义查询的虚拟字段添加到 symfony 实体【英文标题】:Add virtual field with doctrine query to symfony entity 【发布时间】:2017-06-27 21:04:24 【问题描述】:

我的 symfony 项目中有一个实体,看起来像这样:

<?php

namespace AppBundle\Entity;
...
/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\RestaurantRepository")
 */
class Restaurant 

    use TimestampableTrait;

    /**
     * @ORM\Column(type="guid")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="UUID")
     */
    protected $id;
    /**
     * @ORM\Column(type="float", nullable=true)
     */
    protected $latitude;
    /**
     * @ORM\Column(type="float", nullable=true)
     */
    protected $longitude;

我现在已经在我的 EntityRepository 中编写了一个原则查询来选择餐厅以及按位置排序的位置附近的距离。问题是,得到一个结果如下所示的数组:

[0] =>
  (
    [0] => distance1
    [1] => restaurant1
  )
[1] =>
  (
    [0] => distance2
    [1] => restaurant2
  )

我想,距离是餐厅的“虚拟”字段,所以结果数组应该是这样的:

[0] =>
  (
    [0] => restaurant1
  )
[1] =>
  (
    [0] => restaurant2
  )

我的教义查询:

$r = $this->createQueryBuilder('l');
        $r
            ->select('l')
            ->addSelect(
                '( 3959 * acos(cos(radians(' . $cord['latitude'] . '))' .
                '* cos( radians( l.latitude ) )' .
                '* cos( radians( l.longitude )' .
                '- radians(' . $cord['longitude'] . ') )' .
                '+ sin( radians(' . $cord['latitude'] . ') )' .
                '* sin( radians( l.latitude ) ) ) ) as distance'
            );
        $r->orderBy('distance', 'ASC');
        return $r->getQuery()->getResult();

感谢您的帮助!

【问题讨论】:

什么是$cord,它来自哪里?一些数据库支持数据库级别的虚拟字段,但所有计算都必须从同一行的其他列中完成。 【参考方案1】:

我认为仅使用查询是不可能的。我曾经有过类似的任务,需要遍历结果来设置虚拟属性:

$r = $this->createQueryBuilder('l');
$r
    ->select('l')
    ->addSelect(
        '( 3959 * acos(cos(radians(' . $cord['latitude'] . '))' .
        '* cos( radians( l.latitude ) )' .
        '* cos( radians( l.longitude )' .
        '- radians(' . $cord['longitude'] . ') )' .
        '+ sin( radians(' . $cord['latitude'] . ') )' .
        '* sin( radians( l.latitude ) ) ) ) as distance'
    );
$r->orderBy('distance', 'ASC');

$results = $r->getQuery()->getResult();

//  need to loop over results to add the distance to the object itself
$restaurants = array();

foreach ( $results as $result ) 
    $restaurant = $result[1];
    $restaurant->setDistance( $result[0] );
    $restaurants[] = $restaurant;


return $restaurants;

人们可能会争论这种类型的逻辑是否不应该更好地放入添加另一个业务逻辑层的服务类中。对当时的我来说,这是一个有效的解决方案。

我在搜索中使用了距离字段,这就是我在查询中需要它的原因。如果您只需要实体中的距离而不在查询中使用它,您可以轻松地将其移出存储库并创建Event Listener 来设置虚拟属性。

【讨论】:

其他一些解决方案 - 如果您真的想要在您的对象中使用“虚拟”字段,您也可以使用自定义 Hydrator。另一个 - 如果您只想要排序但不需要返回实际距离列,您可以执行 AS HIDDEN distance 并且 Doctrine 不会在结果中返回它。然后,您可以在实体的 getDistance() 方法中复制您的逻辑。

以上是关于将带有教义查询的虚拟字段添加到 symfony 实体的主要内容,如果未能解决你的问题,请参考以下文章

如何在 symfony 2 和教义 2 中自定义 sql 日志?

如何使用 symfony 在教义查询构建器中选择表之间的特定连接?

当 Symfony 教义对象的一个​​属性发生变化时更新另一个属性

Symfony2 - 奏鸣曲 Datagrid 过滤器操作符转换为教义_orm_class 字段失败

教义模式更新总是尝试添加 NOT NULL

如何在 symfony 中将教义升级到 2.6 [重复]