只是为了防止这种想法,即这是重复的问题,如这个,这个,或者这个:这些情况是不同的,因为他们谈论的是获得所有B项,按照连接表字段定义的顺序,为一个特定的a项。我说的是查询许多A项,并且对于每个A项,按照连接表中字段定义的顺序急切加载它的B项。
假设您有paper
和author
表,它们是多对多的,通过paper_author
表相关联。但对于学术论文,作者的顺序很重要,因此paper_author
表中有一个数字rank
字段。
现在您想要显示所有论文标题的列表,并在每篇论文的标题旁边列出作者,以适当的顺序。您还需要急切地加载作者,以避免在论文列表有100篇论文时必须运行100个作者查询。
您已经设置了标准的Yii2 ActiveRecord关系。你可以输入
<?php
$papers = Paper::find()->with('authors')->all();
foreach ($papers as $paper) {
echo "Title: " . $paper->title . "'n"
. "Authors: ";
foreach ($paper->authors as $author) {
echo $author->name . " ";
}
echo "'n";
}
?>
然而,每篇论文的作者都没有明确的顺序。好了,你对Paper
类中作者关系的声明做了一个特殊的修改:
<?php
class Paper extends ActiveRecord {
// ...
public function getAuthors() {
return $this
->hasMany(Author::className(), ['id' => 'author_id'])
->viaTable('paper_author', ['paper_id' => 'id'],
// SPECIAL CHANGE: 3rd param to viaTable()
function ($query) {
$query->orderBy('rank');
}
)
}
// ...
}
?>
但是,这不起作用,因为被修改的$query
是paper_author
上的SELECT,其目的是获取author_ids列表。在此之后,对author
表和WHERE id IN (...author_ids...)
运行另一个SELECT查询,但是该查询的结果不尊重给定给IN()函数的id的顺序——这不是IN()的工作方式。
即使您可以使查询的结果遵循该顺序,您也会遇到这个问题:查询返回的作者列表中每个作者只出现一次。这让它更有效率。然而,在您显示的论文列表中,给定的作者可能附属于不止一篇论文。作者顺序(rank
)不是作者的属性;它是作者-论文关系的一个属性。
所以这是一个Yii2能够解决而我不知道的问题吗?还是需要一些自定义的结果处理?
我认为在这种情况下,正确的方法是为连接表定义额外的AR类,因为它不仅是多对多链接,而且还有自己的含义——作者在作者列表中的顺序位置。
所以,当你有像PaperAuthorship
这样的类时(在Paper, PaperAuthorship和Author之间有适当的关系),你可以这样执行你的查询:
$papers = Paper::find()
->with('paperAuthorships', 'paperAuthorships.author')
->all();
foreach ($papers as $paper) {
echo "Title: " . $paper->title . "'n"
. "Authors: ";
foreach ($paper->paperAuthorships as $paperAuthorship) {
echo $paperAuthorship->author->name . " ";
}
echo "'n";
}
并且它将很容易实现正确的排序顺序(例如在关系Paper.paperAuthorships
)