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.env
Get 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 | |
routing | The plural | articles/1 | |
Routing named | Dotted snake naming | users.show_active | |
model | singular | User | |
HasOne or belongsTo relationship | singular | articleComment | |
All other relationships | The plural | articleComments | |
The form | The plural | article_comments | |
Pivot table | Arrange models in alphabetical order | article_user | |
Data table field | Use a snake and do not use a table name | meta_title | |
Model parameters | Snake named | $model->created_at | |
A foreign key | The singular model name with the _id suffix | article_id | |
A primary key | – | id | |
The migration | – | 2017_01_01_000000_create_articles_table | |
methods | Hump named | getAll | |
Resource controller | table | store | |
The test class | Hump named | testGuestCannotSeeArticle | |
variable | Hump named | $articlesWithAuthor | |
A collection of | Descriptive, plural | $activeUsers = User::active()->get() | data |
object | Descriptive, singular | $activeUser = User::active()->first() | obj |
Configuration and language file index | Snake named | articles_enabled | |
view | Short line name | show-filtered.blade.php | |
configuration | Snake named | google_calendar.php | |
Content (interface) | Adjective or noun | AuthenticationInterface | |
Trait | Use adjectives | Notifiable |
🔝 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.env
Get 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