You might also want to check out the real-world Laravel example application
多国语言列表:
Nederlands (by Protoqol)
한국어 (by cherrypick)
Українська (by Tenevyk)
فارسی (by amirhossein baghaie)
Tiếng Việt (by Chung Nguyễn)
Español (by César Escudero)
Français (by Mikayil S.)
Polski (by Karol Pietruszka)
Deutsch (by Sujal Patel)
Italiana (by Sujal Patel)
العربية (by ahmedsaoud31)
اردو (by RizwanAshraf1)
注释你的代码,但是更优雅的做法是使用描述性的语言来编写你的代码
不要把 JS 和 CSS 放到 Blade 模板中,也不要把任何 HTML 代码放到 PHP 代码里
一个类和一个方法应该只有一个责任。
例如:
public function getFullNameAttribute(): string
{
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(): string
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerifiedClient(): bool
{
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong(): string
{
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort(): string
{
return $this->first_name[0] . '. ' . $this->last_name;
}
如果您使用的是查询生成器或原始SQL查询,请将所有与数据库相关的逻辑放入Eloquent模型或Repository类中。
例如:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
更优的写法:
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();
}
}
把验证规则放到 Request 类中.
例子:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
...
}
更优的写法:
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');
}
}
}
尽可能重用代码,SRP可以帮助您避免重复造轮子。 此外尽量重复使用Blade模板,使用Eloquent的 scopes 方法来实现代码。
例子:
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->validated());
例子 (对于100个用户,将执行101次DB查询):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
更优的写法 (对于100个用户,使用以下写法只需执行2次DB查询):
$users = User::with('profile')->get();
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
例子:
if (count((array) $builder->getQuery()->joins) > 0)
加上注释:
// 确定是否有任何连接
if (count((array) $builder->getQuery()->joins) > 0)
更优的写法:
if ($this->hasJoins())
例子:
let article = `{{ json_encode($article) }}`;
更好的写法:
<input id="article" type="hidden" value='@json($article)'>
Or
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
在Javascript文件中加上:
let article = $('#article').val();
当然最好的办法还是使用专业的PHP的JS包传输数据。
例子:
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功能和扩展包,而不是使用第三方的扩展包和工具。 如果你的项目被其他开发人员接手了,他们将不得不重新学习这些第三方工具的使用教程。 此外,当您使用第三方扩展包或工具时,你很难从Laravel社区获得什么帮助。 不要让你的客户为额外的问题付钱。
想要实现的功能 | 标准工具 | 第三方工具 |
---|---|---|
权限 | Policies | Entrust, Sentinel 或者其他扩展包 |
资源编译工具 | Laravel Mix, Vite | Grunt, Gulp, 或者其他第三方包 |
开发环境 | Laravel Sail, Homestead | Docker |
部署 | Laravel Forge | Deployer 或者其他解决方案 |
自动化测试 | PHPUnit, Mockery | Phpspec, Pest |
页面预览测试 | 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 |
来源 PSR standards.
另外,遵循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 | |
Trait (PSR) | adjective | NotifiableTrait | |
Enum | singular | UserType | |
FormRequest | singular | UpdateUserRequest | |
Seeder | singular | UserSeeder |
例子:
$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) ? 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容器或注入来实现。
例子:
$user = new User;
$user->create($request->validated());
更优的写法:
public function __construct(User $user)
{
$this->user = $user;
}
...
$this->user->create($request->validated());
将数据传递给配置文件,然后使用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 $casts = [
'ordered_at' => 'datetime',
];
public function getSomeDateAttribute($date)
{
return $date->format('m-d');
}
// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}
永远不要在路由文件中放任何的逻辑代码。
尽量不要在Blade模板中写原始 PHP 代码。