1. 前言
不少初学者在写代码时,不注重代码的功能单一性和可扩展性,用一大段代码实现一个功能,导致后续很难阅读和维护。那么,如何优雅地编写好的代码呢?最好的办法就是借鉴和学习别人优秀的写法,时间长了自然而然就能写出令人赏心悦目的代码。下面我们举几个例子来说明。
2. 代码示例
2.1 示例一
假设现在要实现一个垃圾内容检测器,来过滤垃圾回复、标题等内容,怎么实现比较好呢?
在 Laravel 中,我们可以使用 表单验证 来检查提交字段是否满足要求。
首先,我们检查非法关键字:
app/Inspections/InvalidKeywords.php
namespace App\Inspections;
use Exception;
class InvalidKeywords
{
protected $keywords = [
'something forbidden'
];
public function detect($body)
{
foreach ($this->keywords as $invalidKeyword) {
if (stripos($body, $invalidKeyword) !== false) {
throw new Exception('Your reply contains spam.');
}
}
}
}
检查重复输入字符的:
app/Inspections/KeyHeldDown.php
namespace App\Inspections;
use Exception;
class KeyHeldDown
{
public function detect($body)
{
if (preg_match('/(.)\\1{4,}/', $body)) {
throw new Exception('Your reply contains spam.');
}
}
}
统一调用:
app/Inspections/Spam.php
namespace App\Inspections;
class Spam
{
protected $inspections = [
InvalidKeywords::class,
KeyHeldDown::class,
];
public function detect($body)
{
foreach ($this->inspections as $inspection) {
app($inspection)->detect($body);
}
return false;
}
}
新建自定义验证规则:
app/Rules/SpamFree.php
.
.
.
public function passes($attribute, $value)
{
try {
return !app(\App\Inspections\Spam::class)->detect($value);
} catch (\Exception $e) {
return false;
}
}
.
.
.
然后注册自定义规则:
app/Providers/AppServiceProvider.php
.
.
.
public function boot()
{
.
.
.
\Validator::extend('spamfree', \App\Rules\SpamFree::class . '@passes');
}
.
.
.
这样每个检测规则都相对独立,如果我们要补充规则,只需要新建规则并将其添加到 $inspections
属性里面即可。
2.2 示例二
按条件筛选文章为例,根据条件返回对应的文章。如:
https://example.com/articles?by=ilaoniu&unanswered=1
。
我们先创建一个过滤器基类:
app/Filters/Filters.php
namespace App\Filters;
use Illuminate\Http\Request;
class Filters
{
protected $request, $builder;
protected $filters = [];
public function __construct(Request $request)
{
$this->request = $request;
}
public function apply($builder)
{
$this->builder = $builder;
foreach ($this->getFilters() as $filter => $value) {
if (method_exists($this, $filter)) {
$this->$filter($value);
}
}
return $this->builder;
}
protected function getFilters()
{
return $this->request->only($this->filters);
}
}
然后新建文章对应的过滤器:
app/Filters/ArticlesFilters.php
namespace App\Filters;
use App\User;
class ArticlesFilters extends Filters
{
protected $filters = ['by', 'popularity', 'unanswered'];
protected function by($username)
{
$user = User::where('name', $username)->firstOrFail();
return $this->builder->where('user_id', $user->id);
}
protected function popularity()
{
return $this->builder->orderBy('replies_count', 'desc');
}
protected function unanswered()
{
return $this->builder->where('replies_count', 0);
}
}
下一步,在模型中为 定义本地作用域:
app/Article.php
.
.
.
public function scopeFilter($query, $filters)
{
return $filters->apply($query);
}
.
.
.
最后就可以在控制器中很方便地调用了:
app/Http/Controllers/ArticleController.php
.
.
.
public function index(\App\Article $article, \App\Filters\ArticlesFilters $filters)
{
$articles = \App\Article::latest()->filter($filters);
.
.
.
}
.
.
.