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 Maciej Jeziorski)
Deutsche (by Sujal Patel)
Italiana (by Sujal Patel)
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;
}
Put all DB related logic into Eloquent models or into Repository classes if you're using Query Builder or raw SQL queries.
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 la convalida dai controller alle classi di richiesta.
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 aziendale 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. SRP ti aiuta a evitare la duplicazione. Inoltre, riutilizza i modelli di blade, usa gli ambiti eloquenti 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();
}
Preferisco usare Eloquent rispetto a Query Builder e query SQL non elaborate. Preferisce raccolte su array
Eloquent ti consente di scrivere codice leggibile e gestibile. Inoltre, Eloquent ha ottimi strumenti integrati come eliminazioni soft, eventi, ambiti 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
Commenta il tuo codice, ma preferisci il metodo descrittivo e i nomi delle variabili rispetto ai commenti
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'));
Preferisci utilizzare la funzionalità Laravel integrata e i pacchetti della community anziché utilizzare pacchetti e strumenti di terze parti. 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.
Compito | 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 | 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 | Passaporto Laravel | 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 | |
Rotta | plurale | articoli / 1 | |
Percorso denominato | snake_case con notazione punto | users.show_active | |
Modello | 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 modello | snake_case | $ model-> Created_at | |
Foreign key | singular model name with _id suffix | article_id | |
Chiave primaria | - | id | |
Migrazione | - | 2017_01_01_000000_create_articles_table | |
Metodo | camelCase | getAll | |
Metodo nel controller delle risorse | tavolo | store | |
Metodo nella classe di prova | camelCase | testGuestCannotSeeArticle | |
Variabile | camelCase | $ articoliWithAuthor | |
Collezione | descrittivo, plurale | $ activeUsers = Utente :: active () -> get () | |
Oggetto | descrittivo, singolare | $ activeUser = User :: active () -> first () | |
Indice file di configurazione e lingua | snake_case | articoli abilitati | |
Visualizza | astuccio per kebab | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Contratto (interfaccia) | aggettivo o sostantivo | Autenticabile | |
Tratto | 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 nuova sintassi della classe crea un accoppiamento stretto tra le classi e complica i test. Utilizzare invece il contenitore o le facciate IoC.
Male:
$user = new User;
$user->create($request->validated());
Buono:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->validated());
Passa invece i dati ai file di configurazione e quindi usa la funzione di supporto config ()
per usare i dati in un'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. Utilizzare accessori e mutatori 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 PHP vaniglia nei modelli Blade.