当前位置: 首页 > 软件库 > Web应用开发 > Web框架 >

laravel-best-practices

Laravel best practices
授权协议 Readme
开发语言 PHP
所属分类 Web应用开发、 Web框架
软件类型 开源软件
地区 不详
投 递 者 高弘光
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

Translations:

Nederlands (by Protoqol)

Indonesia (by P0rguy, Doni Ahmad)

한국어 (by cherrypick)

日本語 (by 2bo)

简体中文 (by xiaoyi)

繁體中文 (by woeichern)

ภาษาไทย (by kongvut sangkla)

বাংলা (by Anowar Hossain)

فارسی (by amirhossein baghaie)

Português (by jonaselan)

Українська (by Tenevyk)

Русский

Tiếng Việt (by Chung Nguyễn)

Español (by César Escudero)

Français (by Mikayil S.)

Polski (by Karol Pietruszka)

Türkçe (by Burak)

Deutsch (by Sujal Patel)

Italiana (by Sujal Patel)

Azərbaycanca (by Maharramoff)

العربية (by ahmedsaoud31)

اردو (by RizwanAshraf1)

It's not a Laravel adaptation of SOLID principles, patterns etc. Here you'll find the best practices which are usually ignored in real life Laravel projects.

Contents

Single responsibility principle

Fat models, skinny controllers

Validation

Business logic should be in service class

Don't repeat yourself (DRY)

Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays

Mass assignment

Do not execute queries in Blade templates and use eager loading (N + 1 problem)

Comment your code, but prefer descriptive method and variable names over comments

Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes

Use config and language files, constants instead of text in the code

Use standard Laravel tools accepted by community

Follow Laravel naming conventions

Use shorter and more readable syntax where possible

Use IoC container or facades instead of new Class

Do not get data from the .env file directly

Store dates in the standard format. Use accessors and mutators to modify date format

Other good practices

Single responsibility principle

A class and a method should have only one responsibility.

Bad:

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;
    }
}

Good:

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;
}

�� Back to contents

Fat models, skinny controllers

Put all DB related logic into Eloquent models or into Repository classes if you're using Query Builder or raw SQL queries.

Bad:

public function index()
{
    $clients = Client::verified()
        ->with(['orders' => function ($q) {
            $q->where('created_at', '>', Carbon::today()->subWeek());
        }])
        ->get();

    return view('index', ['clients' => $clients]);
}

Good:

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();
    }
}

�� Back to contents

Validation

Move validation from controllers to Request classes.

Bad:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publish_at' => 'nullable|date',
    ]);

    ....
}

Good:

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',
        ];
    }
}

�� Back to contents

Business logic should be in service class

A controller must have only one responsibility, so move business logic from controllers to service classes.

Bad:

public function store(Request $request)
{
    if ($request->hasFile('image')) {
        $request->file('image')->move(public_path('images') . 'temp');
    }
    
    ....
}

Good:

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');
        }
    }
}

�� Back to contents

Don't repeat yourself (DRY)

Reuse code when you can. SRP is helping you to avoid duplication. Also, reuse Blade templates, use Eloquent scopes etc.

Bad:

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();
}

Good:

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();
}

�� Back to contents

Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays

Eloquent allows you to write readable and maintainable code. Also, Eloquent has great built-in tools like soft deletes, events, scopes etc.

Bad:

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

Good:

Article::has('user.profile')->verified()->latest()->get();

�� Back to contents

Mass assignment

Bad:

$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();

Good:

$category->article()->create($request->validated());

�� Back to contents

Do not execute queries in Blade templates and use eager loading (N + 1 problem)

Bad (for 100 users, 101 DB queries will be executed):

@foreach (User::all() as $user)
    {{ $user->profile->name }}
@endforeach

Good (for 100 users, 2 DB queries will be executed):

$users = User::with('profile')->get();

...

@foreach ($users as $user)
    {{ $user->profile->name }}
@endforeach

�� Back to contents

Comment your code, but prefer descriptive method and variable names over comments

Bad:

if (count((array) $builder->getQuery()->joins) > 0)

Better:

// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)

Good:

if ($this->hasJoins())

�� Back to contents

Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes

Bad:

let article = `{{ json_encode($article) }}`;

Better:

<input id="article" type="hidden" value='@json($article)'>

Or

<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>

In a Javascript file:

let article = $('#article').val();

The best way is to use specialized PHP to JS package to transfer the data.

�� Back to contents

Use config and language files, constants instead of text in the code

Bad:

public function isNormal()
{
    return $article->type === 'normal';
}

return back()->with('message', 'Your article has been added!');

Good:

public function isNormal()
{
    return $article->type === Article::TYPE_NORMAL;
}

return back()->with('message', __('app.article_added'));

�� Back to contents

Use standard Laravel tools accepted by community

Prefer to use built-in Laravel functionality and community packages instead of using 3rd party packages and tools. Any developer who will work with your app in the future will need to learn new tools. Also, chances to get help from the Laravel community are significantly lower when you're using a 3rd party package or tool. Do not make your client pay for that.

Task Standard tools 3rd party tools
Authorization Policies Entrust, Sentinel and other packages
Compiling assets Laravel Mix Grunt, Gulp, 3rd party packages
Development Environment Laravel Sail, Homestead Docker
Deployment Laravel Forge Deployer and other solutions
Unit testing PHPUnit, Mockery Phpspec
Browser testing Laravel Dusk Codeception
DB Eloquent SQL, Doctrine
Templates Blade Twig
Working with data Laravel collections Arrays
Form validation Request classes 3rd party packages, validation in controller
Authentication Built-in 3rd party packages, your own solution
API authentication Laravel Passport, Laravel Sanctum 3rd party JWT and OAuth packages
Creating API Built-in Dingo API and similar packages
Working with DB structure Migrations Working with DB structure directly
Localization Built-in 3rd party packages
Realtime user interfaces Laravel Echo, Pusher 3rd party packages and working with WebSockets directly
Generating testing data Seeder classes, Model Factories, Faker Creating testing data manually
Task scheduling Laravel Task Scheduler Scripts and 3rd party packages
DB MySQL, PostgreSQL, SQLite, SQL Server MongoDB

�� Back to contents

Follow Laravel naming conventions

Follow PSR standards.

Also, follow naming conventions accepted by Laravel community:

What How Good Bad
Controller singular ArticleController ArticlesController
Route plural articles/1 article/1
Named route snake_case with dot notation users.show_active users.show-active, show-active-users
Model singular User Users
hasOne or belongsTo relationship singular articleComment articleComments, article_comment
All other relationships plural articleComments articleComment, article_comments
Table plural article_comments article_comment, articleComments
Pivot table singular model names in alphabetical order article_user user_article, articles_users
Table column snake_case without model name meta_title MetaTitle; article_meta_title
Model property snake_case $model->created_at $model->createdAt
Foreign key singular model name with _id suffix article_id ArticleId, id_article, articles_id
Primary key - id custom_id
Migration - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
Method camelCase getAll get_all
Method in resource controller table store saveArticle
Method in test class camelCase testGuestCannotSeeArticle test_guest_cannot_see_article
Variable camelCase $articlesWithAuthor $articles_with_author
Collection descriptive, plural $activeUsers = User::active()->get() $active, $data
Object descriptive, singular $activeUser = User::active()->first() $users, $obj
Config and language files index snake_case articles_enabled ArticlesEnabled; articles-enabled
View kebab-case show-filtered.blade.php showFiltered.blade.php, show_filtered.blade.php
Config snake_case google_calendar.php googleCalendar.php, google-calendar.php
Contract (interface) adjective or noun AuthenticationInterface Authenticatable, IAuthentication
Trait adjective Notifiable NotificationTrait

�� Back to contents

Use shorter and more readable syntax where possible

Bad:

$request->session()->get('cart');
$request->input('name');

Good:

session('cart');
$request->name;

More examples:

Common syntax Shorter and more readable syntax
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 (in PHP 8: $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')

�� Back to contents

Use IoC container or facades instead of new Class

new Class syntax creates tight coupling between classes and complicates testing. Use IoC container or facades instead.

Bad:

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

Good:

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

....

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

�� Back to contents

Do not get data from the .env file directly

Pass the data to config files instead and then use the config() helper function to use the data in an application.

Bad:

$apiKey = env('API_KEY');

Good:

// config/api.php
'key' => env('API_KEY'),

// Use the data
$apiKey = config('api.key');

�� Back to contents

Store dates in the standard format. Use accessors and mutators to modify date format

Bad:

{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}

Good:

// 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 }}

�� Back to contents

Other good practices

Never put any logic in routes files.

Minimize usage of vanilla PHP in Blade templates.

�� Back to contents

  • 1. Ubuntu build a local server 1. /etc/apache2/apache2.conf    <Directory /home/bob/bobserver/public/>       Options Indexes FollowSymLinks       AllowOverride all       Require all granted </Director

  • Hello from the Engine Yard Data Team! We wanted to let you know what we’ve been up to since the  last time we blogged. When the team was formed earlier in the year, our first job was to expand our sta

  • 正好都用过,推荐symfony2。 首先吐槽下,phalcon 太坑,bug太多,不够稳定,如果要用请一定一定再等3年,等phalcon版本到3.0后再用它,唉,说多了都是泪 :( 如果你是从java过来的那么symfony2绝对不会让你失望,该有的都有,稳定可靠全面,不折腾无意义的事情,不会碰到奇怪的问题,学习曲线陡反倒是优点(symfony除了作为一个框架,更重要的是它有自己的Best Pra

  • 网上搜了些资料,都是各说各的好,希望了解这些框架的介绍下他们的优缺点,有对比就更好了,谢谢~~ 回答 正好都用过,推荐symfony2。 首先吐槽下,phalcon 太坑,bug太多,不够稳定,如果要用请一定一定再等3年,等phalcon版本到3.0后再用它,唉,说多了都是泪  如果你是从java过来的那么symfony2绝对不会让你失望,该有的都有,稳定可靠全面,不折腾无意义的事情,不会碰到奇

  • 本文翻译改编自 Laravel 的十八个最佳实践 这篇文章并不是什么由 Laravel 改编的 SOLID 原则、模式等。 只是为了让你注意你在现实生活的 Laravel 项目中最常忽略的内容。   单一责任原则 一个类和一个方法应该只有一个职责。 错误的做法: public function getFullNameAttribute() { if (auth()->user() && auth(

  • Laravel已经是众所周知的“优雅”、“简洁”、“实用”、“敏捷”、“特性丰富”等的代名词,尤其是对PHP开发者来说。当然了,也正是因为它特性过于“丰富”,导致很多特性并没有列在文档里,或者曾经列出来过,但是出于某些原因,后来又移除了。很多时候,我们给新手学习建议的时候,都不会建议他直接去看文档,因为文档体系的展开,往往并不是新手能hold住的。我们往往会推荐他们从一些过来人的、真正优雅规范的教

 相关资料
  • If this page doesn't answer your question, please feel free to open an issue in our repository. The Provider (window.ethereum) ::: tip Recent Breaking Provider Changes If you are an Ethereum applicati

  • Ways to avoid calls to saveLayer(): To implement fading in an image, consider using the FadeInImage widget, which applies a gradual opacity using the GPU’s fragment shader. For more information, see O

  • Best Resume Ever 是一个帮助你快速生成漂亮简历的工具,它基于 Vue 和 LESS,生成的简历可导出为 PDF 格式。     如何使用 best-resume-ever 要求 node v.7.6 及以上版本 克隆存储库 运行 npm install 在 src/person.js 中编辑个人信息  用 npm run dev 进行预览 用 npm run export 导出简历 所有简历将导出至 /pdf 文件内。

  • A FastBoot-enabled addon to detect the best language for your user. Installation $ ember install ember-best-language How does it work? ember-best-language uses a scoring system to determine the best l

  • Best-of Jupyter ��   A ranked list of awesome Jupyter projects. Updated weekly. This curated list contains 290 awesome open-source projects with a total of 270K stars grouped into 13 categories. All p

  • Best practices in Android development Avoid reinventing the wheel by following these guidelines. Lessons learned from Android developers in Futurice. If you are interested in iOS or Windows Phone deve