This is not an officially mandated specification from Laravel, but rather an excellent implementation that we often overlook in our daily development.

content

Single responsibility principle

Keep the controller simple

Use the custom Request class for validation

The business code is placed in the service layer

The DRY principle does not repeat itself

Use ORM instead of pure SQL statements, and use collections instead of arrays

Centralized data processing

Do not query in the template, use lazy loading instead

Comment your code, but it’s more elegant to write your code in descriptive language

Don’t put JS and CSS in Blade templates, and don’t put any HTML code in PHP code

Use configurations, language packs, and constants in your code rather than hard coding

Use standard Laravel tools approved by the community

Follow the Laravel naming convention

Use short, readable syntax whenever possible

Use the IOC container to create an instance rather than simply new one

Avoid direct from.envGet data from files

Use standard formats to store dates and accessors and modifiers to modify date formats

Other good advice

Single responsibility principle

A class and a method should have only one responsibility.

Such as:

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; }}Copy the code

A better way to write:

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;
}
Copy the code

🔝 Back to directory

Keep the controller simple

If you are using a query generator or raw SQL query, place all database-related logic in a Eloquent model or Repository class.

Such as:

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

    return view('index'['clients'= >$clients]);
}
Copy the code

A better way to write:

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

🔝 Back to directory

Use the custom Request class for validation

Put the validation rules in the Request class.

Example:

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

A better way to write:

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

🔝 Back to directory

The business code is placed in the service layer

The controller must follow the single responsibility principle, so it is best to move the business code from the controller to the service layer.

Example:

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

A better way to write:

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

🔝 Back to directory

The DRY principle does not repeat itself

Reuse code as much as possible, and SRP helps you avoid reinventing the wheel. Also, reuse the Blade template as much as possible, using the Eloquent scopes method to implement the code.

Example:

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

A better way to write:

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

🔝 Back to directory

Use ORM instead of pure SQL statements, and use collections instead of arrays

Using the Eloquent helps you write readable and maintainable code. Eloquent also has elegant built-in tools such as soft delete, events, ranges, and more.

Example:

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
Copy the code

A better way to write:

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

🔝 Back to directory

Centralized data processing

Example:

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

A better way to write:

$category->article()->create($request->validated());
Copy the code

🔝 Back to directory

Do not query in the template, use lazy loading instead

Example (101 DB queries for 100 users):

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

Better writing (for 100 users, only 2 DB queries are required using the following notation):

$users = User::with('profile')->get(); . @foreach ($users as $user)
    {{ $user->profile->name }}
@endforeach
Copy the code

🔝 Back to directory

Comment your code, but it’s more elegant to write your code in descriptive language

Example:

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

Annotate:

// Determine if there are any connections
if (count((array) $builder->getQuery()->joins) > 0)
Copy the code

A better way to write:

if ($this->hasJoins())
Copy the code

🔝 Back to directory

Don’t put JS and CSS in Blade templates, and don’t put any HTML code in PHP code

Example:

let article = `{{ json_encode($article) }}`;
Copy the code

Better written:

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

Or

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

Add the following to your Javascript file:

let article = $('#article').val();
Copy the code

Of course, the best way is to use a professional PHP JS package to transfer data.

🔝 Back to directory

Use configurations, language packs, and constants in your code rather than hard coding

Example:

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

return back()->with('message'.'Your article has been added! ');
Copy the code

A better way to write:

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

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

🔝 Back to directory

Use standard Laravel tools approved by the community

It is highly recommended to use the built-in Laravel features and extensions rather than third-party extensions and tools. If your project is taken over by another developer, they will have to relearn how to use these third-party tools. Also, it’s hard to get much help from the Laravel community when you’re using third-party extension packs or tools. Don’t make your client pay for extra questions.

The functionality you want to implement Standard tools Third-party tools
permissions Policies Entrust, Sentinel, or other extensions
Resource compilation tool Laravel Mix Grunt, Gulp, or other third-party packages
The development environment Homestead Docker
The deployment of Laravel Forge Deployer or other solutions
Automated testing PHPUnit, Mockery Phpspec
Page preview test Laravel Dusk Codeception
The DB manipulation Eloquent SQL, Doctrine
The template Blade Twig
Data manipulation Laravel collection An array of
Form validation Request classes He third-party packages and even does validation in the controller
permissions Built-in He third-party package or you work it out
API authentication Laravel Passport, Laravel Sanctum Third-party JWT or OAuth extensions
Create API Built-in Dingo API or similar extension pack
Creating a database structure Migrations Create directly with DB statement
localization Built-in Third-party packages
Real-time message queue Laravel Echo, Pusher Use third-party packages or WebSockets directly
Create test data Seeder classes, Model Factories, Faker Manually create test data
Task scheduling Laravel Task Scheduler Scripts and third-party packages
The database MySQL, PostgreSQL, SQLite, SQL Server MongoDB

🔝 Back to directory

Follow the Laravel naming convention

Source of PSR standards.

Also, follow the naming conventions approved by the Laravel community:

object The rules A better way to write it Should avoid writing
The controller singular ArticleController ArticlesController
routing The plural articles/1 article/1
Routing named Dotted snake naming 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 The plural articleComments articleComment, article_comments
The form The plural article_comments article_comment, articleComments
Pivot table Arrange models in alphabetical order article_user user_article, articles_users
Data table field Use a snake and do not use a table name meta_title MetaTitle; article_meta_title
Model parameters Snake named $model->created_at $model->createdAt
A foreign key The singular model name with the _id suffix article_id ArticleId, id_article, articles_id
A primary key id custom_id
The migration 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
methods Hump named getAll get_all
Resource controller table store saveArticle
The test class Hump named testGuestCannotSeeArticle test_guest_cannot_see_article
variable Hump named $articlesWithAuthor $articles_with_author
A collection of Descriptive, plural $activeUsers = User::active()->get()
a c t i v e . active,
data
object Descriptive, singular $activeUser = User::active()->first()
u s e r s . users,
obj
Configuration and language file index Snake named articles_enabled ArticlesEnabled; articles-enabled
view Short line name show-filtered.blade.php showFiltered.blade.php, show_filtered.blade.php
configuration Snake named google_calendar.php googleCalendar.php, google-calendar.php
Content (interface) Adjective or noun AuthenticationInterface Authenticatable, IAuthentication
Trait Use adjectives Notifiable NotificationTrait

🔝 Back to directory

Use short, readable syntax whenever possible

Example:

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

A better way to write:

session('cart');
$request->name;
Copy the code

More examples:

Regular writing A more elegant way to write it
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')

🔝 Back to directory

Use the IOC container to create an instance rather than simply new one

Creating new classes leads to more coupling between classes, making testing more complex. Use an IoC container or injection instead.

Example:

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

A better way to write:

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

🔝 Back to directory

Avoid direct from.envGet data from files

The data is passed to the configuration file and then invoked using the config () helper function

Example:

$apiKey = env('API_KEY');
Copy the code

A better way to write:

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

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

🔝 Back to directory

Use standard formats to store dates and accessors and modifiers to modify date formats

Example:

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

A better way to write:

// 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 }}
Copy the code

🔝 Back to directory

Some other good advice

Never put any logic code in a routing file.

Try not to write raw PHP code in Blade templates.

🔝 Back to directory