单一职责原则

一个类和一个方法应该只有一个责任。

  1. public function getFullNameAttribute()
  2. {
  3. return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
  4. }
  5. public function isVerifiedClient()
  6. {
  7. return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
  8. }
  9. public function getFullNameLong()
  10. {
  11. return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
  12. }
  13. public function getFullNameShort()
  14. {
  15. return $this->first_name[0] . '. ' . $this->last_name;
  16. }

保持控制器的简洁

如果您使用的是查询生成器或原始 SQL 查询,请将所有与数据库相关的逻辑放入 Eloquent 模型或 Repository 类中。

  1. public function index()
  2. {
  3. return view('index', ['clients' => $this->client->getWithNewOrders()]);
  4. }
  5. class Client extends Model
  6. {
  7. public function getWithNewOrders()
  8. {
  9. return $this->verified()
  10. ->with(['orders' => function ($q) {
  11. $q->where('created_at', '>', Carbon::today()->subWeek());
  12. }])
  13. ->get();
  14. }
  15. }

使用自定义 Request 类来进行验证

把验证规则放到 Request 类中。

  1. public function store(PostRequest $request)
  2. {
  3. ....
  4. }
  5. class PostRequest extends Request
  6. {
  7. public function rules()
  8. {
  9. return [
  10. 'title' => 'required|unique:posts|max:255',
  11. 'body' => 'required',
  12. 'publish_at' => 'nullable|date',
  13. ];
  14. }
  15. }

业务代码要放到服务层中

控制器必须遵循单一职责原则,因此最好将业务代码从控制器移动到服务层中。

  1. public function store(Request $request)
  2. {
  3. $this->articleService->handleUploadedImage($request->file('image'));
  4. ....
  5. }
  6. class ArticleService
  7. {
  8. public function handleUploadedImage($image)
  9. {
  10. if (!is_null($image)) {
  11. $image->move(public_path('images') . 'temp');
  12. }
  13. }
  14. }

DRY 原则,不要重复自己

尽可能重用代码,SRP 可以帮助您避免重复造轮子。 此外尽量重复使用 Blade 模板,使用 Eloquent 的 scopes 方法来实现代码。

  1. public function scopeActive($q)
  2. {
  3. return $q->where('verified', 1)->whereNotNull('deleted_at');
  4. }
  5. public function getActive()
  6. {
  7. return $this->active()->get();
  8. }
  9. public function getArticles()
  10. {
  11. return $this->whereHas('user', function ($q) {
  12. $q->active();
  13. })->get();
  14. }

使用 ORM 而不是纯 SQL 语句,使用集合而不是数组

使用 Eloquent 可以帮您编写可读和可维护的代码。 此外 Eloquent 还有非常优雅的内置工具,如软删除,事件,范围等。

  1. Article::has('user.profile')->verified()->latest()->get();

集中处理数据

  1. $category->article()->create($request->validated());

不要在模板中查询,尽量使用惰性加载

  1. $users = User::with('profile')->get();
  2. ...
  3. @foreach ($users as $user)
  4. {{ $user->profile->name }}
  5. @endforeach

注释你的代码,但是更优雅的做法是使用描述性的语言来编写你的代码

  1. if ($this->hasJoins())

不要把 JS 和 CSS 放到 Blade 模板中,也不要把任何 HTML 代码放到 PHP 代码里

  1. let article = $('#article').val();
  2. <input id="article" type="hidden" value='@json($article)'>
  3. Or
  4. <button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>

在代码中使用配置、语言包和常量,而不是使用硬编码

public function isNormal()
{
  return $article->type === Article::TYPE_NORMAL;
}

return back()->with('message', __('app.article_added'));

使用社区认可的标准 Laravel 工具

强力推荐使用内置的 Laravel 功能和扩展包,而不是使用第三方的扩展包和工具。 如果你的项目被其他开发人员接手了,他们将不得不重新学习这些第三方工具的使用教程。 此外,当您使用第三方扩展包或工具时,你很难从 Laravel 社区获得什么帮助。

想要实现的功能 标准工具 第三方工具
权限 Policies Entrust, Sentinel 或者其他扩展包
资源编译工具 Laravel Mix Grunt, Gulp, 或者其他第三方包
开发环境 Laravel Sail, Homestead Docker
部署 Laravel Forge Deployer 或者其他解决方案
自动化测试 PHPUnit, Mockery Phpspec
页面预览测试 Laravel Dusk Codeception
DB 操作 Eloquent SQL, Doctrine
模板 Blade Twig
数据操作 Laravel 集合 数组
表单验证 Request classes 其他第三方包,甚至在控制器中做验证
权限 Built-in 其他第三方包或者你自己解决
API 身份验证 Laravel Passport, Laravel Sanctum 第三方的 JWT 或者 OAuth 扩展包
创建 API Built-in Dingo API 或者类似的扩展包
创建数据库结构 Migrations 直接用 DB 语句创建
本土化 Built-in 第三方包
实时消息队列 Laravel Echo, Pusher 使用第三方包或者直接使用WebSockets
创建测试数据 Seeder classes, Model Factories, Faker 手动创建测试数据
任务调度 Laravel Task Scheduler 脚本和第三方包
数据库 MySQL, PostgreSQL, SQLite, SQL Server MongoDB

遵循 Laravel 命名约定

对象 规则 写法
控制器 单数 ArticleController
路由 复数 articles/1
路由命名 带点符号的蛇形命名 users.show_active
模型 单数 User
hasOne 或 belongsTo 关系 单数 articleComment
所有其他关系 复数 articleComments
表单 复数 article_comments
透视表 按字母顺序排列模型 article_user
数据表字段 使用蛇形并且不要带表名 meta_title
模型参数 蛇形命名 $model->created_at
外键 带有_id后缀的单数模型名称 article_id
主键 - id
迁移 - 2017_01_01_000000_create_articles_table
方法 驼峰命名 getAll
资源控制器 table store
测试类 驼峰命名 testGuestCannotSeeArticle
变量 驼峰命名 $articlesWithAuthor
集合 描述性的, 复数的 $activeUsers = User::active()->get()
对象 描述性的, 单数的 $activeUser = User::active()->first()
配置和语言文件索引 蛇形命名 articles_enabled
视图 短横线命名 show-filtered.blade.php
配置 蛇形命名 google_calendar.php
内容 (interface) 形容词或名词 AuthenticationInterface
Trait 使用形容词 Notifiable

尽可能使用简短且可读性更好的语法

常规写法 优雅写法
Session::get(‘cart’) session(‘cart’)
$request->session()->get(‘cart’) session(‘cart’)
Session::put(‘cart’, $data) session([‘cart’ => $data])
$request->input(‘name’), Request::get(‘name’) $request->name, request(‘name’)
return Redirect::back() return back()
is_null($object->relation) ? null : $object->relation->id optional($object->relation)->id
return view(‘index’)->with(‘title’, $title)->with(‘client’, $client) return view(‘index’, compact(‘title’, ‘client’))
$request->has(‘value’) ? $request->value : ‘default’ $request->get(‘value’, ‘default’)
Carbon::now(), Carbon::today() now(), today()
App::make(‘Class’) app(‘Class’)
->where(‘column’, ‘=’, 1) ->where(‘column’, 1)
->orderBy(‘created_at’, ‘desc’) ->latest()
->orderBy(‘age’, ‘desc’) ->latest(‘age’)
->orderBy(‘created_at’, ‘asc’) ->oldest()
->select(‘id’, ‘name’)->get() ->get([‘id’, ‘name’])
->first()->name ->value(‘name’)

使用 IOC 容器来创建实例,而不是直接 new 一个实例

创建新的类会让类之间更加耦合,使得测试越发复杂。请改用 IoC 容器或注入来实现。

public function __construct(User $user)
{
    $this->user = $user;
}

....

$this->user->create($request->validated());

避免直接从 .env 文件里获取数据

将数据传递给配置文件,然后使用 config() 帮助函数来调用数据。

// config/api.php
'key' => env('API_KEY'),

// Use the data
$apiKey = config('api.key');

使用标准格式来存储日期,用访问器和修改器来修改日期格式

// Model
protected $dates = ['ordered_at', 'created_at', 'updated_at'];
public function getSomeDateAttribute($date)
{
    return $date->format('m-d');
}

// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}

其他的一些好建议

永远不要在路由文件中放任何的逻辑代码。
尽量不要在 Blade 模板中写原始 PHP 代码。