Whip Monstrous Code Into Shape 19-Consider a Guest User Class

Have you ever found yourself in the situation where you're constantly checking, within your views, to see if a user is signed in before proceeding? "If the user is signed in, and if they are subscribed, then...." It gets annoying, doesn't it? Hmm - what alternatives do we have here?


17 节介绍的是使用 Query Objects,18 节介绍的是重构视图,没有太多新东西,简单概括一下。

Query Objects

在控制器中可能会碰到一些比较复杂的查询

public function index()
{
    $orderBy = $sortByPopular ? 'vote_count' : 'update_at';

    $links = CommunityLink::with('votes', 'creator', 'channel')
        ->forChannel($channel)
        ->where('approve', 1)
        ->leftJoin(
            'community_links_votes v', 'community_links_votes.community_link_id', '=', 'community_link.id'
        )
        ->selectRaw('community_links.*, count(community_links_votes.id) as vote_count')
        ->groupBy('community_links_id')
        ->orderBy($orderBy, 'desc')
        ->paginate(10);

    return view('community.links', compact('links'));
}

写在控制器中看起来很杂乱,写在 model 的 scope 里面也行,另外一个思路是把这些查询都封装到一个 Query Objects 中,集中管理。

class CommunityLinksQuery {
    public static function get()
    {
        return CommunityLink::with('votes', 'creator', 'channel')
            ->forChannel($channel)
            ->where('approve', 1)
            ->leftJoin('community_links_votes v', 'community_links_votes.community_link_id', '=', 'community_link.id')
            ->selectRaw('community_links.*, count(community_links_votes.id) as vote_count')
            ->groupBy('community_links_id')
            ->orderBy($orderBy, 'desc')
            ->paginate(10);
    }
}

一方面便于管理,另一方面也可以复用,控制器中调用静态方法就行:$links = CommunityLinksQuery::get().

重构视图

重构视图里面没有太多新的东西,主要是把一个复杂的 html 分解成很多小的部件,用 include 包含,而不是全部写在一个模版里面。

有一个比较有意思的是这么一个例子,比如不同的文章类型会有不同的模版,普通写法可能会这样:

@foreach($articles as $article)
    @if ($article->type == 'text')
        @include('articles.text')
    @elseif($article->type == 'video')
        @include('articles.video')
    @elseif($article->type == 'pic')
        @include('articles.pic')
    @elseif($article->type == 'markdown')
        @include('articles.markdown')
    @endif
@endforeach

文艺写法可以这样:

@foreach($articles as $article)
    @include("articles.{$article->type}")
@endforeach

不知道有没有二逼写法。。。

Guest User Class

这部分是 19 节视频的内容,可以创建一个访客用户以供视图使用,有什么用呢?看看下面的这个例子。

@if(Auth::check() && Auth::user()->isSubscribed())
    <button>下载</button>
@else
    请登录.
@endif

在视图中需要做访问限制的地方经常需要这样写,首先要判断是否登录,然后再判断登录的用户是否有某种权限。如果使用的地方太多,则可以把 Auth::check()Auth::user() 绑定到视图。

在 ServiceProvider 的 boot 方法里面绑定变量:

public function boot()
{
    view()->share('signedIn', auth()->check());
    view()->share('user', auth()->user());
}

视图中可以更新成这样:

@if($signedIn && $user->isSubscribed())
    <button>下载</button>
@else
    请登录.
@endif

另外为什么要判断两次 $signedIn && $user->isSubscribed()?按道理如果 $user 存在的话就说明已经登录了,何必之前先判断是否登录?这是因为如果没有登录,$user的值就是 null,而在null 上调用isSubscribed()方法肯定会出错。

这个时候可以想到,我们在绑定 $user 变量的时候如果没有登录就返回一个空的模型,这样的话无论后面是调用 $user 的方法还有属性都不会出错了。

public function boot()
{
    view()->share('user', auth()->user() ?: new GuestUser() );
}

GuestUser 是扩展 User 的一个子类,通过重写方法甚至属性,可以创建一个虚拟的用户,这样在视图中不管是不是登录,都不用额外的处理未登录的情况。

class GuestUser extends User
{
    public $name = '访客';

    public function isSubscribed()
    {
        return false;
    }
}

在视图中假如要显示登录用户的用户名,再也不用这样写了:{{ $user ? $user->name : '访客' }},取而代之的是 {{ $user->name }}.

相应的,如果还有不同的角色比如管理员什么的,也可以在绑定视图的时候返回对应的管理员用户,通过重写方法或者属性可以很好的"客制化"一个用户,在视图中就没必要写各种判断了。

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!