Laravel 你应该知道的几个最佳实践

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 :tada:

本作品采用《CC 协议》,转载必须注明作者和本文链接
悲观者永远正确,乐观者永远前行。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 8

大多数之前都有看过了

2年前 评论
MArtian (楼主) 2年前
DonnyLiu

复习了一趟

2年前 评论

好多老项目,代码是一塌糊涂,不忍直视

1年前 评论
MArtian (楼主) 1年前

不知道是懒还是咋样。我就是喜欢把很多东西写在一个函数。虽然分成很多函数比较规范。但是时间久了。很多小函数就比较多。一旦需求变化了。还得一一删除。估计还是我懒的问题

7个月前 评论
MArtian (楼主) 7个月前

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