Laravel 代码开发最佳实践[通俗易懂]

Laravel 代码开发最佳实践

大家好,又见面了,我是全栈君。

我们这里要讨论的并不是 Laravel 版的 SOLID 原则(想要了解更多 SOLID 原则细节查看这篇文章)亦或是设计模式,而是 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;
}

胖模型、瘦控制器

如果你使用的是查询构建器或原生 SQL 查询的话将所有 DB 相关逻辑都放到 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();
    }
}

验证

将验证逻辑从控制器转移到请求类。

坏代码:

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

尽可能复用代码,单一职责原则可以帮助你避免重复,此外,尽可能复用 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 可以编写出可读性和可维护性更好的代码,此外,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());

不要在 Blade 执行查询 & 使用渴求式加载

坏代码:

@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())

将前端代码和 PHP 代码分离:

不要把 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 工具

优先使用 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

遵循 Laravel 命名约定

遵循 PSR 标准。此外,还要遵循 Laravel 社区版的命名约定:

What How Good Bad
控制器 单数 ArticleController ArticlesController
路由 复数 articles/1 article/1
命名路由 下划线+’.’号分隔 users.show_active users.show-active,show-active-users
模型 单数 User Users
一对一关联 单数 articleComment articleComments,article_comment
其他关联关系 复数 articleComments articleComment,article_comments
数据表 复数 article_comments article_comment,articleComments
中间表 按字母表排序的单数格式 article_user user_article,article_users
表字段 下划线,不带模型名 meta_title MetaTitle; article_meta_title
外键 单数、带_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
资源类方法 文档 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
契约(接口) 形容词或名词 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) ? $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 容器或门面

自己创建新的类会导致代码耦合度高,且难于测试,取而代之地,我们可以使用 IoC 容器或门面。

坏代码:

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

好代码:

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

....

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

不要从直接从 .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 getMonthDayAttribute($date)
{
    return $date->format('m-d');
}

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

其他好的实践

不要把任何业务逻辑写到路由文件中。

在 Blade 模板中尽量不要编写原生 PHP。

 

https://laravelacademy.org/post/8464.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/112287.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 数据挖掘复习(包括一些课本习题)[通俗易懂]

    数据挖掘复习(包括一些课本习题)[通俗易懂]第一章1.数据挖掘定义 在大量的数据中提取潜在有用的信息的过程2.任务分类,聚类,关联,离群点3.对象孔家数据库,时间序列数据库,流数据,多媒体数据库,文本数据,万维网4.知识发现(1)数据清洗(2)数据集成(3)数据转换(4)数据挖掘(5)模式评估(6)知识表示第二章(1)数据挖掘中使用的数据是数据对象及其属性的集合,属性为对象的特性(1)类属性和数值属性,标称,序数,区间,比例数据预处理(1)数据清理(2)数据集成(3)数据变换(4)数据规约(5)离

  • game theory博弈论全文翻译_john nash博弈论

    game theory博弈论全文翻译_john nash博弈论ProblemDescriptionLittleJohnisplayingveryfunnygamewithhisyoungerbrother.ThereisonebigboxfilledwithM&Msofdifferentcolors.AtfirstJohnhastoeatseveralM&Msofthesamecolor

  • 华为模拟器ensp怎么安装_模拟器下载手机版

    华为模拟器ensp怎么安装_模拟器下载手机版华为模拟器ENSP下载与安装教程【一】:安装环境1.Win10系统安装也可虚拟机安装【二】安装链接点击连接下载提取码:ob2v要是感觉下载慢的话可以开个会员(土豪随意)步骤1.下载后解压并安装,首先要先在安装这三个软件2.下面我们开始安装VirtualBox5.2.26并默认安装直接下一步就可以下面几步都是默认安装如图所示安装完成不想打开把运行勾去掉就行,点击完…

    2022年10月15日
  • 手摸手教你写一个vue的toast弹窗[通俗易懂]

    手摸手教你写一个vue的toast弹窗[通俗易懂]前言:我们在项目开发的过程中,也许会在很多页面实现弹窗的消息,普通的方法就是在这每个界面写些原生js代码来控制弹窗效果,这样明显非常冗余。可通过引入组件的方式,可解决部分冗余的代码,但是每个要使用的界面都必须导入、注册、使用,这些代码还是比较冗余。通过插件的方式封装Toast,可解决每个页面重复导入、注册、使用的重复过程。一.封装Toast组件css自行设计二.Toast插件方式的封装在使用Toast前需要做相应的准备工作:添加一个index.js文件-里面定义一个对象-然后导

  • 哈佛幸福课笔记上篇「建议收藏」

    哈佛幸福课笔记上篇「建议收藏」改变一生的课:哈佛幸福课笔记上篇第1课什么是积极心理学?第2课为什么要学习积极心理学?第3课幸福是一种随机现象吗?第4课积极的环境能改变人第5课环境的力量第6课乐观主义第7课逆境还是机遇?第8课感激链接:哈佛大学公开课:幸福课.《哈佛幸福课》是改变我生活最大的一项事物,没有之一。我学习了5遍幸福课,并且用过去6年的时间去尝试它践行它,感觉完全改变了我的生活。第1课什么是积极心理学?1.享受安静2.这门课不光是传授信息,而且关于如何变形。重要的不仅仅是获得了什么信息,还是何形状

  • cocos2d-x 3.0游戏实例学习笔记 《跑酷》 完结篇–源代码放送

    cocos2d-x 3.0游戏实例学习笔记 《跑酷》 完结篇–源代码放送

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号