php - 在 Repository 中使用 Doctrine 2 ORM 和 OneToOne 获得“排名”
问题描述
我发现几乎不可能达到我想要的结果,经过多次尝试在这里和其他网站上找到不同的解决方案(子查询、原始查询等),我必须问这个问题。
我的目标是根据他们的“分数”提取/获取每个“项目”的排名。
将“分数”视为 int 并具有 1、2、6、4、8、10、200 等值。
排名应该是这样的:
排名 - 分数
- 200
- 10
- 8
- 6
为了使我的问题尽可能简单明了,我将我的实际表/实体重命名如下:
主要实体( main_table):
/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;
// other fields, un-related to this question
/**
* @ORM\OneToOne(targetEntity="Application\Entity\SecondTable", mappedBy="second_table_data")
*/
protected $second_table;
/**
* @ORM\OneToOne(targetEntity="Application\Entity\ThirdTable", mappedBy="third_table_data")
*
*/
protected $third_table;
第二实体(second_table):
/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;
// other fields, un-related to this question
/**
* @ORM\OneToOne(targetEntity="Application\Entity\SecondTable", inversedBy="second_table")
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
private $second_table_data;
第三实体( third_table):
/**
* @ORM\Id
* @ORM\Column(name="id")
* @ORM\GeneratedValue
*/
protected $id;
// other fields, un-related to this question
/**
* @ORM\Column(name="score")
*/
protected $score;
/**
* @ORM\OneToOne(targetEntity="Application\Entity\ThirdTable", inversedBy="third_table")
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
private $third_table_data;
以及按分数排序的选择“所有项目”的存储库功能:
public function findAllProjects()
{
$entityManager = $this->getEntityManager();
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select('u')
->from(MainEntity::class, 'u')
->leftJoin('u.third_table', 't')
->orderBy('t.score', 'DESC');
return $queryBuilder->getQuery()->getResult();
}
这很好用(我相信),因为我根据它们的“project_id”从main_table + second_table + third_table获得所有“项目”。
但是问题是我找不到正确计算或获取每个项目的“排名”数字的方法。我还尝试使用“foreach”并将“index”用作“rank”,但这无法正常工作,因为我使用的是ORMPaginator,因此每次单击“页面”时,“foreach”“index”将从 0 重置.
我希望这个问题足够清楚,让您清楚地了解我的问题。
请告知我该如何实现这一点,如果我的整个方法是错误的,请指出。
每个建议/提示/解决方案都受到高度赞赏。
解决方案
首先,要真正注意列类型。您的所有列都是由学说 as 创建的varchar(255)
,如果这些列是您想要在其上创建order by
. 这是以下输出的片段vendor/bin/doctrine-module orm:schema-tool:create --dump-sql
:
CREATE TABLE main_entity (
id VARCHAR(255) NOT NULL,
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
因此,您必须做的第一件事是将列类型添加到列中:
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue
*/
protected $id;
我使用了这些数据:
third_table main_entity
+----+------------+-------+ +----+
| id | project_id | score | | id |
+----+------------+-------+ +----+
| 1 | 1 | 8 | | 1 |
| 2 | 2 | 10 | | 2 |
| 3 | 3 | 6 | | 3 |
| 4 | 4 | 200 | | 4 |
+----+------------+-------+ +----+
关于排名,实质上是行号,SQL 查询是:
SELECT
ROW_NUMBER() OVER (ORDER BY t.score DESC) AS r_rank,
t.score AS score
FROM main_entity m
LEFT JOIN third_table t ON m.id = t.project_id
ORDER BY t.score DESC
-- LIMIT 2 OFFSET 2 -- Limits for pagination
without limits with limits
+--------+-------+ +--------+-------+
| r_rank | score | | r_rank | score |
+--------+-------+ +--------+-------+
| 1 | 200 | | 3 | 8 |
| 2 | 10 | | 4 | 6 |
| 3 | 8 | +--------+-------+
| 4 | 6 |
+--------+-------+
不幸的是,ROW_NUMBER
没有为 SQL 查询实现。如果你试试这个:
$queryBuilder->select(['ROW_NUMBER() OVER (ORDER BY t.score DESC)','t.score'])
->from(MainEntity::class, 'u')
->leftJoin('u.third_table', 't')
->orderBy('t.score', 'DESC');
$queryBuilder->getQuery()->getResult();
您将收到以下错误:[Syntax Error] line 0, col 7: Error: Expected known function, got 'ROW_NUMBER'
替代方法可能是:
$sql = 'SELECT
ROW_NUMBER() OVER (ORDER BY t.score DESC) AS r_rank,
t.score AS score
FROM main_entity m
LEFT JOIN third_table t ON m.id = t.project_id
ORDER BY t.score DESC';
$results = $this->entityManager->getConnection()->executeQuery($sql);
我看到分页器步行器中有类似的东西(?!),但并非所有数据库都支持它们,正如您在步行器本身中看到的那样:
public function walkSelectStatement(SelectStatement $AST)
{
if ($this->platformSupportsRowNumber()) {
return $this->walkSelectStatementWithRowNumber($AST);
}
return $this->walkSelectStatementWithoutRowNumber($AST);
}
private function platformSupportsRowNumber()
{
return $this->platform instanceof PostgreSqlPlatform
|| $this->platform instanceof SQLServerPlatform
|| $this->platform instanceof OraclePlatform
|| $this->platform instanceof SQLAnywherePlatform
|| $this->platform instanceof DB2Platform
|| (method_exists($this->platform, 'supportsRowNumberFunction')
&& $this->platform->supportsRowNumberFunction());
}
作为最后的机会,您可以自己“添加”索引,即使使用分页器也是如此。
如果您知道页面大小和当前页面,则可以计算排名:
$pageSize = 2;
$currentPage = 1;
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('t.score') // Here I use only the t.score, but you could put the whole class
->from(MainEntity::class, 'u')
->leftJoin('u.third_table', 't')
->orderBy('t.score', 'DESC');
$queryBuilder->setMaxResults($pageSize);
$queryBuilder->setFirstResult(($currentPage - 1) * $pageSize);
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($queryBuilder->getQuery());
$adapter = new \DoctrineORMModule\Paginator\Adapter\DoctrinePaginator($paginator);
$results = [];
$currentItem = 1 + ($currentPage - 1) * $pageSize;
foreach($adapter->getPaginator()->getIterator()->getArrayCopy() as $result){
$results[] = [
'rank' => $currentItem++,
'item' => $result
];
}
var_dump($results);
结果:
$pageSize = 2; $pageSize = 2;
$currentPage = 1; $currentPage = 2;
Array Array
( (
[0] => Array [0] => Array
( (
[rank] => 1 [rank] => 3
[item] => Array [item] => Array
( (
[score] => 200 [score] => 8
) )
) )
[1] => Array [1] => Array
( (
[rank] => 2 [rank] => 4
[item] => Array [item] => Array
( (
[score] => 10 [score] => 6
) )
) )
) )
推荐阅读
- python - Python写入文件进程在关闭前崩溃
- python - 如何使用 OpenCV 评估 LED 的状态?
- .htaccess - 使用与子文件夹前缀匹配的 .htaccess URL 重定向
- java - 如何在 Mapbox 中为静态图像添加折线叠加?
- php - 从 json 文件播种 laravel 应用程序时插入空值
- clojure - 如何在clojure中获取字符串的所有三元组
- javascript - 有没有办法自动将字符串“附加”到 s3 中已经存在的文件名?
- algorithm - 可接受的启发式修改
- python - 我不知道如何从 optimization.minimize 函数中发送一个数组
- influxdb - 如何找出 Ubuntu 上默认的 influxDB 数据存储位置在哪里?