You might also want to check out the real-world Laravel example application
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)
نمط المسؤولية الواحدة
شيفرة أكثر في النماذج، شيفرة أقل في المتحكمات
التحقق
الشيفرات المنطقية يجب أن تكون في فئة خادمة منفصلة
لا تكرر نفس الشيفرة
،يفضل استخدام نظام التعامل مع قواعد البيانات المسمى بـEloquent بدل استخدام باني الإستعلامات Query Builder أو الاستخدام المباشر لأوامر الإستعلامات SQL عبر raw، ويفضل استخدام المجموعات بدل المصفوفات
تقليص المهام
لا تقم بتنفيذ الإستعلامات داخل ملفات blade واستخدم التحميل الحثيث مشكلة (N+1)
اضف التعليقات للشيفرة ويفضل استخدام صيغ التعليقات القياسية للمتغيرات والخواص والقيم المعادة إلخ
لا تضع شيفرات js و css داخل ملفات Blade ولا تضع أي ششفرات HTML في فئات php
استخدم ملفات الإعدادت واللغات، والثوابت بدلاً من النص داخل الشيفرة
استخدم الأدوات القياسية المعتمدة من مجتمع لارافيل
اتبع طريقة لارافيل في التسميات
استخدم شيفرة أقصر قابلة للقراءة والفهم السريع قدر المستطاع
استخدم الحاويات أو الواجهات بدلاً من الفئات الجديدة
لا تقم بجلب البيانات من ملف .env
احفظ البيانات في الشكل القياسي. استخدم المسترجعات والمُعدلات في تعديل شكل صيغة التاريخ
ممارسات جيدة أخرى
وظيفة الفئة والطريقة يجب أن تكون مسئولية واحدة فقط، بمعنى آخر يجب ألا تكون الفئة أو الطريقة متعددة المهام ويجب أن تختص بمهمة واحدة فقط
❌ طريقة سيئة:
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;
}
}
✔️ طريقة جيدة:
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;
}
🔝 الرجوع للفهرس
ضع كل الشيفرات الخاصة بالتعامل مع قواعد البيانات في فئات خاصة منفصلة ولا تضعها في المتحكمات
❌ طريقة سيئة:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
✔️ طريقة جيدة:
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();
}
}
🔝 الرجوع للفهرس
انقل شيفرات التحقق من المتحكمات إلى فئات الطلبات Request classes
❌ طريقة سيئة:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
...
}
✔️ طريقة جيدة:
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',
];
}
}
🔝 الرجوع للفهرس
المتحكم يجب أن يكون له مسئولية واحدة فقط، أنقل الشيفرات المنطقية لفئات خادمة منفصلة
❌ طريقة سيئة:
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
...
}
✔️ طريقة جيدة:
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');
}
}
}
🔝 الرجوع للفهرس
أعد استخدام نفس الشيفرة قدر المستطاع ولا تقم بإعادة كتابتها، سيساعدك هذا على عدم وجود أكثر من شيفرة لتنفيذ نفس المهمة، وإعادة استخدام قوالب blade وفئات التعامل مع قواعد البيانات Eloquent
مثال استخدم scope في فئات التعامل مع قواعد البيانات Eloquent
❌ طريقة سيئة:
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();
}
✔️ طريقة جيدة:
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();
}
🔝 الرجوع للفهرس
يفضل استخدام نظام التعامل مع قواعد البيانات المسمى بـEloquent بدل استخدام باني الإستعلامات Query Builder أو الاستخدام المباشر لأوامر الإستعلامات SQL عبر raw، ويفضل استخدام المجموعات بدل المصفوفات
Eloquent يجعلك تكتب شيفرة قابلة للقراءة والصيانة. وأيضاً، Eloquent يحتوي على أدوات وخواص داخلية على سبيل الذكر: الحذف الناعم والأحداث والنطاقات إلخ...
❌ طريقة سيئة:
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
✔️ طريقة جيدة:
Article::has('user.profile')->verified()->latest()->get();
🔝 الرجوع للفهرس
❌ طريقة سيئة:
$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();
✔️ طريقة جيدة:
$category->article()->create($request->validated());
🔝 الرجوع للفهرس
❌ طريقة سيئة:
~لعدد 100 مستخدم سيُنفذ 101 استعلام على قاعدة البيانات
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
✔️ طريقة جيدة:
~لعدد 100 مستخدم سيُنفذ 2 استعلام فقط على قاعدة البيانات~
$users = User::with('profile')->get();
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
🔝 الرجوع للفهرس
❌ طريقة سيئة:
if (count((array) $builder->getQuery()->joins) > 0)
طريقة أفضل:
// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)
✔️ طريقة جيدة:
if ($this->hasJoins())
🔝 الرجوع للفهرس
❌ طريقة سيئة:
let article = `{{ json_encode($article) }}`;
✔️ طريقة أفضل:
<input id="article" type="hidden" value='@json($article)'>
Or
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
في ملف جافا سكريبت:
let article = $('#article').val();
الطريقة الأفضل هي استخدام الحزم الخاصة بنقل البيانات من PHP إلى جافا سكريبت.
🔝 الرجوع للفهرس
❌ طريقة سيئة:
public function isNormal()
{
return $article->type === 'normal';
}
return back()->with('message', 'Your article has been added!');
✔️ طريقة جيدة:
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
🔝 الرجوع للفهرس
يفضل استخدام الأدوات المدمجة مع إطار عمل لارافيل والحزم المقترحة من مجتمع لارفيل بدل استخدام غيرها، أي مطور سيعمل على تطبيقك في وقت لاحق سيحتاج إلى تعلم تلك الأدوات التي لا يشيع استخدامها في تطبيقات لارافيل، وأيضاً أطلب المساعدة من مجتمع لارافيل عندما تقرر الإعتماد على أحد الأدوات أو الحزم، ولا تجعل عميلك يدفع مقابل ذلك.
الوظيفة | الأدوات القياسية | أدوات الطرف الثالث |
---|---|---|
Authorization | Policies | Entrust, Sentinel and other packages |
Compiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages |
Development Environment | Laravel Sail, Homestead | Docker |
Deployment | Laravel Forge | Deployer and other solutions |
Unit testing | PHPUnit, Mockery | Phpspec, Pest |
Browser testing | Laravel Dusk | Codeception |
DB | Eloquent | SQL, Doctrine |
Templates | Blade | Twig |
Working with data | Laravel collections | Arrays |
Form validation | Request classes | 3rd party packages, validation in controller |
Authentication | Built-in | 3rd party packages, your own solution |
API authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages |
Creating API | Built-in | Dingo API and similar packages |
Working with DB structure | Migrations | Working with DB structure directly |
Localization | Built-in | 3rd party packages |
Realtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly |
Generating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually |
Task scheduling | Laravel Task Scheduler | Scripts and 3rd party packages |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
🔝 الرجوع للفهرس
راجع PSR standards
وأيضا، راجع اصطلاح التسميات المقبول من جهه مجتمع لارافيل:
ماذا | كيف | جيدة | سيئة |
---|---|---|---|
Controller | singular | ArticleController | |
Route | plural | articles/1 | |
Route name | snake_case with dot notation | users.show_active | |
Model | singular | User | |
hasOne or belongsTo relationship | singular | articleComment | |
All other relationships | plural | articleComments | |
Table | plural | article_comments | |
Pivot table | singular model names in alphabetical order | article_user | |
Table column | snake_case without model name | meta_title | |
Model property | snake_case | $model->created_at | |
Foreign key | singular model name with _id suffix | article_id | |
Primary key | - | id | |
Migration | - | 2017_01_01_000000_create_articles_table | |
Method | camelCase | getAll | |
Method in resource controller | table | store | |
Method in test class | camelCase | testGuestCannotSeeArticle | |
Variable | camelCase | $articlesWithAuthor | |
Collection | descriptive, plural | $activeUsers = User::active()->get() | |
Object | descriptive, singular | $activeUser = User::active()->first() | |
Config and language files index | snake_case | articles_enabled | |
View | kebab-case | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Contract (interface) | adjective or noun | AuthenticationInterface | |
Trait | adjective | Notifiable | |
Trait (PSR) | adjective | NotifiableTrait | |
Enum | singular | UserType | |
FormRequest | singular | UpdateUserRequest | |
Seeder | singular | UserSeeder |
🔝 الرجوع للفهرس
❌ طريقة سيئة:
$request->session()->get('cart');
$request->input('name');
✔️ طريقة جيدة:
session('cart');
$request->name;
أمثلة أكثر:
جمل مركبة | جمل أقصر وأكثر قابلية للقراءة |
---|---|
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') |
🔝 الرجوع للفهرس
إنشاء فئات جديدة يخلق شيئا من التشويش بين الفئات ويعقد عملة الإختبار، الأفضل الإعتماد على الحاويات أو الواجهات في هذا الأمر
❌ طريقة سيئة:
$user = new User;
$user->create($request->validated());
✔️ طريقة جيدة:
public function __construct(User $user)
{
$this->user = $user;
}
...
$this->user->create($request->validated());
🔝 الرجوع للفهرس
مرر البيانات لملف الإعدادت ومن ثَم استخدم الدالة المساعدة `config()` لاستخدامها في جلب البيانات المخزنة في ملف الإعدادت داخل تطبيقك.
❌ طريقة سيئة:
$apiKey = env('API_KEY');
✔️ طريقة جيدة:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
🔝 الرجوع للفهرس
❌ طريقة سيئة:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
✔️ طريقة جيدة:
// Model
protected $casts = [
'ordered_at' => 'datetime',
];
public function getSomeDateAttribute($date)
{
return $date->format('m-d');
}
// ملف العرض
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}
🔝 الرجوع للفهرس
لا تضع أي شيفرة برمجية في ملفات الموجهات.
قلل من استخدامك الشيفرات البرمجية المنطقية في ملفات العرض blade.
🔝 الرجوع للفهرس