Laravel 你应该知道的几个最佳实践
一些常见的最佳实践,整理一下,如果你有更好的建议,可以一起讨论。
单一职责
一个类或一个方法应该只做一件事
不推荐:
public function getFullNameAttribute()
{
if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' $this->last_name;
} else {
return $this->first_name[0] . '. ' . $this->last_name;
}
}
推荐使用:
public function getFullNameAttribute()
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerfiedClient()
{
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong()
{
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort()
{
return $this->first_name[0] . '. ' . $this->last_name;
}
胖模型 & 瘦控制器
一些常用的查询,应该把它们封装到模型中,控制器不应该知道太多事情。
不推荐:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
推荐使用:
// Controller
public function index()
{
return view('index', ['clients' => $this->client->getWithNewOrders()]);
}
// Model
Class Client extends Model
{
public function getWithNewOrders()
{
return $this->verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
}
}
业务逻辑应该在 Service 类中
本着单一职责,我们应该把控制器中的业务逻辑抽离到 Service 类里。
不推荐:
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
....
}
推荐使用:
// Controller
public function store(Request $request)
{
$this->articleService->handleUploadedImage($request->file('image'));
....
}
// Service
class ArticleService
{
public function handleUploadedImage($image)
{
if (!is_null($image)) {
$image->move(public_path('images') . 'temp');
}
}
}
不写重复的逻辑代码 (DRY)
如果你践行 单一职责,其实已经很大程度上避免了 DRY。
不推荐:
public function getActive()
{
return $this->where('verified', 1)->get();
}
public function getArticles()
{
return $this->whereHas('user', function ($q) {
$q->where('verified', 1);
})->get();
}
推荐使用:
public function scopeActive($q)
{
return $q->where('verified', 1);
}
public function getActive()
{
return $this->active()->get();
}
public function getArticles()
{
return $this->whereHas('user', function ($q) {
$q->active();
})->get();
}
尽量使用 Eloquent 而不是原生 SQL 语句
Eloquent 编写的查询可读性比原生 SQL 要好很多。而且 Eloquent 本身带有触发器,软删除,局部查询等。
你是更喜欢读:
SELECT *
FROM `articles`
WHERE EXISTS (SELECT *
FROM `users`
WHERE `articles`.`user_id` = `users`.`id`
AND EXISTS (SELECT *
FROM `profiles`
WHERE `profiles`.`user_id` = `users`.`id`)
AND `users`.`deleted_at` IS NULL)
AND `verified` = '1'
AND `active` = '1'
ORDER BY `created_at` DESC
还是这种:
Article::has('user.profile')->verified()->latest()->get();
在声明方法或变量名时尽量描述他们的作用
最差的:
if (count((array) $builder->getQuery()->joins) > 0)
好一点的:
// 判断是否存在连接
if (count((array) $builder->getQuery()->joins) > 0)
最佳的:
使用方法来封装它,并且起一个一目了然的名字。
if ($this->hasJoins())
用语言文件和常量替代文字,而不是将其写死
不推荐:
public function isNormal()
{
return $article->type === 'normal';
}
return back()->with('message', 'Your article has been added!');
推荐的:
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
不要直接获取 .env 文件中的配置
env()
函数在配置文件之外使用。如果配置被缓存,你将获取不到它。
不推荐:
$apiKey = env('API_KEY');
推荐使用:
// 配置 config/api.php
'key' => env('API_KEY'),
// 获取
$apiKey = config('api.key');
以标准格式存储日期,使用访问器和属性转换来修改日期格式
不推荐:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
推荐使用:
// Model
protected $dates = ['ordered_at', 'created_at', 'updated_at']
public function getMonthDayAttribute($date)
{
return $date->format('m-d');
}
// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->monthDay }}
都是一些老生常谈的问题了,整理一下,希望对你有帮助。
enjoy
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: