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

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 深入浅出谈开窗函数(一)

    深入浅出谈开窗函数(一)

    2021年12月10日
    54
  • 一文搞懂JVM内存结构

    一文搞懂JVM内存结构1.前言Java虚拟机是中、高级开发人员必须修炼的知识,有着较高的学习门槛,很多人都不情愿去接触它。可能是觉得学习成本较高又或者是感觉没什么实用性,所以干脆懒得“搭理”它了。其实这种想法是错误的。举个最简单的例子,JVM基本上是每家招聘公司都会问到的问题,它们会这么无聊问这些不切实际的问题吗?很显然不是。由JVM引发的故障问题,无论在我们开发过程中还是生产环境下都是非常常见的。比如…

    2022年6月14日
    35
  • 分布式爬虫架构_分布式爬虫工具有哪些

    分布式爬虫架构_分布式爬虫工具有哪些目录分布式爬虫框架消息队列Redis和Scrapy-Redis分布式爬虫框架分布式爬虫框架分为两种:控制模式(左)和自由模式(右):控制模式中的控制节点是系统实现中的瓶颈,自由模式则面临爬行节点之间的通信处理问题。因此,在实际工程中,我们通常使用混合模式:各个爬行节点有各自的功能(爬取的对象不同),它们都注册到一个服务节点上,服务节点能分辨各个爬行节点的分工,用户的请求存放在队列中,处理不同请求的程序从队列中取出请求,然后询问服务节点,由服务节点分配爬行节点给请求的处理程序。下面介绍分布式爬

    2025年7月4日
    2
  • win10 硬盘图标变成空白解决办法是什么_新电脑如何分区硬盘win10

    win10 硬盘图标变成空白解决办法是什么_新电脑如何分区硬盘win10Win10硬盘图标变成空白解决办法如图,有时候会出现这种硬盘图标变成空白的情况,虽然不影响使用,但强迫症表示真的很难受,这里给出解决办法:按下组合键WIN+R,输入regedit再按回车运行,这时候会有一个提示,选择“是”。一层一层找到计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DriveIcons\这个路径,或者也可以直接在上方地址栏输入,注意我这里路径最开始显示的是计

    2022年10月18日
    4
  • 怎么用命令提示符运行JAVA代码_java命令提示符如何进入

    怎么用命令提示符运行JAVA代码_java命令提示符如何进入展开全部用命令提示符编译Java程序的方法62616964757a686964616fe59b9ee7ad9431333363363432我们先新建文本文档,输入自己的java程序。这里我写一个简单的java程序,来做示范。importjava.util.*;publicclassHelloDate{publicstaticvoidmain(String[]args){System….

    2022年8月30日
    2
  • C++中定义一个函数为bool类型的作用「建议收藏」

    C++中定义一个函数为bool类型的作用「建议收藏」1.bool型函数bool型函数(即返回值为bool类型的函数)的作用——获取函数返回值boolgetvalue(boolb){if(b==true)returntrue;elsereturnfalse;}intmain(){//在main()中调用函数就可以得到5261函数的返回结果4102cout<<boolalpha<<getValue(true);return0;

    2022年6月8日
    49

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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