Translations:
Nederlands (by Protoqol)
한국어 (by cherrypick)
ภาษาไทย (by kongvut sangkla)
فارسی (by amirhossein baghaie)
Українська (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)
Deutsch (by Sujal Patel)
Italiana (by Sujal Patel)
العربية (by ahmedsaoud31)
Non è un adattamento Laravel di principi, schemi, ecc. SOLID. Qui troverai le migliori pratiche che di solito vengono ignorate nei progetti Laravel nella vita reale.
Principio della sola responsabilità
Modelli grassi, controller skinny
La logica aziendale dovrebbe essere nella classe di servizio
Non eseguire query nei modelli Blade e utilizzare il caricamento desideroso (problema N + 1)
Non inserire JS e CSS nei modelli Blade e non inserire HTML nelle classi PHP
Usa file di configurazione e lingua, costanti anziché testo nel codice
Utilizzare gli strumenti standard Laravel accettati dalla community
Segui le convenzioni di denominazione di Laravel
Utilizzare la sintassi più breve e più leggibile ove possibile
Utilizzare il contenitore o le facciate IoC anziché la nuova classe
Non ottiene direttamente i dati dal file .env
Una classe e un metodo dovrebbero avere una sola responsabilità.
Male:
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;
}
}
Buono:
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;
}
Inserisci tutta la logica legata al DB nei Model Eloquent oppure nei Repository a seconda che tu stia usando il Query Builder o le query SQL raw.
Male:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
Buono:
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();
}
}
Sposta le logiche di validazione dai controller alle Request class.
Male:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
....
}
Buono:
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',
];
}
}
Un controller deve avere una sola responsabilità, quindi sposta la logica di business dai controller alle classi di servizio.
Male:
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
....
}
Buono:
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');
}
}
}
Riutilizzare il codice quando è possibile. Il Principio di Singola Responsabilità (SRP) ti aiuta a evitare la duplicazione. Inoltre, riutilizza i template blade, usa gli eloquenti scopes, ecc.
Male:
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();
}
Buono:
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();
}
Favorisci l'utilizzo dei Model Eloquent rispetto al Query Builder e alle query SQL raw. Preferisci le Collection agli array
Eloquent ti consente di scrivere codice leggibile e manutenibile. Inoltre, Eloquent ha ottimi strumenti integrati come eliminazioni soft, eventi, scopes, ecc.
Male:
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
Buono:
Article::has('user.profile')->verified()->latest()->get();
Male:
$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();
Buono:
$category->article()->create($request->validated());
Male (fo 100 utenti, verranno eseguite 101 query DB):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
Buono (per 100 utenti, verranno eseguite 2 query DB):
$users = User::with('profile')->get();
...
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
Male:
if (count((array) $builder->getQuery()->joins) > 0)
Meglio:
// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)
Buono:
if ($this->hasJoins())
Male:
let article = `{{ json_encode($article) }}`;
Meglio:
<input id="article" type="hidden" value='@json($article)'>
Or
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
In un file Javascript:
let article = $('#article').val();
Il modo migliore è utilizzare il pacchetto PHP-JS specializzato per trasferire i dati.
Male:
public function isNormal()
{
return $article->type === 'normal';
}
return back()->with('message', 'Your article has been added!');
Buono:
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
Favorisci l'utilizzo delle funzionalità integrate in Laravel e i pacchetti della community anziché utilizzare pacchetti e strumenti di terze parti. Altrimenti, qualsiasi sviluppatore che lavorerà con la tua app in futuro dovrà imparare nuovi strumenti. Inoltre, le possibilità di ottenere aiuto dalla comunità Laravel sono significativamente inferiori quando si utilizza un pacchetto o uno strumento di terze parti. Non far pagare il tuo cliente per quello.
Task | Strumenti standard | Strumenti di terze parti |
---|---|---|
Autorizzazione | Politiche | Affida, Sentinel e altri pacchetti |
Compiling assets | Laravel Mix | Grunt, Gulp, 3rd party packages |
Ambiente di sviluppo | Laravel Sail, Fattoria | docker |
Distribuzione | Laravel Forge | Deployer e altre soluzioni |
Test unitari | PHPUnit, Mockery | Phpspec |
Test del browser | Laravel Dusk | Codeception |
DB | Eloquente | SQL, Doctrine |
Modelli | Lama | Ramoscello |
Lavorare con i dati | Collezioni Laravel | Array |
Convalida del modulo | Richiedi classi | Pacchetti di terze parti, convalida nel controller |
Autenticazione | Incorporato | Pacchetti di terze parti, la tua soluzione |
Autenticazione API | Laravel Passport, Laravel Sanctum | Pacchetti JWT e OAuth di terze parti |
Creazione dell'API | Incorporato | API Dingo e pacchetti simili |
Lavorare con la struttura DB | Migrazioni | Lavorare direttamente con la struttura DB |
Localizzazione | Incorporato | Pacchetti di terze parti |
Interfacce utente in tempo reale | Laravel Echo, Pusher | Pacchetti di terze parti e funzionamento diretto con WebSocket |
Generazione di dati di test | Classi di seminatrice, Fabbriche modello, Faker | Creazione manuale dei dati di test |
Pianificazione delle attività | Utilità di pianificazione Laravel | Script e pacchetti di terze parti |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
Seguire Standard PSR.
Inoltre, segui le convenzioni di denominazione accettate dalla comunità Laravel:
Cosa | Come | Buono | Male |
---|---|---|---|
Controller | singolare | Controllo articolo | |
Route | plurale | articoli / 1 | |
Named route | snake_case con notazione punto | users.show_active | |
Model | singolare | Utente | |
hasOne o appartiene alla relazione | singolare | articleComment | |
Tutte le altre relazioni | plurale | articleComments | |
Tabella | plurale | commenti_articolo | |
Tabella pivot | nomi di modelli singolari in ordine alfabetico | user_user | |
Colonna della tabella | snake_case senza nome modello | meta_title | |
Proprietà del Model | snake_case | $ model-> Created_at | |
Foreign key | singular model name with _id suffix | article_id | |
Chiave primaria | - | id | |
Migration | - | 2017_01_01_000000_create_articles_table | |
Metodo | camelCase | getAll | |
Metodo nel resource controller | tavolo | store | |
Metodo nella test class | camelCase | testGuestCannotSeeArticle | |
Variabile | camelCase | $ articoliWithAuthor | |
Collection | descrittivo, plurale | $ activeUsers = Utente :: active () -> get () | |
Oggetto | descrittivo, singolare | $ activeUser = User :: active () -> first () | |
Indice file di configurazione e lingua | snake_case | articoli abilitati | |
View | astuccio per kebab | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Contratto (interfaccia) | aggettivo o sostantivo | AuthenticationInterface | |
Trait | aggettivo | Notificabile |
Male:
$request->session()->get('cart');
$request->input('name');
Buono:
session('cart');
$request->name;
Più esempi:
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 |
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') |
La sintassi new Class crea un accoppiamento stretto tra le classi e complica i test. Utilizzare invece il container IoC o i Facades.
Male:
$user = new User;
$user->create($request->validated());
Buono:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->validated());
Passa i dati presenti nell'.env file ai file di configurazione e quindi usa l'helper config ()
per prelevare i dati all'interno dell'applicazione.
Male:
$apiKey = env('API_KEY');
Buono:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
Memorizza le date nel formato standard. Utilizza gli accessors e i mutators per modificare il formato della data
Male:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
Buono:
// 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 }}
Non inserire mai alcuna logica nei file di route.
Ridurre al minimo l'utilizzo di vanilla PHP nei template Blade.