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