单一职责原则

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

例如:

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 isVerifiedClient()
{
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查询,请将所有与数据库相关的逻辑放入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类来进行验证

把验证规则放到 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');
}
}
}

DRY原则 不要重复自己

尽可能重用代码,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();
}

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

使用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())

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

例子:

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功能和扩展包,而不是使用第三方的扩展包和工具。

如果你的项目被其他开发人员接手了,他们将不得不重新学习这些第三方工具的使用教程。

此外,当您使用第三方扩展包或工具时,你很难从Laravel社区获得什么帮助。 不要让你的客户为额外的问题付钱。

想要实现的功能 标准工具 第三方工具
权限 Policies Entrust, Sentinel 或者其他扩展包
资源编译工具 Laravel Mix Grunt, Gulp, 或者其他第三方包
开发环境 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 第三方的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命名约定

来源 PSR standards.

另外,遵循Laravel社区认可的命名约定:

对象 规则 更优的写法 应避免的写法
控制器 单数 ArticleController ArticlesController
路由 复数 articles/1 article/1
路由命名 带点符号的蛇形命名 users.show_active users.show-active, show-active-users
模型 单数 User Users
hasOne或belongsTo关系 单数 articleComment articleComments, article_comment
所有其他关系 复数 articleComments articleComment, article_comments
表单 复数 article_comments article_comment, articleComments
透视表 按字母顺序排列模型 article_user user_article, articles_users
数据表字段 使用蛇形并且不要带表名 meta_title MetaTitle; article_meta_title
模型参数 蛇形命名 $model->created_at $model->createdAt
外键 带有_id后缀的单数模型名称 article_id ArticleId, id_article, articles_id
主键 - id custom_id
迁移 - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
方法 驼峰命名 getAll get_all
资源控制器 table store saveArticle
测试类 驼峰命名 testGuestCannotSeeArticle test_guest_cannot_see_article
变量 驼峰命名 $articlesWithAuthor $articles_with_author
集合 描述性的, 复数的 $activeUsers = User::active()->get() $active, $data
对象 描述性的, 单数的 $activeUser = User::active()->first() $users, $obj
配置和语言文件索引 蛇形命名 articles_enabled ArticlesEnabled; articles-enabled
视图 短横线命名 show-filtered.blade.php showFiltered.blade.php, show_filtered.blade.php
配置 蛇形命名 google_calendar.php googleCalendar.php, google-calendar.php
内容 (interface) 形容词或名词 Authenticatable AuthenticationInterface, IAuthentication
Trait 使用形容词 Notifiable NotificationTrait

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

例子:

$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容器来创建实例 而不是直接new一个实例

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

例子:

$user = new User;
$user->create($request->validated());

更优的写法:

public function __construct(User $user)
{
$this->user = $user;
} .... $this->user->create($request->validated());

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

将数据传递给配置文件,然后使用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 getSomeDateAttribute($date)
{
return $date->format('m-d');
} // View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}

其他的一些好建议

永远不要在路由文件中放任何的逻辑代码。

尽量不要在Blade模板中写原始 PHP 代码。

原文作者:ikidnapmyself

原文链接:https://github.com/alexeymezenin/laravel-best-practices/

Laravel 最佳实践的更多相关文章

  1. Laravel 的十八个最佳实践

    本文翻译改编自 Laravel 的十八个最佳实践 这篇文章并不是什么由 Laravel 改编的 SOLID 原则.模式等. 只是为了让你注意你在现实生活的 Laravel 项目中最常忽略的内容.   ...

  2. Laravel 代码开发最佳实践

    我们这里要讨论的并不是 Laravel 版的 SOLID 原则(想要了解更多 SOLID 原则细节查看这篇文章)亦或是设计模式,而是 Laravel 实际开发中容易被忽略的最佳实践. 内容概览 单一职 ...

  3. 《modern-php》 - 阅读笔记 - 最佳实践

    过滤.验证和转义数据 过滤数据 不要相信任何外部数据! 常见的有以下几种数据需要过滤:HTML,SQL查询,用户提交的信息(邮件地址.电话号码.身份证) HTML htmlentities() HTM ...

  4. Vue 工程化最佳实践

    目录结构 总览   api 目录用于存放 api 请求,文件名与模型名称基本一致,文件名使用小驼峰,方法名称与后端 restful 控制器一致.   enums 目录存放 常量,与后端的常量目录对应 ...

  5. ASP.NET跨平台最佳实践

    前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语言本身来看,C#确实比Java ...

  6. 《AngularJS深度剖析与最佳实践》简介

    由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...

  7. ASP.NET MVC防范CSRF最佳实践

    XSS与CSRF 哈哈,有点标题党,但我保证这篇文章跟别的不太一样. 我认为,网站安全的基础有三块: 防范中间人攻击 防范XSS 防范CSRF 注意,我讲的是基础,如果更高级点的话可以考虑防范机器人刷 ...

  8. 快速web开发中的前后端框架选型最佳实践

    这个最佳实践是我目前人在做的一个站点,主要功能: oauth登录 发布文章(我称为"片段"),片段可以自定义一些和内容有关的指标,如“文中人物:12”.支持自定义排版.插图.建立相 ...

  9. Spring Batch在大型企业中的最佳实践

    在大型企业中,由于业务复杂.数据量大.数据格式不同.数据交互格式繁杂,并非所有的操作都能通过交互界面进行处理.而有一些操作需要定期读取大批量的数据,然后进行一系列的后续处理.这样的过程就是" ...

随机推荐

  1. 解决node-sass无法下载的问题

    本文链接:https://blog.csdn.net/qq383366204/article/details/86605960在国内用npm安装依赖的时候经常都会有各种奇怪的问题,个人强烈推荐用yar ...

  2. shell 字符串分割cut

    cut 选项与参数 -d:后面接分隔字符.与-f一起使用. -f:依据-d的分隔字符将一段信息分隔数段,用-f取出第几段的意思. -c:以字符的单位取出固定字符区间 [zhang@localhost ...

  3. Tomcat部署时war和war exploded区别以及如何实现热部署

    war和war exploded的区别       使用IDEA配置Tomcat服务的时候,在 Select Artifacts to Deploy 选项中相同项目名有war和 war explode ...

  4. java spring boot- freemarker 配置 yml使用流程

    1.pom.xml  加入maven 依赖 <!-- 引入 freemarker 模板依赖 --><dependency> <groupId>org.springf ...

  5. 【零基础】风格迁移之deep-painterly-harmonization的安装和使用

    注:原项目名叫deep-painterly-harmonization,这里我缩写下称呼其为“DPH” 注:原项目GIT链接:https://github.com/luanfujun/deep-pai ...

  6. 一个U盘制作多个系统

    写在前面:一个U盘可以装多个ghost系统,但不能装多个原版ISO系统. 一.一个U盘装多个ghost系统 下载老毛桃或电脑店U盘制作工具,点击一键制作U盘启动盘.然后将gho文件拷贝复制到U盘的GH ...

  7. Npoi Web 项目中(XSSFWorkbook) 导出出现无法访问已关闭的流

    NPOI生产.xlsx文件件时,在使用book.Write(ms);后,会关闭流,这样导致再次使用Respons输出流的时候就出错了. 造成关闭流的主要原因有时其实是跨域,同域是没有问题的. //新建 ...

  8. Flask中current_app和g对象

      Flask零基础到项目实战(七)请求方法.g对象和钩子函数 一.get方法 二.post方法 post请求在模板中要注意几点: input标签中,要写name来标识这个value的key,方便后台 ...

  9. MyEclipse环境的项目改为在Eclipse中运行爬坑记【我】

      新检出一个web项目,同事都是运行在MyEclipse中的,我用Eclipse启动, 1.首先是许多jar包报错: 处理方法为 remove掉,然后 选 WEB-INF 下的所有 jar 重新添加 ...

  10. Java8 根据对象某个属性值去重

    list中的对象某个属性存在重复时将重复的对象去重 //根据skuAttrValueName值去重 List<SkuValue> uniqueSkuValues = skuValues.s ...