话题排序
虽然我们已经有话题列表,不过目前只有一种排序逻辑,本章节中,我们将让话题列表支持『最后回复』和『最新发布』排序:
我们可以通过 URI 传参 order 给控制器,控制器根据此参数来决定数据的读取逻辑。因为『分类下的话题列表』也会用到排序,并且是在不同的控制器中,所以在此处为了复用性考虑,我们将会把排序逻辑代码放置于 Topic 数据模型中。
作为一个合格的程序员,编码时需时刻注意代码复用性。
接下来的步骤是:
- Topic 中编写排序逻辑;
- TopicsController 控制器中调用;
- CategoriesController 控制器中调用。
1. 编写排序逻辑
app/Models/Topic.php
/**
* 排序逻辑
*/
public function scopeWithOrder($query, $order)
{
// 不同的排序,使用不同的数据读取逻辑
switch ($order) {
case 'recent':
$query->recent();
break;
default:
$query->recentReplied();
break;
}
// 预加载防止 N+1 问题
return $query->with('user', 'category');
}
public function scopeRecentReplied($query)
{
// 当话题有新回复时,我们将编写逻辑来更新话题模型的 reply_count 属性,
// 此时会自动触发框架对数据模型 updated_at 时间戳的更新
return $query->orderBy('updated_at', 'desc');
}
//创建时间排序
public function scopeRecent($query)
{
// 按照创建时间排序
return $query->orderBy('created_at', 'desc');
}
这里我们使用了 Laravel 。本地作用域允许我们定义通用的约束集合以便在应用中复用。要定义这样的一个作用域,只需简单在对应 Eloquent 模型方法前加上一个 scope 前缀,作用域总是返回 。一旦定义了作用域,则可以在查询模型时调用作用域方法。在进行方法调用时不需要加上 scope 前缀。如以上代码中的 recent() 和 recentReplied() 。
2. 控制器中调用
接下来我们在话题控制器中链式调用定义的方法 withOrder() :
app/Http/Controllers/TopicsController.php
// 话题首页
public function index(Request $request, Topic $topic)
{
$topics = $topic->withOrder($request->order)->paginate(20);
return view('topics.index', compact('topics'));
}
$request->order 是获取 URI larabbs.test/topics?orde… 中的 order 参数。
接下来修改模板,我们需要为按钮添加链接还有选中状态:
resources/views/topics/index.blade.php
<div class="card-header bg-transparent">
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link {{ active_class( ! if_query('order', 'recent')) }}" href="{{ Request::url() }}?order=default">
最后回复
</a>
</li>
<li class="nav-item">
<a class="nav-link {{ active_class(if_query('order', 'recent')) }}" href="{{ Request::url() }}?order=recent">
最新发布
</a>
</li>
</ul>
</div>
Request::url() 获取的是当前请求的 URL,查看页面,通过 Laravel 开发者工具类查看读取列表数据的 SQL 请求,根据 updated_at 字段来排序:
3. 分类话题列表排序
接下来我们在分类控制器中调用方法 withOrder() :
app/Http/Controllers/CategoriesController.php
<?php
namespace App\Http\Controllers;
use App\Models\Category;
use App\Models\Topic;
use Illuminate\Http\Request;
class CategoriesController extends Controller
{
// 类别详情页
public function show(Category $category, Request $request, Topic $topic)
{
// 读取分类 ID 关联的话题,并按每 20 条分页
$topics = $topic->withOrder($request->order)
->where('category_id', $category->id)
->paginate(20);
// 传参变量话题和分类到模板中
return view('topics.index', compact('topics', 'category'));
}
}
测试分类下的排序: