Menu

17.根据回复数筛选话题

本节说明

  • 对应视频第 17 小节:A User Can Filter Threads By Polularity

本节内容

在开始本节的内容之前,让我们先在index页加上回复数。首先更改首页:
forum\resources\views\threads\index.blade.php

.
.
<article>
    <div class="level">
        <h4 class="flex">
            <a href="{{ $thread->path() }}">
                {{ $thread->title }}
            </a>
        </h4>

        <a href="{{ $thread->path() }}">
            {{ $thread->replies_count }} {{ str_plural('reply',$thread->replies_count) }}
        </a>
    </div>

    <div class="body">{{ $thread->body }}</div>
</article>
.
.

接着更改页面布局的样式:
forum\resources\views\layouts\app.blade.php

.
.
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">

    <script>
        window.Laravel = {!! json_encode([
            'csrfToken' => csrf_token(),
        ]) !!};
    </script>

    <style>
        body{ padding-bottom: 100px; }
        .level { display: flex;align-items: center; }
        .flex { flex: 1 }
    </style>
</head>
<body>
    <div id="app">
        .
        .

完成后的页面:
file
现在开始本节的内容:根据回复数来筛选话题。一如既往,首先新建测试:
forum\tests\Feature\ReadThreadsTest.php

.
.
/** @test */
public function a_user_can_filter_threads_by_popularity()
{
    // Given we have three threads
    // With 2 replies,3 replies,0 replies, respectively
    // When I filter all threads by popularity
    // Then they should be returned from most replies to least.
}
.

填充具体代码:

.
.
/** @test */
public function a_user_can_filter_threads_by_popularity()
{
    // Given we have three threads
    // With 2 replies,3 replies,0 replies, respectively
    $threadWithTwoReplies = create('App\Thread');
    create('App\Reply',['thread_id'=>$threadWithTwoReplies->id],2);

    $threadWithThreeReplies = create('App\Thread');
    create('App\Reply',['thread_id'=>$threadWithThreeReplies->id],3);

    $threadWithNoReplies = $this->thread;

    // When I filter all threads by popularity
    $response = $this->getJson('threads?popularity=1')->json();

    // Then they should be returned from most replies to least.
    $this->assertEquals([3,2,0],array_column($response,'replies_count'));
}
.

修改一下functions.php文件:
forum\tests\utilities\functions.php

<?php

function create($class,$attributes = [],$times = null)
{
    return factory($class,$times)->create($attributes);
}

function make($class,$attributes = [],$times = null)
{
    return factory($class,$times)->make($attributes);
}

function raw($class,$attributes = [],$times = null)
{
    return factory($class,$times)->raw($attributes);
}

运行测试:
file
我们需要修改一下index()方法:
forum\app\Http\Controllers\ThreadsController.php

.
.
public function index(Channel $channel,ThreadsFilters $filters)
{
    $threads = $this->getThreads($channel, $filters);

    if(request()->wantsJson()){
        return $threads;
    }

    return view('threads.index',compact('threads'));
}
.
.

再次运行测试,可以清楚地看到两者的顺序不匹配,所以测试未通过:
file

得益于上一节的重构,所以我们现在增加一个popularity筛选条件,只需在$filter = ['by']数组中增加一个popularity再增加一个相应的筛选方法即可:
forum\app\Filters\ThreadsFilters.php

<?php

namespace App\Filters;

use App\User;

class ThreadsFilters extends Filters
{
    protected $filters = ['by','popularity'];

    /**
     * @param $username
     * @return mixed
     */
    protected function by($username)
    {
        $user = User::where('name', $username)->firstOrfail();

        return $this->builder->where('user_id', $user->id);
    }

    /**
     * @return mixed
     */
    public function popularity()
    {
        return $this->builder->orderBy('replies_count','desc');
    }
}

再次运行,依旧失败:
file
我们将具体的sql语句打印出来:
forum\app\Http\Controllers\ThreadsController.php

.
.
protected function getThreads(Channel $channel, ThreadsFilters $filters)
{
    $threads = Thread::latest()->filter($filters);

    if ($channel->exists) {
        $threads->where('channel_id', $channel->id);
    }

    dd($threads->toSql());

    $threads = $threads->get();
    return $threads;
}
.

访问 http://forum.test/threads?popularity=1 :
file
发现我们的前置排序条件是:order by created_at desc。这是因为我们使用了:latest()方法,返回的结果默认按created_at字段倒序:

$threads = Thread::latest()->filter($filters);

修复这个问题,我们需要在根据popularity筛选时,清空其他的order by条件即可:
forum\app\Filters\ThreadsFilters.php

.
.
public function popularity()
{
    $this->builder->getQuery()->orders = [];

    return $this->builder->orderBy('replies_count','desc');
}
.

再次刷新页面:
file
去掉dd()语句再刷新:
file
运行测试:
file
运行全部的测试:
file
现在只剩将排序的链接显示出来:
forum\resources\views\layouts\app.blade.php

.
.
<ul class="dropdown-menu">
    <li><a href="/threads">ALL Threads</a> </li>

    @if(auth()->check())
        <li><a href="/threads?by={{ auth()->user()->name }}">My Threads</a> </li>
    @endif

    <li><a href="/threads?popularity=1">Popular Threads</a> </li>
</ul>
.
.

访问应用:
file

本文章首发在 Laravel China 社区
上一篇 下一篇
讨论数量: 0
发起讨论


暂无话题~
刻意练习,每日精进。
5
点赞
238
浏览
0
讨论

维护者
14
14