咱们这里要讨论的并非 Laravel 版的 SOLID 原则(想要了解更多 SOLID 原则细节查看这篇文章)亦或是设计模式,而是 Laravel 实际开发中容易被忽略的最佳实践。php
.env
文件获取数据一个类和方法只负责一项职责。html
坏代码:前端
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; } }
好代码:laravel
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; }
若是你使用的是查询构建器或原生 SQL 查询的话将全部 DB 相关逻辑都放到 Eloquent 模型或 Repository 类。json
坏代码:设计模式
public function index() { $clients = Client::verified() ->with(['orders' => function ($q) { $q->where('created_at', '>', Carbon::today()->subWeek()); }]) ->get(); return view('index', ['clients' => $clients]); }
好代码:api
public function index() { return view('index', ['clients' => $this->client->getWithNewOrders()]); } Class Client extends Model { public function getWithNewOrders() { return $this->verified() ->with(['orders' => function ($q) { $q->where('created_at', '>', Carbon::today()->subWeek()); }]) ->get(); } }
将验证逻辑从控制器转移到请求类。数组
坏代码:浏览器
public function store(Request $request) { $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]); .... }
好代码:session
public function store(PostRequest $request) { .... } class PostRequest extends Request { public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]; } }
一个控制器只负责一项职责,因此须要把业务逻辑都转移到服务类中。
坏代码:
public function store(Request $request) { if ($request->hasFile('image')) { $request->file('image')->move(public_path('images') . 'temp'); } .... }
好代码:
public function store(Request $request) { $this->articleService->handleUploadedImage($request->file('image')); .... } class ArticleService { public function handleUploadedImage($image) { if (!is_null($image)) { $image->move(public_path('images') . 'temp'); } } }
尽量复用代码,单一职责原则能够帮助你避免重复,此外,尽量复用 Blade 模板,使用 Eloquent 做用域。
坏代码:
public function getActive() { return $this->where('verified', 1)->whereNotNull('deleted_at')->get(); } public function getArticles() { return $this->whereHas('user', function ($q) { $q->where('verified', 1)->whereNotNull('deleted_at'); })->get(); }
好代码:
public function scopeActive($q) { return $q->where('verified', 1)->whereNotNull('deleted_at'); } public function getActive() { return $this->active()->get(); } public function getArticles() { return $this->whereHas('user', function ($q) { $q->active(); })->get(); }
经过 Eloquent 能够编写出可读性和可维护性更好的代码,此外,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();
关于批量赋值细节可查看对应文档。
坏代码:
$article = new Article; $article->title = $request->title; $article->content = $request->content; $article->verified = $request->verified; // Add category to article $article->category_id = $category->id; $article->save();
好代码:
$category->article()->create($request->all());
坏代码:
@foreach (User::all() as $user) {{ $user->profile->name }} @endforeach
好代码:
$users = User::with('profile')->get(); ... @foreach ($users as $user) {{ $user->profile->name }} @endforeach
坏代码:
if (count((array) $builder->getQuery()->joins) > 0)
好代码:
// Determine if there are any joins. if (count((array) $builder->getQuery()->joins) > 0)
最佳:
if ($this->hasJoins())
不要把 JS 和 CSS 代码写到 Blade 模板里,也不要在 PHP 类中编写 HTML 代码。
坏代码:
let article = `{{ json_encode($article) }}`;
好代码:
<input id="article" type="hidden" value="{{ json_encode($article) }}"> 或者 <button class="js-fav-article" data-article="{{ json_encode($article) }}">{{ $article->name }}<button>
在 JavaScript 文件里:
let article = $('#article').val();
坏代码:
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'));
优先使用 Laravel 内置功能和社区版扩展包,其次才是第三方扩展包和工具。这样作的好处是下降之后的学习和维护成本。
任务 | 标准工具 | 第三方工具 |
---|---|---|
受权 | 策略类 | Entrust、Sentinel等 |
编译资源 | Laravel Mix | Grunt、Gulp等 |
开发环境 | Homestead | Docker |
部署 | Laravel Forge | Deployer等 |
单元测试 | PHPUnit、Mockery | Phpspec |
浏览器测试 | Laravel Dusk | Codeception |
DB | Eloquent | SQL、Doctrine |
模板 | Blade | Twig |
处理数据 | Laravel集合 | 数组 |
表单验证 | 请求类 | 第三方扩展包、控制器中验证 |
认证 | 内置功能 | 第三方扩展包、你本身的解决方案 |
API认证 | Laravel Passport | 第三方 JWT 和 OAuth 扩展包 |
建立API | 内置功能 | Dingo API和相似扩展包 |
处理DB结构 | 迁移 | 直接操做DB |
本地化 | 内置功能 | 第三方工具 |
实时用户接口 | Laravel Echo、Pusher | 第三方直接处理 WebSocket的扩展包 |
生成测试数据 | 填充类、模型工厂、Faker | 手动建立测试数据 |
任务调度 | Laravel Task Scheduler | 脚本或第三方扩展包 |
DB | MySQL、PostgreSQL、SQLite、SQL Server | MongoDB |
遵循 PSR 标准。此外,还要遵循 Laravel 社区版的命名约定:
What | How | Good | Bad |
---|---|---|---|
控制器 | 单数 | ArticleController | |
路由 | 复数 | articles/1 | |
命名路由 | 下划线+'.'号分隔 | users.show_active | |
模型 | 单数 | User | |
一对一关联 | 单数 | articleComment | |
其余关联关系 | 复数 | articleComments | |
数据表 | 复数 | article_comments | |
中间表 | 按字母表排序的单数格式 | article_user | |
表字段 | 下划线,不带模型名 | meta_title | |
外键 | 单数、带_id后缀 | article_id | |
主键 | - | id | |
迁移 | - | 2017_01_01_000000_create_articles_table | |
方法 | 驼峰 | getAll | |
资源类方法 | 文档 | store | |
测试类方法 | 驼峰 | testGuestCannotSeeArticle | |
变量 | 驼峰 | $articlesWithAuthor | |
集合 | 复数 | $activeUsers = User::active()->get() | |
对象 | 单数 | $activeUser = User::active()->first() | |
配置和语言文件索引 | 下划线 | articles_enabled | |
视图 | 下划线 | show_filtered.blade.php | |
配置 | 下划线 | google_calendar.php | |
契约(接口) | 形容词或名词 | Authenticatable | |
Trait | 形容词 | Notifiable |
坏代码:
$request->session()->get('cart'); $request->input('name');
好代码:
session('cart'); $request->name;
更多示例:
通用语法 | 可读性更好的 |
---|---|
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) ? $object->relation->id : null } |
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 容器或门面。
坏代码:
$user = new User; $user->create($request->all());
好代码:
public function __construct(User $user) { $this->user = $user; } .... $this->user->create($request->all());
传递数据到配置文件而后使用 config
辅助函数获取数据。
坏代码:
$apiKey = env('API_KEY');
好代码:
// config/api.php 'key' => env('API_KEY'), // Use the data $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 }}
不要把任何业务逻辑写到路由文件中。
在 Blade 模板中尽可能不要编写原生 PHP。
https://laravelacademy.org/post/8464.html