You might also want to check out the real-world Laravel example application
Translations:
Nederlands (by Protoqol)
한국어 (by cherrypick)
ภาษาไทย (by kongvut sangkla)
বাংলা (by Anowar Hossain)
فارسی (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)
Azərbaycanca (by Maharramoff)
العربية (by ahmedsaoud31)
اردو (by RizwanAshraf1)
Fette Models, schlanke Controller
Geschäftslogik sollte in einer Serviceklasse sein
Wiederholen Sie sich nicht / Don't repeat yourself (DRY)
Führen Sie keine Abfragen in Blade-Templates aus und verwenden Sie eager loading (N + 1-Problem)
Schreiben Sie kein JS und CSS in Blade-Templates und schreiben Sie kein HTML in PHP-Klassen
Verwenden Sie Konfigurations- und Sprachdateien und Konstanten anstelle von Text im Code
Verwenden Sie Standard-Laravel-Tools, die von der Community akzeptiert werden
Befolgen Sie die Namenskonventionen von Laravel
Verwenden Sie nach Möglichkeit eine kürzere und besser lesbare Syntax
Verwenden Sie IoC-Container oder Facades, statt neue Klassen zu instanziieren
Rufen Sie Daten nicht direkt aus der .env
-Datei ab
Eine Klasse und eine Methode sollten nur eine Verantwortung haben.
Schlecht:
public function getFullNameAttribute(): string
{
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;
}
}
Gut:
public function getFullNameAttribute(): string
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerifiedClient(): bool
{
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong(): string
{
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort(): string
{
return $this->first_name[0] . '. ' . $this->last_name;
}
🔝 Zurück zum Inhaltsverzeichnis
Fügen Sie die gesamte DB-bezogene Logik in Eloquent-Models oder in Repository-Klassen ein, wenn Sie Query Builder oder SQL-Rohabfragen verwenden.
Schlecht:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
Gut:
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();
}
}
🔝 Zurück zum Inhaltsverzeichnis
Verschieben Sie die Validierung von Controllern in Request-Klassen.
Schlecht:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
...
}
Gut:
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',
];
}
}
🔝 Zurück zum Inhaltsverzeichnis
Ein Controller darf nur eine Verantwortung haben, also verschieben Sie die Geschäftslogik von Controllern in Serviceklassen.
Schlecht:
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
...
}
Gut:
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');
}
}
}
🔝 Zurück zum Inhaltsverzeichnis
Verwenden Sie Code wieder, wenn Sie können. Das Single-Responsibility-Prinzip (SRP) hilft Doppelarbeit zu vermeiden. Verwenden Sie auch Blade Templates, Eloquent Scopes etc. wieder.
Schlecht:
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();
}
Gut:
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();
}
🔝 Zurück zum Inhaltsverzeichnis
Verwenden Sie lieber Eloquent anstelle des Query Builders und rohen SQL-Abfragen. Bevorzugen Sie Collections gegenüber Arrays
Eloquent ermöglicht es, lesbaren und wartbaren Code schreiben. Außerdem verfügt Eloquent über großartige integrierte Tools wie Soft Deletes, Events, Scopes usw.
Schlecht:
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
Gut:
Article::has('user.profile')->verified()->latest()->get();
🔝 Zurück zum Inhaltsverzeichnis
Schlecht:
$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();
Gut:
$category->article()->create($request->validated());
🔝 Zurück zum Inhaltsverzeichnis
Schlecht (für 100 Benutzer werden 101 Datenbankabfragen ausgeführt):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
Gut (für 100 Benutzer werden 2 Datenbankabfragen ausgeführt):
$users = User::with('profile')->get();
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
🔝 Zurück zum Inhaltsverzeichnis
Kommentieren Sie Ihren Code, aber bevorzugen Sie beschreibende Methoden- und Variablennamen gegenüber Kommentaren
Schlecht:
if (count((array) $builder->getQuery()->joins) > 0)
Besser:
// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)
Gut:
if ($this->hasJoins())
🔝 Zurück zum Inhaltsverzeichnis
Schlecht:
let article = `{{ json_encode($article) }}`;
Besser:
<input id="article" type="hidden" value='@json($article)'>
oder
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
In einer JavaScript-Datei:
let article = $('#article').val();
Am besten verwenden Sie ein spezielles PHP-zu-JS-Paket, um die Daten zu übertragen.
🔝 Zurück zum Inhaltsverzeichnis
Schlecht:
public function isNormal()
{
return $article->type === 'normal';
}
return back()->with('message', 'Your article has been added!');
Gut:
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
🔝 Zurück zum Inhaltsverzeichnis
Verwenden Sie vorzugsweise integrierte Laravel-Funktionen und Community-Pakete, anstatt Pakete und Tools von Drittanbietern zu verwenden. Ansonsten muss jeder Entwickler, der in Zukunft an Ihrer App arbeitet, neue Tools erlernen. Außerdem sind die Chancen, Hilfe von der Laravel-Community zu erhalten, erheblich geringer, wenn Sie ein Paket oder Tool eines Drittanbieters verwenden. Lassen Sie Ihren Kunden nicht dafür bezahlen.
Aufgabe | Standardwerkzeuge | Tools von Drittanbietern |
---|---|---|
Autorisierung | Policies | Entrust, Sentinel und andere Pakete |
Assets kompilieren | Laravel Mix, Vite | Grunt, Gulp, 3rd-Party-Pakete |
Entwicklungsumgebung | Laravel Sail, Homestead | Docker |
Bereitstellung | Laravel Forge | Deployer und andere Lösungen |
Unit Tests | PHPUnit, Mockery | Phpspec, Pest |
Browsertests | Laravel Dusk | Codeception |
DB | Eloquent | SQL, Doctrine |
Templates | Blade | Twig |
Mit Daten arbeiten | Laravel Collections | Arrays |
Formularvalidierung | Request-Klassen | Pakete von Drittanbietern, Validierung im Controller |
Authentifizierung | Integriert | Pakete von Drittanbietern, Ihre eigene Lösung |
API-Authentifizierung | Laravel Passport, Laravel Sanctum | JWT- und OAuth-Pakete von Drittanbietern |
API erstellen | Integriert | Dingo API und ähnliche Pakete |
Mit DB-Struktur arbeiten | Migrationen | Direkt mit der DB-Struktur arbeiten |
Lokalisierung | Integriert | Pakete von Drittanbietern |
Echtzeit-Benutzeroberflächen | Laravel Echo, Pusher | Pakete von Drittanbietern und direktes Arbeiten mit WebSockets |
Testdaten generieren | Seeder-Klassen, Model Factories, Faker | Testdaten manuell erstellen |
Aufgabenplanung | Laravel Task Scheduler | Skripte und Pakete von Drittanbietern |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
🔝 Zurück zum Inhaltsverzeichnis
Folgen Sie den PSR standards.
Befolgen Sie außerdem die von der Laravel-Community akzeptierten Namenskonventionen:
Was | Wie | Gut | Schlecht |
---|---|---|---|
Controller | singular | ArticleController | |
Route | plural | articles/1 | |
Benannte Route | snake_case mit Punktnotation | users.show_active | |
Model | singular | User | |
hasOne oder belongsTo Beziehung | singular | articleComment | |
Alle anderen Beziehungen | plural | articleComments | |
Tabelle | Plural | article_comments | |
Pivot-Tabelle | singuläre Modellnamen in alphabetischer Reihenfolge | article_user | |
Tabellenspalte | snake_case ohne Modellname | meta_title | |
Model-Eigenschaft | snake_case | $model->created_at | |
Fremdschlüssel | singulärer Modellname mit Suffix _id | article_id | |
Primärschlüssel | - | id | |
Migration | - | 2017_01_01_000000_create_articles_table | |
Methode | camelCase | getAll | |
Methode im Ressourcencontroller | Tabelle | store | |
Methode in einer Testklasse | camelCase | testGuestCannotSeeArticle | |
Variable | camelCase | $articlesWithAuthor | |
Collection | beschreibend, plural | $activeUsers = User::active()->get() | |
Objekt | beschreibend, singular | $activeUser = User::active()->first() | |
Konfigurations- und Sprachdateien index | snake_case | articles_enabled | |
View | kebab-case | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Vertrag (Interface) | Adjektiv oder Substantiv | AuthenticationInterface | |
Trait | Adjektiv | Notifiable | |
Trait (PSR) | adjective | NotifiableTrait | |
Enum | singular | UserType | |
FormRequest | singular | UpdateUserRequest | |
Seeder | singular | UserSeeder |
🔝 Zurück zum Inhaltsverzeichnis
Schlecht:
$request->session()->get('cart');
$request->input('name');
Gut:
session('cart');
$request->name;
Mehr Beispiele:
Gängige Syntax | Kürzere und lesbarere 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') |
🔝 Zurück zum Inhaltsverzeichnis
Die Syntax new Class
erzeugt eine enge Kopplung zwischen Klassen und erschwert dadurch das Testen. Verwenden Sie stattdessen IoC-Container oder Facades.
Schlecht:
$user = new User;
$user->create($request->validated());
Gut:
public function __construct(User $user)
{
$this->user = $user;
}
...
$this->user->create($request->validated());
🔝 Zurück zum Inhaltsverzeichnis
Übergeben Sie die Daten stattdessen an eine Konfigurationsdatei und verwenden Sie dann die Hilfsfunktion config()
, um die Daten in ihrer Anwendung zu verwenden.
Schlecht:
$apiKey = env('API_KEY');
Gut:
// config/api.php
'key' => env('API_KEY'),
// Verwendung der Daten
$apiKey = config('api.key');
🔝 Zurück zum Inhaltsverzeichnis
Speichern Sie Datumsangaben im Standardformat. Verwenden Sie Accessoren und Mutatoren, um das Datumsformat zu ändern
Strings für Daten sind generell weniger belastbar als Objekte (z.B. Carbon Objekte). Es ist empfehlenswert Carbon-Instanzen zwischen Klassen zu übergeben. Formatierung sollte in den blade Dateien erfolgen:
Schlecht:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
Gut:
// Model
protected $casts = [
'ordered_at' => 'datetime',
];
// Blade view
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->format('m-d') }}
🔝 Zurück zum Inhaltsverzeichnis
Logik sollte nicht in Routes Dateien eingebaut werden.
Minimieren Sie die Verwendung von Vanilla PHP in Blade-Templates.