我正在使用Laravel 5.1,我需要使用polymorphic many-to-many relationship来限制我提取的相关记录的数量。
我想做的是通过parent_id获取类别列表。对于每个类别,我只想拉四个帖子。
我已经使用了下面的代码,但它导致了一堆额外的查询。如果可能,我想只打一次数据库。如果可能的话,我想使用Laravel / Eloquent框架,但我愿意接受任何有用的工作。
@foreach ($categories as $category)
@if ($category->posts->count() > 0)
<h2>{{ $category->name }}</h2>
<a href="/style/{{ $category->slug }}">See more</a>
{-- This part is the wonky part --}
@foreach ($category->posts()->take(4)->get() as $post)
{{ $post->body }}
@endforeach
@endif
@endforeach
PostsController
public function index(Category $category)
{
$categories = $category->with('posts')
->whereParentId(2)
->get();
return view('posts.index')->with(compact('categories'));
}
数据库
posts
id - integer
body - string
categories
id - integer
name - string
parent_id - integer
categorizables
category_id - integer
categorizable_id - integer
categorizable_type - string
发布模型
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function categories()
{
return $this->morphToMany('App\Category', 'categorizable');
}
类别模型
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
public function category()
{
return $this->belongsTo('App\Category', 'parent_id');
}
public function subcategories()
{
return $this->hasMany('App\Category', 'parent_id')->orderBy('order', 'asc');
}
public function posts()
{
return $this->morphedByMany('App\Post', 'categorizable');
}
我在网上看到过这方面的一些链接,但实际上对我没有任何影响。
I have tried this solution没有任何运气。
$categories = $category->with('posts')
->whereParentId(2)
->posts()
->take(4)
->get();
Jarek在SoftOnTheSofa上发表了{p> I have looked into this solution,但这是为了一个有很多关系而且说实话有点超出我的sql技能,我可以根据自己的需要调整它。
我为此设置添加了github repo,如果它对任何人都有用。
答案 0 :(得分:4)
这对你有用吗?:
$categories = $category->with(['posts' => function($query)
{
$query->take(4);
})
->whereParentId(2)
->get();
答案 1 :(得分:4)
我认为最常用的DBMS方式是使用union all。也许是这样的:
public function index(Category $category)
{
$categories = $category->whereParentId(2)->get();
$query = null;
foreach($categories as $category) {
$subquery = Post::select('*', DB::raw("$category->id as category_id"))
->whereHas('categories', function($q) use ($category) {
$q->where('id', $category->id);
})->take(4);
if (!$query) {
$query = $subquery;
continue;
}
$query->unionAll($subquery->getQuery());
}
$posts = $query->get()->groupBy('category_id');
foreach ($categories as $category) {
$categoryPosts = isset($posts[$category->id]) ? $posts[$category->id] : collect([]);
$category->setRelation('posts', $categoryPosts);
}
return view('posts.index')->with(compact('categories'));
}
然后你就可以在视图中循环浏览类别及其帖子了。不一定是最好看的解决方案,但它会将其减少到2个查询。将其缩减为1个查询可能需要使用窗口函数(特别是row_number()
),如果没有一些技巧来模拟它(More on that here.),MySQL就不支持。不过,我很高兴被证明是错的。
答案 2 :(得分:4)
编辑您的类别模型
public function fourPosts() {
// This is your relation object
return $this->morphedByMany('App\Post', 'categorizable')
// We will join the same instance of it and will add a temporary incrementing
// Number to each post
->leftJoin(\DB::raw('(' . $this->morphedByMany('App\Post', 'categorizable')->select(\DB::raw('*,@post_rank := IF(@current_category = category_id, @post_rank + 1, 1) AS post_rank, @current_category := category_id'))
->whereRaw("categorizable_type = 'App\\\\Post'")
->orderBy('category_id', 'ASC')->toSql() . ') as joined'), function($query) {
$query->on('posts.id', '=', 'joined.id');
// And at the end we will get only posts with post rank of 4 or below
})->where('post_rank', '<=', 4);
}
然后在您的控制器中使用此
获得所有类别$categories = Category::whereParentId(1)->with('fourPosts')->get();
只有四个帖子。要测试这一点(请记住,现在您将使用fourPosts
方法加载帖子,因此您必须使用此属性访问这四个帖子):
foreach ($categories as $category) {
echo 'Category ' . $category->id . ' has ' . count($category->fourPosts) . ' posts<br/>';
}
简而言之,您可以向变形对象添加子查询,以便我们为类别中的每个帖子分配临时递增编号。然后你可以得到临时数小于或等于4的行:)
答案 3 :(得分:1)
这是“半答案”。我给你的一半是向你展示MySQL代码。我无法给你的一半是将它翻译成Laravel。
以下是在加拿大各省寻找人口最多的3个城市的例子:
SELECT
province, n, city, population
FROM
( SELECT @prev := '', @n := 0 ) init
JOIN
( SELECT @n := if(province != @prev, 1, @n + 1) AS n,
@prev := province,
province, city, population
FROM Canada
ORDER BY
province ASC,
population DESC
) x
WHERE n <= 3
ORDER BY province, n;
更多详细信息和说明可在my blog on groupwise-max中找到。
答案 4 :(得分:0)
我认为最好的选择(不使用原始查询)将设置一个自定义方法,以便与预先加载一起使用。
因此,在Category
模型中,您可以执行以下操作:
<?php
public function my_custom_method()
{
return $this->morphedByMany('App\Post', 'categorizable')->take(4);
}
然后在急切加载时使用该方法:
<?php
App\Category::with('my_custom_method')->get();
请务必在视图中使用my_custom_method()
代替posts()
:
$category->my_custom_method()
答案 5 :(得分:0)
您可以尝试这个,对我有用!
$projects = Project::with(['todo'])->orderBy('id', 'asc')->get()->map(function($sql) {
return $sql->setRelation('todo', $sql->todo->take(4));
});