[
  {
    "path": "README.md",
    "content": "![Laravel best practices](/images/logo-english.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app) and [Eloquent SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nTranslations:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[Indonesia](indonesia.md) (by [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[简体中文](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[繁體中文](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Română](romanian.md) (by [als698](https://github.com/als698))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Turkmen](turkmen.md) (by [Artyom Osepyan](https://github.com/artengin))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[မြန်မာဘာသာ](burmese.md) (by [Kaung Zay Yan](https://github.com/KaungZayY))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Contents\n\n[Single responsibility principle](#single-responsibility-principle)\n\n[Methods should do just one thing](#methods-should-do-just-one-thing)\n\n[Fat models, skinny controllers](#fat-models-skinny-controllers)\n\n[Validation](#validation)\n\n[Business logic should be in service class](#business-logic-should-be-in-service-class)\n\n[Don't repeat yourself (DRY)](#dont-repeat-yourself-dry)\n\n[Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays](#prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays)\n\n[Mass assignment](#mass-assignment)\n\n[Do not execute queries in Blade templates and use eager loading (N + 1 problem)](#do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem)\n\n[Chunk data for data-heavy tasks](#chunk-data-for-data-heavy-tasks)\n\n[Comment your code, but prefer descriptive method and variable names over comments](#prefer-descriptive-method-and-variable-names-over-comments)\n\n[Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes](#do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes)\n\n[Use config and language files, constants instead of text in the code](#use-config-and-language-files-constants-instead-of-text-in-the-code)\n\n[Use standard Laravel tools accepted by community](#use-standard-laravel-tools-accepted-by-community)\n\n[Follow Laravel naming conventions](#follow-laravel-naming-conventions)\n\n[Convention over configuration](#convention-over-configuration)\n\n[Use shorter and more readable syntax where possible](#use-shorter-and-more-readable-syntax-where-possible)\n\n[Use IoC / Service container instead of new Class](#use-ioc--service-container-instead-of-new-class)\n\n[Do not get data from the `.env` file directly](#do-not-get-data-from-the-env-file-directly)\n\n[Store dates in the standard format. Use accessors and mutators to modify date format](#store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format)\n\n[Do not use DocBlocks](#do-not-use-docblocks)\n\n[Other good practices](#other-good-practices)\n\n### **Single responsibility principle**\n\nA class should have only one responsibility.\n\nBad:\n\n```php\npublic function update(Request $request): string\n{\n    $validated = $request->validate([\n        'title' => 'required|max:255',\n        'events' => 'required|array:date,type'\n    ]);\n\n    foreach ($request->events as $event) {\n        $date = $this->carbon->parse($event['date'])->toString();\n\n        $this->logger->log('Update event ' . $date . ' :: ' . $);\n    }\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\nGood:\n\n```php\npublic function update(UpdateRequest $request): string\n{\n    $this->logService->logEvents($request->events);\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Methods should do just one thing**\n\nA function should do just one thing and do it well.\n\nBad:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGood:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Fat models, skinny controllers**\n\nPut all DB related logic into Eloquent models.\n\nBad:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGood:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders(): Collection\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Validation**\n\nMove validation from controllers to Request classes.\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules(): array\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Business logic should be in service class**\n\nA controller must have only one responsibility, so move business logic from controllers to service classes.\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image): void\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Don't repeat yourself (DRY)**\n\nReuse code when you can. SRP is helping you to avoid duplication. Also, reuse Blade templates, use Eloquent scopes etc.\n\nBad:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGood:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', true)->whereNotNull('deleted_at');\n}\n\npublic function getActive(): Collection\n{\n    return $this->active()->get();\n}\n\npublic function getArticles(): Collection\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays**\n\nEloquent allows you to write readable and maintainable code. Also, Eloquent has great built-in tools like soft deletes, events, scopes etc. You may want to check out [Eloquent to SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nBad:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGood:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Back to contents](#contents)\n\n### **Mass assignment**\n\nBad:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nGood:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Back to contents](#contents)\n\n### **Do not execute queries in Blade templates and use eager loading (N + 1 problem)**\n\nBad (for 100 users, 101 DB queries will be executed):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nGood (for 100 users, 2 DB queries will be executed):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Back to contents](#contents)\n\n### **Chunk data for data-heavy tasks**\n\nBad:\n\n```php\n$users = $this->get();\n\nforeach ($users as $user) {\n    ...\n}\n```\n\nGood:\n\n```php\n$this->chunk(500, function ($users) {\n    foreach ($users as $user) {\n        ...\n    }\n});\n```\n\n[🔝 Back to contents](#contents)\n\n### **Prefer descriptive method and variable names over comments**\n\nBad:\n\n```php\n// Determine if there are any joins\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGood:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Back to contents](#contents)\n\n### **Do not put JS and CSS in Blade templates and do not put any HTML in PHP classes**\n\nBad:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nBetter:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nIn a Javascript file:\n\n```javascript\nlet article = $('#article').val();\n```\n\nThe best way is to use specialized PHP to JS package to transfer the data.\n\n[🔝 Back to contents](#contents)\n\n### **Use config and language files, constants instead of text in the code**\n\nBad:\n\n```php\npublic function isNormal(): bool\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nGood:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Back to contents](#contents)\n\n### **Use standard Laravel tools accepted by community**\n\nPrefer to use built-in Laravel functionality and community packages instead of using 3rd party packages and tools. Any developer who will work with your app in the future will need to learn new tools. Also, chances to get help from the Laravel community are significantly lower when you're using a 3rd party package or tool. Do not make your client pay for that.\n\nTask | Standard tools | 3rd party tools\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Back to contents](#contents)\n\n### **Follow Laravel naming conventions**\n\nFollow [PSR standards](https://www.php-fig.org/psr/psr-12/).\n\nAlso, follow naming conventions accepted by Laravel community:\n\nWhat | How | Good | Bad\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Back to contents](#contents)\n\n### **Convention over configuration**\n\nAs long as you follow certain conventions, you do not need to add additional configuration.\n\nBad:\n\n```php\n// Table name 'Customer'\n// Primary key 'customer_id'\nclass Customer extends Model\n{\n    const CREATED_AT = 'created_at';\n    const UPDATED_AT = 'updated_at';\n\n    protected $table = 'Customer';\n    protected $primaryKey = 'customer_id';\n\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');\n    }\n}\n```\n\nGood:\n\n```php\n// Table name 'customers'\n// Primary key 'id'\nclass Customer extends Model\n{\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class);\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Use shorter and more readable syntax where possible**\n\nBad:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGood:\n\n```php\nsession('cart');\n$request->name;\n```\n\nMore examples:\n\nCommon syntax | Shorter and more readable syntax\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id` (in PHP 8: `$object->relation?->id`)\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Back to contents](#contents)\n\n### **Use IoC / Service container instead of new Class**\n\nnew Class syntax creates tight coupling between classes and complicates testing. Use IoC container or facades instead.\n\nBad:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGood:\n\n```php\npublic function __construct(protected User $user) {}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Back to contents](#contents)\n\n### **Do not get data from the `.env` file directly**\n\nPass the data to config files instead and then use the `config()` helper function to use the data in an application.\n\nBad:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGood:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 Back to contents](#contents)\n\n### **Store dates in the standard format. Use accessors and mutators to modify date format**\n\nA date as a string is less reliable than an object instance, e.g. a Carbon-instance. It's recommended to pass Carbon objects between classes instead of date strings. Rendering should be done in the display layer (templates):\n\nBad:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGood:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\n// Blade view\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->format('m-d') }}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Do not use DocBlocks**\n\nDocBlocks reduce readability. Use a descriptive method name and modern PHP features like return type hints instead.\n\nBad:\n\n```php\n/**\n * The function checks if given string is a valid ASCII string\n *\n * @param string $string String we get from frontend which might contain\n *                       illegal characters. Returns True is the string\n *                       is valid.\n *\n * @return bool\n * @author  John Smith\n *\n * @license GPL\n */\n\npublic function checkString($string)\n{\n}\n```\n\nGood:\n\n```php\npublic function isValidAsciiString(string $string): bool\n{\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### **Other good practices**\n\nAvoid using patterns and tools that are alien to Laravel and similar frameworks (i.e. RoR, Django). If you like Symfony (or Spring) approach for building apps, it's a good idea to use these frameworks instead.\n\nNever put any logic in routes files.\n\nMinimize usage of vanilla PHP in Blade templates.\n\nUse in-memory DB for testing.\n\nDo not override standard framework features to avoid problems related to updating the framework version and many other issues.\n\nUse modern PHP syntax where possible, but don't forget about readability.\n\nAvoid using View Composers and similar tools unless you really know what you're doing. In most cases, there is a better way to solve the problem.\n\n[🔝 Back to contents](#contents)\n"
  },
  {
    "path": "arabic.md",
    "content": "![Laravel best practices](/images/logo-arabic.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## <p dir=\"rtl\">الترجمات</p>\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## <p dir=\"rtl\">الفهرس</p>\n\n[<p dir=\"rtl\">نمط المسؤولية الواحدة</p>](#1)\n\n[<p dir=\"rtl\">شيفرة أكثر في النماذج، شيفرة أقل في المتحكمات</p>](#2)\n\n[<p dir=\"rtl\">التحقق</p>](#3)\n\n[<p dir=\"rtl\">الشيفرات المنطقية يجب أن تكون في فئة خادمة منفصلة</p>](#4)\n\n[<p dir=\"rtl\">لا تكرر نفس الشيفرة</p>](#5)\n،\n[<p dir=\"rtl\">يفضل استخدام نظام التعامل مع قواعد البيانات المسمى بـEloquent بدل استخدام باني الإستعلامات Query Builder أو الاستخدام المباشر لأوامر الإستعلامات SQL عبر raw، ويفضل استخدام المجموعات بدل المصفوفات</p>](#6)\n\n[<p dir=\"rtl\">تقليص المهام</p>](#7)\n\n[<p dir=\"rtl\">لا تقم بتنفيذ الإستعلامات داخل ملفات blade واستخدم التحميل الحثيث مشكلة (N+1)</p>](#8)\n\n[<p dir=\"rtl\">اضف التعليقات للشيفرة ويفضل استخدام صيغ التعليقات القياسية للمتغيرات والخواص والقيم المعادة إلخ</p>](#9)\n\n[<p dir=\"rtl\">لا تضع شيفرات js و css داخل ملفات Blade ولا تضع أي ششفرات HTML في فئات php</p>](#10)\n\n[<p dir=\"rtl\">استخدم ملفات الإعدادت واللغات، والثوابت بدلاً من النص داخل الشيفرة</p>](#11)\n\n[<p dir=\"rtl\">استخدم الأدوات القياسية المعتمدة من مجتمع لارافيل</p>](#12)\n\n[<p dir=\"rtl\">اتبع طريقة لارافيل في التسميات</p>](#13)\n\n[<p dir=\"rtl\">استخدم شيفرة أقصر قابلة للقراءة والفهم السريع قدر المستطاع</p>](#14)\n\n[<p dir=\"rtl\">استخدم الحاويات أو الواجهات بدلاً من الفئات الجديدة</p>](#15)\n\n[<p dir=\"rtl\">لا تقم بجلب البيانات من ملف `.env`</p>](#16)\n\n[<p dir=\"rtl\">احفظ البيانات في الشكل القياسي. استخدم المسترجعات والمُعدلات في تعديل شكل صيغة التاريخ</p>](#17)\n\n[<p dir=\"rtl\">ممارسات جيدة أخرى</p>](#18)\n### <p dir=\"rtl\">1</p>\n### **<p dir=\"rtl\">نمط المسئولية الواحدة</p>**\n\n<p dir=\"rtl\">وظيفة الفئة والطريقة يجب أن تكون مسئولية واحدة فقط، بمعنى آخر يجب ألا تكون الفئة أو الطريقة متعددة المهام ويجب أن تختص بمهمة واحدة فقط</p>\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n\n### <p dir=\"rtl\">2</p>\n### **<p dir=\"rtl\">شيفرة أكثر في النماذج، شيفرة أقل في المتحكمات</p>**\n\n<p dir=\"rtl\">ضع كل الشيفرات الخاصة بالتعامل مع قواعد البيانات في فئات خاصة منفصلة ولا تضعها في المتحكمات</p>\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">3</p>\n### **<p dir=\"rtl\">التحقق</p>**\n\n<p dir=\"rtl\">انقل شيفرات التحقق من المتحكمات إلى فئات الطلبات Request classes</p>\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">4</p>\n### **<p dir=\"rtl\">الشيفرات المنطقية يجب أن تكون في فئة خادمة منفصلة</p>**\n\n<p dir=\"rtl\">المتحكم يجب أن يكون له مسئولية واحدة فقط، أنقل الشيفرات المنطقية لفئات خادمة منفصلة</p>\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">5</p>\n### **<p dir=\"rtl\">لا تكرر نفس الشيفرة</p>**\n\n<p dir=\"rtl\">أعد استخدام نفس الشيفرة قدر المستطاع ولا تقم بإعادة كتابتها، سيساعدك هذا على عدم وجود أكثر من شيفرة لتنفيذ نفس المهمة، وإعادة استخدام قوالب blade وفئات التعامل مع قواعد البيانات Eloquent</p>\n\n<p dir=\"rtl\">مثال استخدم scope في فئات التعامل مع قواعد البيانات Eloquent</p>\n \n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">6</p>\n### **<p dir=\"rtl\">يفضل استخدام نظام التعامل مع قواعد البيانات المسمى بـEloquent بدل استخدام باني الإستعلامات Query Builder أو الاستخدام المباشر لأوامر الإستعلامات SQL عبر raw، ويفضل استخدام المجموعات بدل المصفوفات</p>**\n\n<p dir=\"rtl\">Eloquent يجعلك تكتب شيفرة قابلة للقراءة والصيانة. وأيضاً، Eloquent يحتوي على أدوات وخواص داخلية على سبيل الذكر: الحذف الناعم والأحداث والنطاقات إلخ...</p> \n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">7</p>\n### **<p dir=\"rtl\">تقليص المهام</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\n$category->article()->create($request->validated());\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">8</p>\n### **<p dir=\"rtl\">لا تقم بتنفيذ الإستعلامات داخل ملفات blade واستخدم التحميل الحثيث مشكلة (N+1)</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n<p dir=\"rtl\">~لعدد 100 مستخدم سيُنفذ 101 استعلام على قاعدة البيانات</p>\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n<p dir=\"rtl\">~لعدد 100 مستخدم سيُنفذ 2 استعلام فقط على قاعدة البيانات~</p>\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">9</p>\n### **<p dir=\"rtl\">اضف التعليقات للشيفرة، ويفضل استخدام صيغ التعليقات القياسية للمتغيرات والخواص والقيم المعادة إلخ</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n<p dir=\"rtl\">طريقة أفضل:</p>\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\nif ($this->hasJoins())\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">10</p>\n### **<p dir=\"rtl\">لا تضع شيفرات js و css داخل ملفات Blade ولا تضع أي شيفرات HTML في فئات php</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\n<p dir=\"rtl\">✔️ طريقة أفضل:</p>\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nفي ملف جافا سكريبت:\n\n```javascript\nlet article = $('#article').val();\n```\n\n<p dir=\"rtl\">الطريقة الأفضل هي استخدام الحزم الخاصة بنقل البيانات من PHP إلى جافا سكريبت.</p>\n\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">11</p>\n### **<p dir=\"rtl\">استخدم ملفات الإعدادت واللغات، والثوابت بدلاً من النص داخل الشيفرة</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">12</p>\n### **<p dir=\"rtl\">استخدم الأدوات القياسية المعتمدة من مجتمع لارافيل</p>**\n\n<p dir=\"rtl\">يفضل استخدام الأدوات المدمجة مع إطار عمل لارافيل والحزم المقترحة من مجتمع لارفيل بدل استخدام غيرها، أي مطور سيعمل على تطبيقك في وقت لاحق سيحتاج إلى تعلم تلك الأدوات التي لا يشيع استخدامها في تطبيقات لارافيل، وأيضاً أطلب المساعدة من مجتمع لارافيل عندما تقرر الإعتماد على أحد الأدوات أو الحزم، ولا تجعل عميلك يدفع مقابل ذلك. </p>\n\nالوظيفة | الأدوات القياسية  | أدوات الطرف الثالث\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">13</p>\n### **<p dir=\"rtl\">اتبع طريقة لارافيل في التسميات</p>**\n<p dir=\"rtl\">راجع <a href=\"http://www.php-fig.org/psr/psr-2\">PSR standards</a></p>\n \n <p dir=\"rtl\">وأيضا، راجع اصطلاح التسميات المقبول من جهه مجتمع لارافيل:</p>\n\nماذا | كيف | جيدة | سيئة\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">14</p>\n### **<p dir=\"rtl\">استخدم شيفرة أقصر قابلة للقراءة والفهم السريع قدر المستطاع</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\nsession('cart');\n$request->name;\n```\n\n<p dir=\"rtl\">أمثلة أكثر:</p>\n\nجمل مركبة | جمل أقصر وأكثر قابلية للقراءة\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">15</p>\n### **<p dir=\"rtl\">استخدم الحاويات أو الواجهات بدلاً من الفئات الجديدة</p>**\n\n<p dir=\"rtl\">إنشاء فئات جديدة يخلق شيئا من التشويش بين الفئات ويعقد عملة الإختبار، الأفضل الإعتماد على الحاويات أو الواجهات في هذا الأمر</p>\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">16</p>\n### **<p dir=\"rtl\">لا تقم بجلب البيانات من ملف  `.env` مباشرة</p>**\n\n<p dir=\"rtl\">مرر البيانات لملف الإعدادت ومن ثَم استخدم الدالة المساعدة `config()` لاستخدامها في جلب البيانات المخزنة في ملف الإعدادت داخل تطبيقك.</p>\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\n$apiKey = env('API_KEY');\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">17</p>\n### **<p dir=\"rtl\">خزن التواريخ بأشكالها القياسية، واستخدم المسترجعات والمُعدلات لتعديل صيغة التواريخ كما تريد.</p>**\n\n<p dir=\"rtl\">❌ طريقة سيئة:</p>\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\n<p dir=\"rtl\">✔️ طريقة جيدة:</p>\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// ملف العرض\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n### <p dir=\"rtl\">18</p>\n### **<p dir=\"rtl\">ممارسات جيدة أخرى</p>**\n\n<p dir=\"rtl\">لا تضع أي شيفرة برمجية في ملفات الموجهات.</p>\n\n<p dir=\"rtl\">قلل من استخدامك الشيفرات البرمجية المنطقية في ملفات العرض blade.</p>\n\n[<p dir=\"rtl\">🔝 الرجوع للفهرس</p>](#الفهرس)\n"
  },
  {
    "path": "bangla.md",
    "content": "![Laravel best practices](/images/logo-english.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nঅনুবাদঃ\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## সূচীপত্র\n\n[সিঙ্গেল রেস্পন্সিবিলিটি প্রিন্সিপল বা একক দায়িত্ব নীতি](#সিঙ্গেল-রেস্পন্সিবিলিটি-প্রিন্সিপল-বা-একক-দায়িত্ব-নীতি)\n\n[ফ্যাট মডেলস, স্কীনি কন্ট্রোলারস](#ফ্যাট-মডেলস-স্কীনি-কন্ট্রোলারস)\n\n[ভ্যালিডেশন](#ভ্যালিডেশন)\n\n[বিসনেস লজিক সমূহ সার্ভিস ক্লাসে থাকা প্রয়োজন](#বিসনেস-লজিক-সমূহ-সার্ভিস-ক্লাসে-থাকা-প্রয়োজন)\n\n[একি জিনিস বার বার করবেন না। (DRY)](#একি-জিনিস-বার-বার-করবেন-না-dry)\n\n[Query Builder এবং Raw SQL Query লেখার পরিবর্তে Eloquent কে গুরুত্ব দিন। Array থেকে Collection ব্যবহার কে গুরুত্ব দিন](#query-builder-এবং-raw-sql-query-লেখার-পরিবর্তে-eloquent-কে-গুরুত্ব-দিন-array-থেকে-collection-ব্যবহার-কে-গুরুত্ব-দিন)\n\n[সমানে এসাইন করা](#সমানে-এসাইন-করা)\n\n[ব্লেড-টেমপ্লেটে কোয়েরী লিখবেন-না এবং একবারে লোড করুন। (N + 1 সমস্যা)](#ব্লেড-টেমপ্লেটে-কোয়েরী-লিখবেন-না-এবং-একবারে-লোড-করুন-n--1-সমস্যা)\n\n[কোড মন্তব্য লিখতে সমস্যা নাই, কিন্তু মেথডের নামকরণ এবং ভেরিয়েবলের নামকরণ মন্তব্য থেকে বেশি গুরুত্বপুর্ণ](#কোড-মন্তব্য-লিখতে-সমস্যা-নাই-কিন্তু-মেথডের-নামকরণ-এবং-ভেরিয়েবলের-নামকরণ-মন্তব্য-থেকে-বেশি-গুরুত্বপুর্ণ)\n\n[ব্লেড টেমপ্লেটের মধ্যে JS এবং CSS সরাসরি ইঞ্জেক্ট করবেন না এবং PHP Class এ HTML লিখবেন না](#ব্লেড-টেমপ্লেটের-মধ্যে-js-এবং-css-সরাসরি-ইঞ্জেক্ট-করবেন-না-এবং-php-class-এ-html-লিখবেন-না)\n\n[সরাসরি লেখা থেকে কনফিগারেশন, ল্যাঙ্গুয়েজ এবং কনস্টান্ট ফাইল ব্যাবহার করুন](#সরাসরি-লেখা-থেকে-কনফিগারেশন-ল্যাঙ্গুয়েজ-এবং-কনস্টান্ট-ফাইল-ব্যাবহার-করুন)\n\n[কমিউনিটিতে প্রচলিত মানসম্মত লারাভেলের টুলস গুলো ব্যাবহার করুন](#কমিউনিটিতে-প্রচলিত-মানসম্মত-লারাভেলের-টুলস-গুলো-ব্যাবহার-করুন)\n\n[লারাভেল নেমিং কনভেনশন অনুসরণ করুন](#লারাভেল-নেমিং-কনভেনশন-অনুসরণ-করুন)\n\n[যত সম্ভব সংক্ষিপ্ত ও সহজে পড়া যায় এমন সিনট্যাক্স লিখবেন](#যত-সম্ভব-সংক্ষিপ্ত-ও-সহজে-পড়া-যায়-এমন-সিনট্যাক্স-লিখবেন)\n\n[নতুন ক্লাসের পরিবর্তে IoC কন্টেইনার বা facades ব্যবহার করুন](#নতুন-ক্লাসের-পরিবর্তে-ioc-কন্টেইনার-বা-facades-ব্যবহার-করুন)\n\n[`.env` ফাইলের ডাটা সরাসরি নিবেন না](#env-ফাইলের-ডাটা-সরাসরি-নিবেন-না)\n\n[তারিখ গুলো স্ট্যান্ডার্ড ফরম্যাট এ রাখবেন। তারিখের ফরম্যাট পরিবর্তনের জন্য accessors এবং mutators ব্যবহার করুন](#তারিখ-গুলো-স্ট্যান্ডার্ড-ফরম্যাট-এ-রাখবেন-তারিখের-ফরম্যাট-পরিবর্তনের-জন্য-accessors-এবং-mutators-ব্যবহার-করুন)\n\n[অন্যান্য ভাল অনুশীলন](#অন্যান্য-ভাল-অনুশীলন)\n\n### **সিঙ্গেল রেস্পন্সিবিলিটি প্রিন্সিপল বা একক দায়িত্ব নীতি**\n\nএকটা ক্লাস এবং একটা মেথডের একটা করে কাজ/দায়িত্ব হওয়া উচিৎ।\n\nখারাপঃ\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nভালঃ\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **ফ্যাট মডেলস, স্কীনি কন্ট্রোলারস**\n\nসবগুলো ডাটাবেস লজিক Eloquent মডেলে অথবা Repository ক্লাসে থাকা উচিৎ, আপনি যদি Query Builder অথবা raw SQL queries ব্যাবহার করেন।\n\nখারাপঃ\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nভালঃ\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **ভ্যালিডেশন**\n\nভ্যালিডেশন কোড গুলো Controller থেকে Request class এ সরিয়ে ফেলুন।\n\nখারাপঃ\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nভালঃ\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **বিসনেস লজিক সমূহ সার্ভিস ক্লাসে থাকা প্রয়োজন**\n\nএকটা কন্ট্রোলারের একটাই কাজ হওয়া উচিৎ। তাই বিজনেস লজিক গুলো কন্ট্রোলার থেকে সার্ভিস ক্লাসে সরিয়ে ফেলুন।\n\nখারাপঃ\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nভালঃ\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **একি জিনিস বার বার করবেন না। (DRY)**\n\nকোডের পুনঃব্যবহার নিশ্চিত করুন। বার বার লেখার থেকে SRP আপনাকে সাহায্য করবে। সাথে Blade টেম্পলেট, Eloquent স্কোপ ইত্যাদির পুনঃব্যবহার করুন।\n\nখারাপঃ\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nভালঃ\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **Query Builder এবং Raw SQL Query লেখার পরিবর্তে Eloquent কে গুরুত্ব দিন। Array থেকে Collection ব্যবহার কে গুরুত্ব দিন**\n\nEloquent আপনাকে পাঠযোগ্য এবং রক্ষণাবেক্ষণযোগ্য কোড করতে সাহায্য করবে। এছাড়াও, Eloquent এর বেশ কিছু বিল্ট-ইন টুলস আছে যেমনঃ soft deletes, events, scopes ইত্যাদি।\n\nখারাপঃ\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nভালঃ\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **সমানে এসাইন করা**\n\nখারাপঃ\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nভালঃ\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **ব্লেড-টেমপ্লেটে কোয়েরী লিখবেন-না এবং একবারে লোড করুন। (N + 1 সমস্যা)**\n\nখারাপ (১০০ জন ইউজারের জন্য, ১০১ টা DB queries এক্সিকিউট হবে):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nভালো (১০০ জন ইউজারের জন্য, ২ টা DB queries এক্সিকিউট হবে)-ঃ \n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **কোড মন্তব্য লিখতে সমস্যা নাই, কিন্তু মেথডের নামকরণ এবং ভেরিয়েবলের নামকরণ মন্তব্য থেকে বেশি গুরুত্বপুর্ণ**\n\nখারাপঃ\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nতুলনামূলক ভালঃ\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nভালঃ\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **ব্লেড টেমপ্লেটের মধ্যে JS এবং CSS সরাসরি ইঞ্জেক্ট করবেন না এবং PHP Class এ HTML লিখবেন না**\n\nখারাপঃ\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nতুলনামূলক ভালঃ\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nJavascript ফাইলের এর মধ্যেঃ\n\n```javascript\nlet article = $('#article').val();\n```\n\nসবচেয়ে ভালো হয় আলাদা ভাবে PHP থেকে JS প্যাকেজে ডাটা ট্র্যান্সফার করা।\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **সরাসরি লেখা থেকে কনফিগারেশন, ল্যাঙ্গুয়েজ এবং কনস্টান্ট ফাইল ব্যাবহার করুন**\n\nখারাপঃ\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nভালঃ\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **কমিউনিটিতে প্রচলিত মানসম্মত লারাভেলের টুলস গুলো ব্যাবহার করুন**\n\nঅন্যকোন থার্ডপার্টি packages বা tools ব্যাবহার করা থেকে লারাভেলের নিজস্ব ফাংশনালিটি এবং কমিউনিটি প্যাকেজ ব্যাবহার করা ভাল। ভবিষ্যতে কোন ডেভেলপার আপনার প্রজেক্টে কাজ করতে গেলে তাকে টুলস গুলো শিখে নেয়া লাগবে। এছাড়াও, থার্ডপার্টি packages বা tools ব্যাবহার করলে লারাভেল কমিউনিটি থেকে সাপোর্ট পাওয়ার সম্ভাবনা কম। আপনার ক্লায়েন্ট কে সেটার জন্য অর্থ খরচ করাবেন না।\n\nকাজ | স্ট্যান্ডার্ড টুলস | থার্ডপার্টি টুলস\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **লারাভেল নেমিং কনভেনশন অনুসরণ করুন**\n\n[PSR standards](https://www.php-fig.org/psr/psr-12/) অনুসরণ করুন।\n\nএছাড়াও, লারাভেল কমিউনিটি কর্তৃক স্বীকৃত নামকরণের রীতি(নেমিং কনভেনশন) অনুসরণ করা যায়ঃ\n\nকি | কিভাবে | ভাল | খারাপ\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **যত সম্ভব সংক্ষিপ্ত ও সহজে পড়া যায় এমন সিনট্যাক্স লিখবেন**\n\nখারাপঃ\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nভালঃ\n\n```php\nsession('cart');\n$request->name;\n```\n\nআরো উদাহরণঃ\n\nসাধারণ সিনটেক্স | সংক্ষিপ্ত এবং আরো সহজে পাঠযোগ্য সিনটেক্স\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **নতুন ক্লাসের পরিবর্তে IoC কন্টেইনার বা facades ব্যবহার করুন**\n\nনতুন ক্লাসের সিনট্যাক্স ক্লাস গুলোকে টাইট কাপলিং করে এবং টেস্টিং জটিল করে। এর থেকে ভালো IoC container অথবা facades ব্যাবহার করা।\n\nখারাপঃ\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nভালঃ\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **`.env` ফাইলের ডাটা সরাসরি নিবেন না**\n\nবরং ডাটা গুলোকে কনফিগ ফাইলের মধ্যে রাখুন এবং `config()` হেল্পার ফাংশন ব্যাবহার করে আপনার এপ্লিকেশনে ব্যবহার করুন।\n\nখারাপঃ\n\n```php\n$apiKey = env('API_KEY');\n```\n\nভালঃ\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **তারিখ গুলো স্ট্যান্ডার্ড ফরম্যাট এ রাখবেন। তারিখের ফরম্যাট পরিবর্তনের জন্য accessors এবং mutators ব্যবহার করুন**\n\nখারাপঃ\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nভালঃ\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n\n### **অন্যান্য ভাল অনুশীলন**\n\nroutes ফাইলের মধ্যে কখনো লজিক রাখবেন না।\n\nBlade টেম্পলেটের মধ্যে ভ্যানিলা PHP এর ব্যাবহার কমায় ফেলেন।\n\n[🔝 সূচীপত্রে ফিরে যান](#সূচীপত্র)\n"
  },
  {
    "path": "burmese.md",
    "content": "![Laravel best practices](/images/logo-burmese.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app) and [Eloquent SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nTranslations:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[Indonesia](indonesia.md) (by [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[简体中文](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[繁體中文](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Română](romanian.md) (by [als698](https://github.com/als698))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[မြန်မာဘာသာ](burmese.md) (by [Kaung Zay Yan](https://github.com/KaungZayY))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Contents\n\n[အလုပ်တစ်ခုပဲ တာဝန်ယူနိယာမ](#အလုပ်တစ်ခုပဲ-တာဝန်ယူနိယာမ)\n\n[Method တစ်ခုက အလုပ်တစ်ခုပဲလုပ်သင့်ပါတယ်](#Method-တစ်ခုက-အလုပ်တစ်ခုပဲလုပ်သင့်ပါတယ်)\n\n[Fat models, skinny controllers](#fat-models-skinny-controllers)\n\n[Validation](#validation)\n\n[Business logic တွေက Service class ထဲမှာပဲ ရှိသင့်တယ်](#business-logic-တွေက-Service-class-ထဲမှာပဲ-ရှိသင့်တယ်)\n\n[ထပ်တစ်လဲလဲပြန်မရေးနဲ့ (DRY)](#ထပ်တစ်လဲလဲပြန်မရေးနဲ့-DRY)\n\n[Query Builder နဲ့ raw SQL queries အစား Eloquent၊ arrays အစား collection ကိုပိုသုံးပါ](#query-builder-နဲ့-raw-sql-queries-အစား-eloquent-arrays-အစား-collection-ကိုပိုသုံးပါ)\n\n[Mass assignment](#mass-assignment)\n\n[Queries တွေကို Blade Templates တွေထဲမှာ Execute မလုပ်ပဲနဲ့ အဲတာအစား eager loading ကိုအသုံးပြုပါ။ (N + 1 problem)](#queries-တွေကို-blade-templates-တွေထဲမှာ-execute-မလုပ်ပဲနဲ့-အဲတာအစား-eager-loading-ကိုအသုံးပြုပါ-n--1-problem)\n\n[Data-heavy tasks တွေအတွက် Chunk data ကိုသုံးပါ](#data-heavy-tasks-တွေအတွက်-chunk-data-ကိုသုံးပါ)\n\n[Comment ရေးမဲ့ အစား method နဲ့ variable name တွေကို သေချာပေးခဲ့ပါ](#comment-ရေးမဲ့-အစား-method-နဲ့-variable-name-တွေကို-သေချာပေးခဲ့ပါ)\n\n[JS နဲ့ CSS ကို blade templates ထဲကို မထည့်ပါနဲ့၊ PHP class တွေထဲမှာ HTML code တွေမထည့်ပါနဲ့](#js-နဲ့-css-ကို-blade-templates-ထဲကို-မထည့်ပါနဲ့-php-class-တွေထဲမှာ-html-code-တွေမထည့်ပါနဲ့)\n\n[Code ထဲမှာ hard coded စာသားတွေ ထည့်မဲ့အစား config နဲ့ language files တွေကိုသုံးပါ](#code-ထဲမှာ-hard-coded-စာသားတွေ-ထည့်မဲ့အစား-config-နဲ့-language-files-တွေကိုသုံးပါ)\n\n[Community က လက်ခံပြီး အသုံးပြုနေကျ standard laravel tools တွေကိုပဲသုံးပါ](#community-က-လက်ခံပြီး-အသုံးပြုနေကျ-standard-laravel-tools-တွေကိုပဲသုံးပါ)\n\n[Laravel ရဲ့ အမည်ပေးပုံတွေကိုလိုက်နာပါ](#laravel-ရဲ့-အမည်ပေးပုံတွေကိုလိုက်နာပါ)\n\n[Convention over configuration](#convention-over-configuration)\n\n[တိုတိုနဲ့ ဖတ်ရလွယ်တဲ့ syntax ကိုတက်နိုင်သမျှသုံးပါ](#တိုတိုနဲ့-ဖတ်ရလွယ်တဲ့-syntax-ကိုတက်နိုင်သမျှသုံးပါ)\n\n[new Class အစား loC / Service container တွေကိုသုံးပါ](#new-class-အစား-loC--Service-container-တွေကိုသုံးပါ)\n\n[`.env` file ကနေ data ကိုတိုက်ရိုက်မယူပါနဲ့](#env-file-ကနေ-data-ကိုတိုက်ရိုက်မယူပါနဲ့)\n\n[ရက်စွဲတွေကို standard format အတိုင်းသိမ်းပါ။ Date format တွေကို modify လုပ်ချင်ရင် accessors နဲ့ mutators ကိုသုံးပါ](#ရက်စွဲတွေကို-standard-format-အတိုင်းသိမ်းပါ-date-format-တွေကို-modify-လုပ်ချင်ရင်-accessors-နဲ့-mutators-ကိုသုံးပါ)\n\n[DocBlocks တွေကိုမသုံးပါနဲ့](#docblocks-တွေကိုမသုံးပါနဲ့)\n\n[တစ်ခြားအလေ့အကျင့်ကောင်းများ](#တစ်ခြားအလေ့အကျင့်ကောင်းများ)\n\n### **အလုပ်တစ်ခုပဲ တာဝန်ယူနိယာမ**\n\nClass တစ်ခုမှာ တာဝန်တစ်ခုပဲရှိသင့်ပါတယ်။\n\nBad:\n\n```php\npublic function update(Request $request): string\n{\n    $validated = $request->validate([\n        'title' => 'required|max:255',\n        'events' => 'required|array:date,type'\n    ]);\n\n    foreach ($request->events as $event) {\n        $date = $this->carbon->parse($event['date'])->toString();\n\n        $this->logger->log('Update event ' . $date . ' :: ' . $);\n    }\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\nGood:\n\n```php\npublic function update(UpdateRequest $request): string\n{\n    $this->logService->logEvents($request->events);\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Method တစ်ခုက အလုပ်တစ်ခုပဲလုပ်သင့်ပါတယ်**\n\nFunction တစ်ခုက အလုပ်တစ်ခုကိုပဲ သေချာလုပ်သင့်ပါတယ်။\n\nBad:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGood:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Fat models, skinny controllers**\n\nDB logic တွေ အကုန်လုံးကို Eloquent Model ထဲကို ထည့်ပါ။\n\nBad:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGood:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders(): Collection\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Validation**\n\nValidation စစ်တာကို controller ထဲမှာ မစစ်ပဲ request class ထဲမှာစစ်ပါ။\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules(): array\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Business logic တွေက Service class ထဲမှာပဲ ရှိသင့်တယ်**\n\nController တစ်ခုက အလုပ်တစ်ခုပဲ လုပ်သင့်တယ်။ Business‌ logic တွေကို သပ်သပ် service class ထဲကိုရွှေ့ပါ။\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image): void\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **ထပ်တစ်လဲလဲပြန်မရေးနဲ့ (DRY)**\n\nCode ကိုတက်နိုင်သမျှ ထပ်တစ်လဲလဲပြန်မရေးပဲနဲ့ ပြန်သုံးပါ။ Blade templates၊ Eloquent scopes တွေကိုပြန်သုံးပါ။\n\nBad:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGood:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', true)->whereNotNull('deleted_at');\n}\n\npublic function getActive(): Collection\n{\n    return $this->active()->get();\n}\n\npublic function getArticles(): Collection\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Query Builder နဲ့ raw SQL queries အစား Eloquent၊ arrays အစား collection ကိုပိုသုံးပါ**\n\nEloquent က code ကို ဖတ်လို့လွယ် ပြုပြင်ဖို့လွယ်စေတယ်။ နောက်ပြီး eloquent မှာ သုံးလို့‌ကောင်းတဲ့ soft deletes, events, scopes စတဲ့ build-in tools တွေ ပါပါတယ်။ ဒီမှာဝင်ဖတ်ကြည့်လို့ရပါတယ် [Eloquent to SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nBad:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGood:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Mass assignment**\n\nBad:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nGood:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Queries တွေကို Blade Templates တွေထဲမှာ Execute မလုပ်ပဲနဲ့ အဲတာအစား eager loading ကိုအသုံးပြုပါ။ (N + 1 problem)**\n\nBad (user အယောက် ၁၀၀ အတွက် Query ၁၀၁ ခု execute လုပ်ရမယ် ):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nGood (user အယောက် ၁၀၀ အတွက် Query ၂ ခု ပဲ execute ရမယ်):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Data-heavy tasks တွေအတွက် Chunk data ကိုသုံးပါ**\n\nBad:\n\n```php\n$users = $this->get();\n\nforeach ($users as $user) {\n    ...\n}\n```\n\nGood:\n\n```php\n$this->chunk(500, function ($users) {\n    foreach ($users as $user) {\n        ...\n    }\n});\n```\n\n[🔝 Back to contents](#contents)\n\n### **Comment ရေးမဲ့ အစား method နဲ့ variable name တွေကို သေချာပေးခဲ့ပါ**\n\nBad:\n\n```php\n// Determine if there are any joins\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGood:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **JS နဲ့ CSS ကို blade templates ထဲကို မထည့်ပါနဲ့၊ PHP class တွေထဲမှာ HTML code တွေမထည့်ပါနဲ့**\n\nBad:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nBetter:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nIn a Javascript file:\n\n```javascript\nlet article = $('#article').val();\n```\n\nအကောင်းဆုံးကတော့ data transfer ဖို့အတွက် specialized PHP to JS Package တွေကိုသုံးပါ။\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Code ထဲမှာ hard coded စာသားတွေ ထည့်မဲ့အစား config နဲ့ language files တွေကိုသုံးပါ**\n\nBad:\n\n```php\npublic function isNormal(): bool\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nGood:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Community က လက်ခံပြီး အသုံးပြုနေကျ standard laravel tools တွေကိုပဲသုံးပါ**\n\n3rd party packages နဲ့ tools တွေ သုံးမဲ့အစား build-in laravel functionality တွေနဲ့ community packages တွေကိုသာ ပိုသုံးသင့်ပါတယ်။ မဟုတ်ရင် နောက်ပိုင်းမှာ ကိုယ့် project ကို တစ်ခြား developer တွေ ဆက်ပြီး လုပ်တဲ့အခါမှာ tools အသစ်တွေကိုထပ်ပြီး လေ့လာနေရပါလိမ့်မယ်။ ဒါ့အပြင် တစ်ခြား third party package နဲ့ tool သုံးခဲ့ရင် အဲဒီ tools တွေနဲ့ ပက်သတ်ပြီး community ဆီကနေ အကူအညီရနိုင်ခြေလဲ သိသိသာသာလျော့သွားပါလိမ့်မယ်။ ကိုယ့် client ကို အဲတာအတွက် အပိုမကုန်ပါစေနဲ့။\n\nTask | Standard tools | 3rd party tools\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Laravel ရဲ့ အမည်ပေးပုံတွေကိုလိုက်နာပါ**\n\nFollow [PSR standards](https://www.php-fig.org/psr/psr-12/).\n\nနောက်ပြီး laravel community က လက်ခံထားတဲ့ အမည်ပေးပုံတွေကိုလိုက်နာပါ။\n\nWhat | How | Good | Bad\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **Convention over configuration**\n\nတစ်ချို့ naming conventions တွေကိုလိုက်နာနေရင် တစ်ခြား configuration တွေလုပ်စရာမလိုတော့ဘူး။\n\nBad:\n\n```php\n// Table name 'Customer'\n// Primary key 'customer_id'\nclass Customer extends Model\n{\n    const CREATED_AT = 'created_at';\n    const UPDATED_AT = 'updated_at';\n\n    protected $table = 'Customer';\n    protected $primaryKey = 'customer_id';\n\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');\n    }\n}\n```\n\nGood:\n\n```php\n// Table name 'customers'\n// Primary key 'id'\nclass Customer extends Model\n{\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class);\n    }\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **တိုတိုနဲ့ ဖတ်ရလွယ်တဲ့ syntax ကိုတက်နိုင်သမျှသုံးပါ**\n\nBad:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGood:\n\n```php\nsession('cart');\n$request->name;\n```\n\nနောက်ထပ် ဥပမာများ:\n\nတွေ့မြင်နေကျ syntax | တိုတိုနဲ့ ဖတ်ရလွယ် syntax\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id` (in PHP 8: `$object->relation?->id`)\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **new Class အစား loC / Service container တွေကိုသုံးပါ**\n\nnew Class syntax က class တွေအတွင်းမှာ tight coupling ဖြစ်စေတဲ့အပြင် testing လုပ်တဲ့အခါမှာ ပိုပြီး ရှုတ်ထွေးစေတယ်။ အဲ့အစား LoC container နဲ့ facades ကိုသုံးပါ။\n\nBad:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGood:\n\n```php\npublic function __construct(protected User $user) {}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **`.env` file ကနေ data ကိုတိုက်ရိုက်မယူပါနဲ့**\n\nအဲလိုလုပ်မဲ့အစား application မှာသုံးရမဲ့ data ကို config files တွေဆီပို့ပြီးတော့ `config()` helper function ကိုသုံးပါ။\n\nBad:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGood:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **ရက်စွဲတွေကို standard format အတိုင်းသိမ်းပါ။ Date format တွေကို modify လုပ်ချင်ရင် accessors နဲ့ mutators ကိုသုံးပါ**\n\nရက်စွဲတစ်ခုကို စာသား(string) အနေနဲ့သိမ်းတာက object instance အနေနဲ့သိမ်းတာလောက် စိတ်မချရဘူး ၊ ဥပမာ Carbon-instance။ Class အချင်းချင်းကြား date string အနေနဲ့ ပေးတာထက် carbon objects အနေနဲ့‌ပေးတာကို ပိုအားပေးပါတယ်။ ဒေတာပြန်ပြတာကိုတော့ display layer(templates) မှာပဲ လုပ်သင့်ပါတယ်။\n\nBad:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGood:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\n// Blade view\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->format('m-d') }}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **DocBlocks တွေကိုမသုံးပါနဲ့**\n\nDocBlocks တွေက ဖတ်ရတာ ပိုခက်စေတယ်။ အဲ့အစား method name ကိုသေချာပေးတာ နဲ့ အသစ်ထွက် PHP feautre တွေဖြစ်တဲ့ return type hints တွေကိုသုံးပါ။\n\nBad:\n\n```php\n/**\n * The function checks if given string is a valid ASCII string\n *\n * @param string $string String we get from frontend which might contain\n *                       illegal characters. Returns True is the string\n *                       is valid.\n *\n * @return bool\n * @author  John Smith\n *\n * @license GPL\n */\n\npublic function checkString($string)\n{\n}\n```\n\nGood:\n\n```php\npublic function isValidAsciiString(string $string): bool\n{\n}\n```\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n\n### **တစ်ခြားအလေ့အကျင့်ကောင်းများ**\n\nLaravel၊ တစ်ခြား ဆင်တူတဲ့(RoR၊ Django)အစရှိတဲ့ frameworks တွေနဲ့စိမ်းတဲ့ patterns တွေ tools တွေကိုမသုံးပါနဲ့။ App တစ်ခုဆောက်ဖို့ကို Symfony (ဒါမှမဟုတ် Spring) ရဲ့ approach ကို သုံးရတာကြိုက်ရင် အဲဒီ framework ကိုပဲသုံးလိုက်သင့်ပါတယ်။ \n\nRoute file တွေမှာ ဘာlogic မှမထည့်ပါနဲ့။\n\nVanilla PHP ကို Blade templates တွေမှာ နည်းနိုင်သမျှ နည်းသုံးပါ။\n\nTesting အတွက် in-memory DB ကိုသုံးပါ။\n\nFramework version update လုပ်တာ နဲ့ တစ်ခြား issues တွေမတက်‌အောင် framework ရဲ့ standard features တွေကိုပြင်မရေးပါနဲ့။\n\nနောက်ထွက် PHP syntax တွေကိုတက်နိုင်သမျှ အသုံးပြုပါ ဒါပေမယ့် ဖတ်ရလွယ်အောင်ရေးဖို့လဲ မမေ့ပါနဲ့။\n\nတကယ်သေချာမသိရင် View Composers နဲ့ တစ်ခြားဆင်တူတဲ့ tools တွေကိုမသုံးပါနဲ့။ များသောအားဖြင့် ပြဿနာကို ဖြေရှင်းဖို့ ပိုကောင်းတဲ့ နည်းတွေရှိပါတယ်။\n\n[🔝Contents တွေဆီပြန်သွားမယ်](#contents)\n"
  },
  {
    "path": "chinese.md",
    "content": "![Laravel best practices](/images/logo-chinese.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n多国语言列表:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## 内容\n\n[单一职责原则](#单一职责原则)\n\n[保持控制器的简洁](#保持控制器的简洁)\n\n[使用自定义Request类来进行验证](#使用自定义Request类来进行验证)\n\n[业务代码要放到服务层中](#业务代码要放到服务层中)\n\n[DRY原则 不要重复自己](#DRY原则-不要重复自己)\n\n[使用ORM而不是纯sql语句，使用集合而不是数组](#使用ORM而不是纯sql语句使用集合而不是数组)\n\n[集中处理数据](#集中处理数据)\n\n[不要在模板中查询，尽量使用惰性加载](#不要在模板中查询尽量使用惰性加载)\n\n[注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码](#注释你的代码但是更优雅的做法是使用描述性的语言来编写你的代码)\n\n[不要把 JS 和 CSS 放到 Blade 模板中，也不要把任何 HTML 代码放到 PHP 代码里](#不要把-JS-和-CSS-放到-Blade-模板中也不要把任何-HTML-代码放到-PHP-代码里)\n\n[在代码中使用配置、语言包和常量，而不是使用硬编码](#在代码中使用配置语言包和常量而不是使用硬编码)\n\n[使用社区认可的标准Laravel工具](#使用社区认可的标准Laravel工具)\n\n[遵循laravel命名约定](#遵循laravel命名约定)\n\n[尽可能使用简短且可读性更好的语法](#尽可能使用简短且可读性更好的语法)\n\n[使用IOC容器来创建实例 而不是直接new一个实例](#使用IOC容器来创建实例-而不是直接new一个实例)\n\n[避免直接从 `.env` 文件里获取数据](#避免直接从-env-文件里获取数据)\n\n[使用标准格式来存储日期，用访问器和修改器来修改日期格式](#使用标准格式来存储日期用访问器和修改器来修改日期格式)\n\n[其他的好建议](#其他的一些好建议)\n\n### **单一职责原则**\n\n一个类和一个方法应该只有一个责任。\n\n例如:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\n更优的写法:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 返回目录](#内容)\n\n### **保持控制器的简洁**\n\n如果您使用的是查询生成器或原始SQL查询，请将所有与数据库相关的逻辑放入Eloquent模型或Repository类中。\n\n例如:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\n更优的写法:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 返回目录](#内容)\n\n### **使用自定义Request类来进行验证**\n\n把验证规则放到 Request 类中.\n\n例子:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\n更优的写法:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 返回目录](#内容)\n\n### **业务代码要放到服务层中**\n\n控制器必须遵循单一职责原则，因此最好将业务代码从控制器移动到服务层中。\n\n例子:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\n更优的写法:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 返回目录](#内容)\n\n### **DRY原则 不要重复自己**\n\n尽可能重用代码，SRP可以帮助您避免重复造轮子。 此外尽量重复使用Blade模板，使用Eloquent的 scopes 方法来实现代码。\n\n例子:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\n更优的写法:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 返回目录](#内容)\n\n### **使用ORM而不是纯sql语句，使用集合而不是数组**\n\n使用Eloquent可以帮您编写可读和可维护的代码。 此外Eloquent还有非常优雅的内置工具，如软删除，事件，范围等。\n\n例子:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\n更优的写法:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 返回目录](#内容)\n\n### **集中处理数据**\n\n例子:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\n更优的写法:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 返回目录](#内容)\n\n### **不要在模板中查询，尽量使用惰性加载**\n\n例子 (对于100个用户，将执行101次DB查询):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n更优的写法 (对于100个用户，使用以下写法只需执行2次DB查询):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 返回目录](#内容)\n\n### **注释你的代码，但是更优雅的做法是使用描述性的语言来编写你的代码**\n\n例子:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n加上注释:\n\n```php\n// 确定是否有任何连接\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n更优的写法:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 返回目录](#内容)\n\n### **不要把 JS 和 CSS 放到 Blade 模板中，也不要把任何 HTML 代码放到 PHP 代码里**\n\n例子:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\n更好的写法:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\n在Javascript文件中加上:\n\n```javascript\nlet article = $('#article').val();\n```\n\n当然最好的办法还是使用专业的PHP的JS包传输数据。\n\n[🔝 返回目录](#内容)\n\n### **在代码中使用配置、语言包和常量，而不是使用硬编码**\n\n例子:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\n更优的写法:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 返回目录](#内容)\n\n### **使用社区认可的标准Laravel工具**\n\n\n强力推荐使用内置的Laravel功能和扩展包，而不是使用第三方的扩展包和工具。\n如果你的项目被其他开发人员接手了，他们将不得不重新学习这些第三方工具的使用教程。\n此外，当您使用第三方扩展包或工具时，你很难从Laravel社区获得什么帮助。 不要让你的客户为额外的问题付钱。\n\n想要实现的功能 | 标准工具 | 第三方工具\n------------ | ------------- | -------------\n权限 | Policies | Entrust, Sentinel 或者其他扩展包\n资源编译工具| Laravel Mix, Vite | Grunt, Gulp, 或者其他第三方包\n开发环境| Laravel Sail, Homestead | Docker\n部署 | Laravel Forge | Deployer 或者其他解决方案\n自动化测试 | PHPUnit, Mockery | Phpspec, Pest\n页面预览测试 | Laravel Dusk | Codeception\nDB操纵 | Eloquent | SQL, Doctrine\n模板 | Blade | Twig\n数据操纵 | Laravel集合 | 数组\n表单验证| Request classes | 他第三方包,甚至在控制器中做验证\n权限 | Built-in | 他第三方包或者你自己解决\nAPI身份验证 | Laravel Passport, Laravel Sanctum | 第三方的JWT或者 OAuth 扩展包\n创建 API | Built-in | Dingo API 或者类似的扩展包\n创建数据库结构 | Migrations | 直接用 DB 语句创建\n本土化 | Built-in |第三方包\n实时消息队列 | Laravel Echo, Pusher | 使用第三方包或者直接使用WebSockets\n创建测试数据| Seeder classes, Model Factories, Faker | 手动创建测试数据\n任务调度| Laravel Task Scheduler | 脚本和第三方包\n数据库 | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 返回目录](#内容)\n\n### **遵循laravel命名约定**\n\n来源 [PSR standards](https://www.php-fig.org/psr/psr-12/).\n \n另外，遵循Laravel社区认可的命名约定：\n\n对象 | 规则 | 更优的写法 | 应避免的写法\n------------ | ------------- | ------------- | -------------\n控制器 | 单数 | ArticleController | ~~ArticlesController~~\n路由 | 复数 | articles/1 | ~~article/1~~\n路由命名| 带点符号的蛇形命名 | users.show_active | ~~users.show-active, show-active-users~~\n模型 | 单数 | User | ~~Users~~\nhasOne或belongsTo关系 | 单数 | articleComment | ~~articleComments, article_comment~~\n所有其他关系 | 复数 | articleComments | ~~articleComment, article_comments~~\n表单 | 复数 | article_comments | ~~article_comment, articleComments~~\n透视表| 按字母顺序排列模型 | article_user | ~~user_article, articles_users~~\n数据表字段| 使用蛇形并且不要带表名 | meta_title | ~~MetaTitle; article_meta_title~~\n模型参数 | 蛇形命名 | $model->created_at | ~~$model->createdAt~~\n外键 | 带有_id后缀的单数模型名称 | article_id | ~~ArticleId, id_article, articles_id~~\n主键 | - | id | ~~custom_id~~\n迁移 | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\n方法 | 驼峰命名 | getAll | ~~get_all~~\n资源控制器 | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\n测试类| 驼峰命名 | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\n变量 | 驼峰命名 | $articlesWithAuthor | ~~$articles_with_author~~\n集合 | 描述性的, 复数的 | $activeUsers = User::active()->get() | ~~$active, $data~~\n对象 | 描述性的, 单数的 | $activeUser = User::active()->first() | ~~$users, $obj~~\n配置和语言文件索引 | 蛇形命名 | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\n视图 | 短横线命名 | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\n配置 | 蛇形命名 | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\n内容 (interface) | 形容词或名词 | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | 使用形容词 | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 返回目录](#内容)\n\n### **尽可能使用简短且可读性更好的语法**\n\n例子:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\n更优的写法:\n\n```php\nsession('cart');\n$request->name;\n```\n\n更多示例:\n\n常规写法 | 更优雅的写法\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 返回目录](#内容)\n\n### **使用IOC容器来创建实例 而不是直接new一个实例**\n\n创建新的类会让类之间的更加耦合，使得测试越发复杂。请改用IoC容器或注入来实现。\n\n例子:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\n更优的写法:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 返回目录](#内容)\n\n### **避免直接从 `.env` 文件里获取数据**\n\n将数据传递给配置文件，然后使用`config（）`帮助函数来调用数据\n\n例子:\n\n```php\n$apiKey = env('API_KEY');\n```\n\n更优的写法:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 返回目录](#内容)\n\n### **使用标准格式来存储日期，用访问器和修改器来修改日期格式**\n\n例子:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\n更优的写法:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 返回目录](#内容)\n\n### **其他的一些好建议**\n\n永远不要在路由文件中放任何的逻辑代码。\n\n尽量不要在Blade模板中写原始 PHP 代码。\n\n[🔝 返回目录](#内容)\n\n\n"
  },
  {
    "path": "french.md",
    "content": "![Laravel best practices](/images/logo-french.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nTraductions:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Contenu\n\n[Principe de responsabilité unique](#principe-de-responsabilité-unique)\n\n[Gros Modèles, contrôleurs maigres](#gros-modèles-contrôleurs-maigres)\n\n[Validation](#validation)\n\n[La logique métier doit être dans une classe de service](#la-logique-métier-doit-être-dans-une-classe-de-service)\n\n[Ne te répète pas (DRY)](#ne-te-répète-pas-dry)\n\n[Préférez utiliser Eloquent à l’utilisation de Query Builder et de requêtes SQL brutes. Préférez les collections aux tableaux](#préférez-utiliser-eloquent-à-lutilisation-de-query-builder-et-de-requêtes-sql-brutes-préférez-les-collections-aux-tableaux)\n\n[Affectation en masse](#affectation-en-masse)\n\n[N'exécutez pas de requêtes dans les modèles de blade et utilisez un chargement rapide (N + 1 problème)](#nexécutez-pas-de-requêtes-dans-les-modèles-blade-et-utilisez-un-chargement-rapide-eager-loading-problème-n--1)\n\n[Commentez votre code, mais préférez une méthode descriptive et les noms de variables aux commentaires](#commentez-votre-code-mais-préférez-une-méthode-descriptive-et-des-noms-de-variables-aux-commentaires)\n\n[Ne mettez pas JS et CSS dans les templates Blade et ne mettez pas de HTML dans les classes PHP](#ne-mettez-pas-js-et-css-dans-les-templates-blade-et-ne-mettez-pas-de-html-dans-les-classes-php)\n\n[Utilisez des fichiers de configuration et de langue, des constantes au lieu du texte dans le code](#utilisez-des-fichiers-de-configuration-et-de-langue-des-constantes-au-lieu-du-texte-dans-le-code)\n\n[Utiliser les outils standard de Laravel acceptés par la communauté](#utiliser-les-outils-standard-de-laravel-acceptés-par-la-communauté)\n\n[Suivre les conventions de nommage de Laravel](#suivre-les-conventions-de-nommage-de-laravel)\n\n[Utilisez une syntaxe plus courte et plus lisible dans la mesure du possible](#utilisez-une-syntaxe-plus-courte-et-plus-lisible-dans-la-mesure-du-possible)\n\n[Utilisez un conteneur IoC ou des façades au lieu de la nouvelle classe](#utilisez-un-conteneur-ioc-ou-des-façades-au-lieu-de-la-nouvelle-classe)\n\n[Ne pas obtenir directement les données du fichier `.env` directement](#ne-pas-obtenir-directement-les-données-du-fichier-env)\n\n[Stocker les dates au format standard. Utiliser des accesseurs et des mutateurs pour modifier le format de date](#stocker-les-dates-au-format-standard-Utiliser-des-accesseurs-et-des-mutateurs-pour-modifier-le-format-de-date)\n\n[D'autres bonnes pratiques](#dautres-bonnes-pratiques)\n\n### **Principe de responsabilité unique**\n\nUne classe et une méthode ne devraient avoir qu'une seule responsabilité.\n\nMal:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nBien:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Gros modèles, contrôleurs maigres**\n\nPlacez toute la logique liée à la base de données dans les modèles Eloquent ou dans les classes du Repository si vous utilisez le générateur de requêtes ou des requêtes SQL brutes.\n\nMal:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nBien:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Validation**\n\nDéplacez la validation des contrôleurs vers les classes Request.\n\nMal:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nBien:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **La logique métier doit être dans une classe de service**\n\nUn contrôleur ne doit avoir qu'une seule responsabilité. Par conséquent, déplacez la logique métier des contrôleurs vers les classes de service.\n\nMal:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nBien:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Ne te répète pas (DRY)**\n\nRéutilisez le code quand vous le pouvez. SRP vous aide à éviter les doubles emplois. Réutilisez également les modèles Blade, utilisez les Eloquent scopes, etc.\n\nMal:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nBien:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Préférez utiliser Eloquent à l’utilisation de Query Builder et de requêtes SQL brutes. Préférez les collections aux tableaux**\n\nEloquent vous permet d’écrire du code lisible et maintenable. Eloquent dispose également d'excellents outils intégrés tels que les suppressions, les événements, les scopes, etc.\n\nMal:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nBien:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Affectation en masse**\n\nMal:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nBien:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **N'exécutez pas de requêtes dans les modèles Blade et utilisez un chargement rapide (eager loading) (problème N + 1)**\n\nMal (Pour 100 utilisateurs, 101 requêtes DB seront exécutées):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nBien (pour 100 utilisateurs, 2 requêtes de base de données seront exécutées):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Commentez votre code, mais préférez une méthode descriptive et des noms de variables aux commentaires**\n\nMal:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nMeilleure:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nBien:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Ne mettez pas JS et CSS dans les templates Blade et ne mettez pas de HTML dans les classes PHP**\n\nMal:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nMeilleure:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nDans un fichier Javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nLe meilleur moyen consiste à utiliser un package PHP vers JS spécialisé pour transférer les données.\n\n[🔝 Retour au contenu](#contents)\n\n### **Utilisez des fichiers de configuration et de langue, des constantes au lieu du texte dans le code**\n\nMal:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nBien:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Utiliser les outils standard de Laravel acceptés par la communauté**\n\nPréférez utiliser les fonctionnalités intégrées de Laravel et les packages de communauté au lieu d'utiliser des packages et des outils tiers. Tout développeur qui travaillera avec votre application à l'avenir devra apprendre de nouveaux outils. En outre, les chances d'obtenir de l'aide de la communauté Laravel sont considérablement réduites lorsque vous utilisez un package ou un outil tiers. Ne faites pas payer votre client pour cela.\n\nTâche | Outils standard | Outils tiers\n------------ | ------------- | -------------\nAutorisation | Policies | Entrust, Sentinel et d'autres packages\nCompiler des assets | Laravel Mix, Vite | Grunt, Gulp, packages tiers\nEnvironnement de développement | Laravel Sail, Homestead | Docker\nDéploiement | Laravel Forge | Deployer et d'autre solutions\nTests unitaires | PHPUnit, Mockery | Phpspec, Pest\nTest du navigateur | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nTravailler avec des données | Laravel collections | Arrays\nValidation du formulaire | Request classes | 3rd party packages, validation dans le contrôleur\nAuthentification | Built-in | 3rd party packages, votre propre solution\nAPI D'authentification | Laravel Passport, Laravel Sanctum | 3rd party JWT et OAuth packages\nCréation d'API | Built-in | Dingo API and similar packages\nTravailler avec une structure de base de données | Migrations | Travailler directement avec la structure de la base de données\nLocalisation | Built-in | 3rd party packages\nInterfaces utilisateur en temps réel | Laravel Echo, Pusher | Packages tiers et utilisation directe de WebSockets\nGénérer des données de test | Seeder classes, Model Factories, Faker | Création manuelle de données de test\nPlanification des tâches | Laravel Task Scheduler | Scripts et packages tiers\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Retour au contenu](#contents)\n\n### **Suivre les conventions de nommage de Laravel**\n\nSuivre [Normes PSR](https://www.php-fig.org/psr/psr-12/).\n\nSuivez également les conventions de nommage acceptées par la communauté Laravel:\n\nQuoi | Comment | Bien | Mal\n------------ | ------------- | ------------- | -------------\nController | singulier | ArticleController | ~~ArticlesController~~\nRoute | pluriel | articles/1 | ~~article/1~~\nRoute nommée | snake_case avec notation par points | users.show_active | ~~users.show-active, show-active-users~~\nModel | singulier | User | ~~Users~~\nRelations hasOne or belongsTo | singulier | articleComment | ~~articleComments, article_comment~~\nToutes les autres relations | pluriel | articleComments | ~~articleComment, article_comments~~\nTable | plurielle | article_comments | ~~article_comment, articleComments~~\nTable pivot | noms des modèles au singulier dans l'ordre alphabétique | article_user | ~~user_article, articles_users~~\nColonne de table | snake_case sans nom de modèle | meta_title | ~~MetaTitle; article_meta_title~~\nAttribut du Model | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | Nom du modèle au singulier avec _id comme suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMéthode | camelCase | getAll | ~~get_all~~\nMéthodes dans le controlleur de ressource | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMéthode dans une classe de test | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptif, pluriel | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptif, singulier | $activeUser = User::active()->first() | ~~$users, $obj~~\nIndex de fichier de config et de langage | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nVue | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjectif ou nom | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjectif | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Retour au contenu](#contents)\n\n### **Utilisez une syntaxe plus courte et plus lisible dans la mesure du possible**\n\nMal:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nBien:\n\n```php\nsession('cart');\n$request->name;\n```\n\nPlus d'exemples:\n\nSyntaxe commune | Syntaxe plus courte et plus lisible\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Retour au contenu](#contents)\n\n### **Utilisez un conteneur IoC ou des façades au lieu de la nouvelle classe**\n\nLa nouvelle syntaxe de classe crée un couplage étroit entre les classes et complique les tests. Utilisez plutôt le conteneur IoC ou les façades.\n\nMal:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nBien:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Ne pas obtenir directement les données du fichier `.env`**\n\nPassez les données aux fichiers de configuration à la place, puis utilisez la fonction d'assistance `config ()` pour utiliser les données dans une application.\n\nMal:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nBien:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **Stocker les dates au format standard. Utiliser des accesseurs et des mutateurs pour modifier le format de date**\n\nMal:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nBien:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Retour au contenu](#contents)\n\n### **D'autres bonnes pratiques**\n\nNe mettez jamais aucune logique dans les fichiers de routes.\n\nMinimisez l'utilisation de PHP vanilla dans les modèles de blade.\n\n[🔝 Retour au contenu](#contents)\n"
  },
  {
    "path": "german.md",
    "content": "![Laravel best practices](/images/logo-german.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nTranslations:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[中文維基百科](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Inhaltsverzeichnis\n\n[Single-Responsibility-Prinzip](#single-responsibility-prinzip)\n\n[Fette Models, schlanke Controller](#fette-models-schlanke-controller)\n\n[Validierung](#validierung)\n\n[Geschäftslogik sollte in einer Serviceklasse sein](#geschäftslogik-sollte-in-einer-serviceklasse-sein)\n\n[Wiederholen Sie sich nicht / Don't repeat yourself (DRY)](#wiederholen-sie-sich-nicht--dont-repeat-yourself-dry)\n\n[Verwenden Sie Eloquent anstelle des Query Builders und rohen SQL-Abfragen. Bevorzugen Sie Collections gegenüber Arrays](#verwenden-sie-lieber-eloquent-anstelle-des-query-builders-und-rohen-sql-abfragen-bevorzugen-sie-collections-gegenüber-arrays)\n\n[Massenzuordnung](#massenzuordnung)\n\n[Führen Sie keine Abfragen in Blade-Templates aus und verwenden Sie eager loading (N + 1-Problem)](#führen-sie-keine-abfragen-in-blade-templates-aus-und-verwenden-sie-eager-loading-n--1-problem)\n\n[Kommentieren Sie Ihren Code, aber bevorzugen Sie beschreibende Methoden- und Variablennamen gegenüber Kommentaren](#kommentieren-sie-ihren-code-aber-bevorzugen-sie-beschreibende-methoden--und-variablennamen-gegenüber-kommentaren)\n\n[Schreiben Sie kein JS und CSS in Blade-Templates und schreiben Sie kein HTML in PHP-Klassen](#schreiben-sie-kein-js-und-css-in-blade-templates-und-schreiben-sie-kein-html-in-php-klassen)\n\n[Verwenden Sie Konfigurations- und Sprachdateien und Konstanten anstelle von Text im Code](#verwenden-sie-konfigurations--und-sprachdateien-und-konstanten-anstelle-von-text-im-code)\n\n[Verwenden Sie Standard-Laravel-Tools, die von der Community akzeptiert werden](#verwenden-sie-standard-laravel-tools-die-von-der-community-akzeptiert-werden)\n\n[Befolgen Sie die Namenskonventionen von Laravel](#befolgen-sie-die-namenskonventionen-von-laravel)\n\n[Verwenden Sie nach Möglichkeit eine kürzere und besser lesbare Syntax](#verwenden-sie-nach-möglichkeit-eine-kürzere-und-besser-lesbare-syntax)\n\n[Verwenden Sie IoC-Container oder Facades, statt neue Klassen zu instanziieren](#verwenden-sie-ioc-container-oder-facades-statt-neue-klassen-zu-instanziieren)\n\n[Rufen Sie Daten nicht direkt aus der `.env`-Datei ab](#rufen-sie-daten-nicht-direkt-aus-der-env-datei-ab)\n\n[Speichern Sie Datumsangaben im Standardformat. Verwenden Sie Accessoren und Mutatoren, um das Datumsformat zu ändern](#speichern-sie-datumsangaben-im-standardformat-verwenden-sie-accessoren-und-mutatoren-um-das-datumsformat-zu-ändern)\n\n[Andere gute Praktiken](#andere-gute-praktiken)\n\n### **Single-Responsibility-Prinzip**\n\nEine Klasse und eine Methode sollten nur eine Verantwortung haben.\n\nSchlecht:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGut:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Fette Models, schlanke Controller**\n\nFügen Sie die gesamte DB-bezogene Logik in Eloquent-Models oder in Repository-Klassen ein, wenn Sie Query Builder oder SQL-Rohabfragen verwenden.\n\nSchlecht:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGut:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Validierung**\n\nVerschieben Sie die Validierung von Controllern in Request-Klassen.\n\nSchlecht:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGut:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Geschäftslogik sollte in einer Serviceklasse sein**\n\nEin Controller darf nur eine Verantwortung haben, also verschieben Sie die Geschäftslogik von Controllern in Serviceklassen.\n\nSchlecht:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGut:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Wiederholen Sie sich nicht / Don't repeat yourself (DRY)**\n\nVerwenden 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.\n\nSchlecht:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGut:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Verwenden Sie lieber Eloquent anstelle des Query Builders und rohen SQL-Abfragen. Bevorzugen Sie Collections gegenüber Arrays**\n\nEloquent ermöglicht es, lesbaren und wartbaren Code schreiben. Außerdem verfügt Eloquent über großartige integrierte Tools wie Soft Deletes, Events, Scopes usw.\n\nSchlecht:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGut:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Massenzuordnung**\n\nSchlecht:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nGut:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Führen Sie keine Abfragen in Blade-Templates aus und verwenden Sie eager loading (N + 1-Problem)**\n\nSchlecht (für 100 Benutzer werden 101 Datenbankabfragen ausgeführt):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nGut (für 100 Benutzer werden 2 Datenbankabfragen ausgeführt):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Kommentieren Sie Ihren Code, aber bevorzugen Sie beschreibende Methoden- und Variablennamen gegenüber Kommentaren**\n\nSchlecht:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nBesser:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGut:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Schreiben Sie kein JS und CSS in Blade-Templates und schreiben Sie kein HTML in PHP-Klassen**\n\nSchlecht:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nBesser:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\noder\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nIn einer JavaScript-Datei:\n\n```javascript\nlet article = $('#article').val();\n```\n\nAm besten verwenden Sie ein spezielles PHP-zu-JS-Paket, um die Daten zu übertragen.\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Verwenden Sie Konfigurations- und Sprachdateien und Konstanten anstelle von Text im Code**\n\nSchlecht:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nGut:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Verwenden Sie Standard-Laravel-Tools, die von der Community akzeptiert werden**\n\nVerwenden 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.\n\nAufgabe | Standardwerkzeuge | Tools von Drittanbietern\n------------ | ------------- | -------------\nAutorisierung | Policies | Entrust, Sentinel und andere Pakete\nAssets kompilieren | Laravel Mix, Vite | Grunt, Gulp, 3rd-Party-Pakete\nEntwicklungsumgebung | Laravel Sail, Homestead | Docker\nBereitstellung | Laravel Forge | Deployer und andere Lösungen\nUnit Tests | PHPUnit, Mockery | Phpspec, Pest\nBrowsertests | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nMit Daten arbeiten | Laravel Collections | Arrays\nFormularvalidierung | Request-Klassen | Pakete von Drittanbietern, Validierung im Controller\nAuthentifizierung | Integriert | Pakete von Drittanbietern, Ihre eigene Lösung\nAPI-Authentifizierung | Laravel Passport, Laravel Sanctum | JWT- und OAuth-Pakete von Drittanbietern\nAPI erstellen | Integriert | Dingo API und ähnliche Pakete\nMit DB-Struktur arbeiten | Migrationen | Direkt mit der DB-Struktur arbeiten\nLokalisierung | Integriert | Pakete von Drittanbietern\nEchtzeit-Benutzeroberflächen | Laravel Echo, Pusher | Pakete von Drittanbietern und direktes Arbeiten mit WebSockets\nTestdaten generieren | Seeder-Klassen, Model Factories, Faker | Testdaten manuell erstellen\nAufgabenplanung | Laravel Task Scheduler | Skripte und Pakete von Drittanbietern\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Befolgen Sie die Namenskonventionen von Laravel**\n\nFolgen Sie den [PSR standards](https://www.php-fig.org/psr/psr-12/).\n\nBefolgen Sie außerdem die von der Laravel-Community akzeptierten Namenskonventionen:\n\nWas | Wie | Gut | Schlecht\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nBenannte Route | snake_case mit Punktnotation | users.show_active |~~users.show-active, show-active-users~~\nModel | singular | User |~~Users~~\nhasOne oder belongsTo Beziehung | singular | articleComment | ~~articleComments, article_comment~~\nAlle anderen Beziehungen | plural | articleComments | ~~articleComment, article_comments~~\nTabelle | Plural | article_comments |~~article_comment, articleComments~~\nPivot-Tabelle | singuläre Modellnamen in alphabetischer Reihenfolge | article_user | ~~user_article, articles_users~~\nTabellenspalte | snake_case ohne Modellname | meta_title | ~~MetaTitle; article_title_title~~\nModel-Eigenschaft | snake_case | $model->created_at | ~~$model->createdAt~~\nFremdschlüssel | singulärer Modellname mit Suffix _id | article_id | ~~ArticleId, id_article, articles_id~~\nPrimärschlüssel | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethode | camelCase | getAll | ~~get_all~~\nMethode im Ressourcencontroller | [Tabelle](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethode in einer Testklasse | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | beschreibend, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObjekt | beschreibend, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nKonfigurations- und Sprachdateien index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nVertrag (Interface) | Adjektiv oder Substantiv | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | Adjektiv | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Verwenden Sie nach Möglichkeit eine kürzere und besser lesbare Syntax**\n\nSchlecht:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGut:\n\n```php\nsession('cart');\n$request->name;\n```\n\nMehr Beispiele:\n\nGängige Syntax | Kürzere und lesbarere Syntax\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Verwenden Sie IoC-Container oder Facades, statt neue Klassen zu instanziieren**\n\nDie Syntax `new Class` erzeugt eine enge Kopplung zwischen Klassen und erschwert dadurch das Testen. Verwenden Sie stattdessen IoC-Container oder Facades.\n\nSchlecht:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGut:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Rufen Sie Daten nicht direkt aus der `.env`-Datei ab**\n\nÜbergeben Sie die Daten stattdessen an eine Konfigurationsdatei und verwenden Sie dann die Hilfsfunktion `config()`, um die Daten in ihrer Anwendung zu verwenden.\n\nSchlecht:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGut:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Verwendung der Daten\n$apiKey = config('api.key');\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Speichern Sie Datumsangaben im Standardformat. Verwenden Sie Accessoren und Mutatoren, um das Datumsformat zu ändern**\n\nStrings 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:\n\nSchlecht:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGut:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\n// Blade view\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->format('m-d') }}\n```\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n\n### **Andere gute Praktiken**\n\nLogik sollte nicht in Routes Dateien eingebaut werden.\n\nMinimieren Sie die Verwendung von Vanilla PHP in Blade-Templates.\n\n[🔝 Zurück zum Inhaltsverzeichnis](#inhaltsverzeichnis)\n"
  },
  {
    "path": "indonesia.md",
    "content": "\n![Laravel best practices](/images/logo-english.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Konten\n\n[Prinsip *single responsibility*](#prinsip-single-responsibility)\n\n[Model tebal, *controller* tipis](#model-tebal-controller-tipis)\n\n[Validasi](#validasi)\n\n[*Business logic* harus di dalam kelas *services*](#business-logic-harus-di-dalam-kelas-services)\n\n[*Don't repeat yourself* (DRY)](#dont-repeat-yourself-dry)\n\n[Lebih memilih menggunakan *Eloquent* daripada menggunakan *Query Builder* dan query SQL mentah. Lebih memilih *collections* daripada *array*](#lebih-memilih-menggunakan-eloquent-daripada-menggunakan-query-builder-dan-query-sql-mentah-lebih-memilih-collections-daripada-array)\n\n[*Mass assignment*](#mass-assignment)\n\n[Jangan mengeksekusi kueri dalam *template blade* dan gunakan *eager loading* (masalah N + 1)](#jangan-mengeksekusi-kueri-dalam-template-blade-dan-gunakan-eager-loading-masalah-n-1)\n\n[Komentari kode anda, tetapi lebih baik *method* dan nama variabel yang deskriptif daripada komentar](#komentari-kode-anda-tetapi-lebih-baik-method-dan-nama-variabel-yang-deskriptif-daripada-komentar)\n\n[Jangan letakkan JS dan CSS di *template blade* dan jangan letakkan HTML apa pun di kelas PHP](#jangan-letakkan-js-dan-css-di-template-blade-dan-jangan-letakkan-html-apa-pun-di-kelas-php)\n\n[Gunakan file *config*, *language*, dan konstanta daripada teks dalam kode](#gunakan-file-config-language-dan-konstanta-daripada-teks-dalam-kode)\n\n[Gunakan *tools* standar Laravel yang diterima oleh komunitas](#gunakan-tools-standar-laravel-yang-diterima-oleh-komunitas)\n\n[Ikuti konvensi penamaan Laravel](#ikuti-konvensi-penamaan-laravel)\n\n[Gunakan sintaks yang lebih pendek dan lebih mudah dibaca jika memungkinkan](#gunakan-sintaks-yang-lebih-pendek-dan-lebih-mudah-dibaca-jika-memungkinkan)\n\n[Gunakan *IoC Container* atau *facades* daripada kelas baru](#gunakan-ioc-container-atau-facades-daripada-kelas-baru)\n\n[Jangan mendapatkan data dari file `.env` secara langsung](#jangan-mendapatkan-data-dari-file-env-secara-langsung)\n\n[Simpan tanggal dalam format standar. Gunakan *accessors* dan *mutators* untuk mengubah format tanggal](#simpan-tanggal-dalam-format-standar-gunakan-accessors-dan-mutators-untuk-mengubah-format-tanggal)\n\n[Praktik bagus lainnya](#praktik-bagus-lainnya)\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n### **Prinsip *single responsibility***\n\nKelas dan metode seharusnya hanya memiliki satu tanggung jawab.\n\nContoh buruk:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nContoh terbaik:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Model tebal, *controller* tipis**\n\nMasukkan semua logika terkait DB ke model *eloquent* atau ke dalam kelas repositori jika anda menggunakan *Query Builder* atau kueri SQL mentah.\n\nContoh buruk:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nContoh terbaik:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Validasi**\n\nPindahkan validasi dari *controller* ke kelas *request*.\n\nContoh buruk:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nContoh terbaik:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### ***Business logic* harus di dalam kelas *services***\n\n*Controller* harus hanya memiliki satu tanggung jawab, jadi pindahkan *business logic* dari *controller* ke kelas *service*.\n\nContoh buruk:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nContoh terbaik:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### ***Don't repeat yourself* (DRY)**\n\nGunakan kembali kode ketika anda bisa. [PSR](#prinsip-single-responsibility) membantu anda menghindari duplikasi. Juga, gunakan kembali *template blade*, *scope eloquent*, dll.\n\nContoh buruk:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nContoh terbaik:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Lebih memilih menggunakan *Eloquent* daripada menggunakan *Query Builder* dan query SQL mentah. Lebih memilih *collections* daripada *array***\n\n*Eloquent* memungkinkan anda menulis kode yang dapat dibaca dan *maintainable*. Dan, *Eloquent* memiliki *built-in tools* yang bagus seperti *soft deletes*, *events*, *scopes*, dll.\n\nContoh buruk:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nContoh terbaik:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### ***Mass assignment***\n\nContoh buruk:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nContoh terbaik:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Jangan mengeksekusi kueri dalam *template blade* dan gunakan *eager loading* (masalah N + 1)**\n\nContoh buruk (untuk 100 *user*, 101 kueri DB akan dieksekusi):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nContoh terbaik (untuk 100 *user*, 2 kueri DB akan dieksekusi):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Komentari kode anda, tetapi lebih baik *method* dan nama variabel yang deskriptif daripada komentar**\n\nContoh buruk:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nContoh lebih baik:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nContoh terbaik:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Jangan letakkan JS dan CSS di *template blade* dan jangan letakkan HTML apa pun di kelas PHP**\n\nContoh buruk:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nContoh lebih baik:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nAtau\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nDalam file javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nCara terbaik adalah dengan menggunakan *package* `PHP to JS` khusus untuk mentransfer data.\n\n[🔝 Kembali ke konten](#konten)\n\n### **Gunakan file *config*, *language*, dan konstanta daripada teks dalam kode**\n\nContoh buruk:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nContoh terbaik:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Gunakan *tools* standar Laravel yang diterima oleh komunitas**\n\nSelalu gunakan fungsi *built-in* bawaan laravel dan *packages* komunitas daripada menggunakan *packages* dan *tools* pihak ke-3. *Developer* manapun yang akan bekerja dengan aplikasi anda di masa mendatang perlu mempelajari *tools* baru. Dan juga, peluang untuk mendapatkan bantuan dari komunitas Laravel jauh lebih rendah saat anda menggunakan *packages* atau *tools* pihak ke-3. Jangan membuat klien Anda membayar untuk itu.\n\nTask | Tools *standar* | *Tools* pihak ke-3\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Kembali ke konten](#konten)\n\n### **Ikuti konvensi penamaan Laravel**\n\nIkuti [PSR standards](https://www.php-fig.org/psr/psr-12/).\n\nDan juga, ikuti konvensi penamaan yang diterima oleh komunitas Laravel:\n\nWhat | How | Good | Bad\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Kembali ke konten](#konten)\n\n### **Gunakan sintaks yang lebih pendek dan lebih mudah dibaca jika memungkinkan**\n\nContoh buruk:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nContoh terbaik:\n\n```php\nsession('cart');\n$request->name;\n```\n\nContoh:\n\nSintaks umum | Sintaks pendek dan mudah dibaca\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Kembali ke konten](#konten)\n\n### **Gunakan *IoC Container* atau *facades* daripada kelas baru**\n\nSintaks Kelas baru membuat penggabungan yang sempit antar kelas dan memperumit proses pengujian. Gunakan *facades* atau *IoC container* sebagai gantinya.\n\nContoh buruk:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nContoh terbaik:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Jangan mendapatkan data dari file `.env` secara langsung**\n\nAlihkan data ke file konfigurasi sebagai gantinya dan kemudian gunakan fungsi `config ()` untuk menggunakan data dalam aplikasi.\n\nContoh buruk:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nContoh terbaik:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Simpan tanggal dalam format standar. Gunakan *accessors* dan *mutators* untuk mengubah format tanggal**\n\nContoh buruk:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nContoh terbaik:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Kembali ke konten](#konten)\n\n### **Praktik bagus lainnya**\n\nJangan pernah menaruh logika apa pun di file `route`.\n\nMinimalkan penggunaan *vanilla* PHP di *template blade*.\n\n[🔝 Kembali ke konten](#konten)\n"
  },
  {
    "path": "italian.md",
    "content": "![Laravel best practices](/images/logo-english.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nTranslations:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Contenuto\n\n[Principio della sola responsabilità](#single-responsibility-principle)\n\n[Modelli grassi, controller skinny](#fat-models-skinny-controllers)\n\n[Validazione](#validation)\n\n[La logica aziendale dovrebbe essere nella classe di servizio](#business-logic-should-be-in-service-class)\n\n[Non ripeterti (SECCO)](#dont-repeat-yourself-dry)\n\n[Preferisco usare Eloquent rispetto a Query Builder e query SQL non elaborate. Preferisce raccolte su array](#prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays)\n\n[Assegnazione di massa](#mass-assignment)\n\n[Non eseguire query nei modelli Blade e utilizzare il caricamento desideroso (problema N + 1)](#do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem)\n\n[Commenta il tuo codice, ma preferisci il metodo descrittivo e i nomi delle variabili rispetto ai commenti](#comment-your-code-but-prefer-descriptive-method-and-variable-names-over-comments)\n\n[Non inserire JS e CSS nei modelli Blade e non inserire HTML nelle classi PHP](#do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes)\n\n[Usa file di configurazione e lingua, costanti anziché testo nel codice](#use-config-and-language-files-constants-instead-of-text-in-the-code)\n\n[Utilizzare gli strumenti standard Laravel accettati dalla community](#use-standard-laravel-tools-accepted-by-community)\n\n[Segui le convenzioni di denominazione di Laravel](#follow-laravel-naming-conventions)\n\n[Utilizzare la sintassi più breve e più leggibile ove possibile](#use-shorter-and-more-readable-syntax-where-possible)\n\n[Utilizzare il contenitore o le facciate IoC anziché la nuova classe](#use-ioc-container-or-facades-instead-of-new-class)\n\n[Non ottiene direttamente i dati dal file `.env`](#do-not-get-data-from-the-env-file-directly)\n\n[Memorizza le date nel formato standard. Utilizzare accessori e mutatori per modificare il formato della data](#store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format)\n\n[Altre buone pratiche](#other-good-practices)\n\n### **Principio di singola responsabilità**\n\nUna classe e un metodo dovrebbero avere una sola responsabilità.\n\nSbagliato:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGiusto:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝Torna ai contenuti](#contents)\n\n### **Fat models, skinny controllers**\n\nInserisci 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.\n\nSbagliato:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGiusto:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[Torna ai contenuti](#contents)\n\n### **Validazione**\n\nSposta le logiche di validazione dai controller alle Request class.\n\nSbagliato:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGiusto:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[Torna ai contenuti](#contents)\n\n### **La logica di business dovrebbe essere nella classe di servizio**\n\nUn controller deve avere una sola responsabilità, quindi sposta la logica di business dai controller alle classi di servizio.\n\nSbagliato:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGiusto:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[Torna ai contenuti](#contents)\n\n### **Non ripeterti (DRY: Don't Repeat Yourself)**\n\nRiutilizzare 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.\n\nSbagliato:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGiusto:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[Torna ai contenuti](#contents)\n\n### **Favorisci l'utilizzo dei Model Eloquent rispetto al Query Builder e alle query SQL raw. Preferisci le Collection agli array**\n\nEloquent ti consente di scrivere codice leggibile e manutenibile. Inoltre, Eloquent ha ottimi strumenti integrati come eliminazioni soft, eventi, scopes, ecc.\n\nSbagliato:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGiusto:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[Torna ai contenuti](#contents)\n\n### **Assegnazione di massa**\n\nSbagliato:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nGiusto:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[Torna ai contenuti](#contents)\n\n### **Non eseguire query nei template Blade e utilizzare l'eager loading (problema N + 1)**\n\nSbagliato (per 100 utenti, verranno eseguite 101 query DB):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nGiusto (per 100 utenti, verranno eseguite 2 query DB):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[Torna ai contenuti](#contents)\n\n### **Commenta il tuo codice, ma cerca anche di rendere autoesplicativi i nomi di metodi e variabili**\n\nSbagliato:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nMeglio:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGiusto:\n\n```php\nif ($this->hasJoins())\n```\n\n[Torna ai contenuti](#contents)\n\n### **Non inserire JS e CSS nei templte Blade e non inserire HTML nelle classi PHP**\n\nSbagliato:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nMeglio:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nIn un file Javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nIl modo migliore è utilizzare il pacchetto PHP-JS specializzato per trasferire i dati.\n\n[Torna ai contenuti](#contents)\n\n### **Usa file di configurazione e lingua, costanti anziché testo nel codice**\n\nSbagliato:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nGiusto:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[Torna ai contenuti](#contents)\n\n### **Utilizzare gli strumenti standard Laravel accettati dalla community**\n\nFavorisci 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.\n\nTask | Strumenti standard | Strumenti di terze parti\n------------ | ------------- | -------------\nAutorizzazione | Policies | Entrust, Sentinel e altri pacchetti\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, pacchetti terzi\nAmbiente di sviluppo | Laravel Sail, Homestead | Docker\nDistribuzione | Laravel Forge | Deployer e altre soluzioni\nTest unitari | PHPUnit, Mockery | Phpspec, Pest\nTest dal browser | Laravel Dusk | Codeception\nDB | Eloquent, Query Builder | SQL, Doctrine\nTemplate | Blade | Twig\nLavorare con i dati | Laravel collection | Array\nValidazione form | Form Request | Pacchetti di terze parti, convalida nel controller\nAutenticazione | Incorporato | Pacchetti di terze parti, la tua soluzione\nAutenticazione API | Laravel Passport, Laravel Sanctum | Pacchetti JWT e OAuth di terze parti\nCreazione dell'API | Incorporato | API Dingo e pacchetti simili\nLavorare con la struttura DB | Migrazioni | Lavorare direttamente con la struttura DB\nLocalizzazione | Incorporato | Pacchetti di terze parti\nInterfacce utente in tempo reale | Laravel Echo, Pusher | Pacchetti di terze parti e funzionamento diretto con WebSocket\nGenerazione di dati di test | Seeder, Model Factories, Faker | Creazione manuale dei dati di test\nPianificazione delle attività | Laravel Task Scheduler | Script e pacchetti di terze parti\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[Torna ai contenuti](#contents)\n\n### **Segui le naming convention di Laravel**\n\nSeguire [Standard PSR](https://www.php-fig.org/psr/psr-12/).\n\nInoltre, segui le convenzioni di denominazione accettate dalla comunità Laravel:\n\nCosa | Come | Giusto | Sbagliato\n------------ | ------------- | ------------- | -------------\nController | singolare | ArticleController |~~ArticlesController~~\nRoute | plurale | articles/1 | ~~article/1~~\nRoute name | snake_case con notazione punto | users.show_active | ~~users.show-active, show-active-users~~\nModel | singolare | User | ~~Users~~\nRelazioni hasOne o belongsTo | singolare | articleComment |~~articleComments, article_comment~~\nTutte le altre relazioni | plurale | articleComments | ~~articleComment, article_comments~~\nTabella | plurale | article_comments | ~~article_comment, articleComments~~\nTabella pivot | nomi di modelli singolari in ordine alfabetico | article_user | ~~user_article, articles_users~~\nColonna della tabella | snake_case senza nome modello | meta_title |~~Meta Title; articolo meta_title~~\nProprietà del Model | snake_case | $ model->created_at |~~$model->createdAt~~\nForeign key | modello in singolare con un suffisso _id | article_id | ~~ArticleId, id_article, articles_id~~\nChiave primaria | - | id |~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table |~~2017_01_01_000000_articles~~\nMetodo | camelCase | getAll | ~~get_all~~\nMetodo nel resource controller | [resource](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMetodo nella test class | camelCase | testGuestCannotSeeArticle |~~test_guest_cannot_see_article~~\nVariabile | camelCase | $articolesWithAuthor |~~$articles_with_author~~\nCollection | descrittivo, plurale | $activeUsers = User::active()->get() | ~~$active, $data~~\nOggetto | descrittivo, singolare | $activeUser = User::active()->first() | ~~$users, $obj~~\nIndice file di configurazione e lingua | snake_case | articles_enabled |~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php |~~googleCalendar.php, google-calendar.php~~\nContratto (interfaccia) | aggettivo o sostantivo | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | aggettivo | Notificabile | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[Torna ai contenuti](#contents)\n\n### **Utilizzare una sintassi più breve e più leggibile ove possibile**\n\nSbagliato:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGiusto:\n\n```php\nsession('cart');\n$request->name;\n```\n\nPiù esempi:\n\nCommon syntax | Shorter and more readable syntax\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[Torna ai contenuti](#contents)\n\n### **Utilizzare il container IoC o i Facades invece di istanziare nuove classi**\n\nLa sintassi new Class crea un accoppiamento stretto tra le classi e complica i test. Utilizzare invece il container IoC o i Facades.\n\nSbagliato:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGiusto:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[Torna ai contenuti](#contents)\n\n### **Non prelevare direttamente i dati dal file `.env`**\n\nPassa i dati presenti nell'.env file ai file di configurazione e quindi usa l'helper `config ()` per prelevare i dati all'interno dell'applicazione.\n\nSbagliato:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGiusto:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[Torna ai contenuti](#contents)\n\n### **Memorizza le date nel formato standard. Utilizza gli accessors e i mutators per modificare il formato della data**\n\nSbagliato:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGiusto:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[Torna ai contenuti](#contents)\n\n### **Altre buone pratiche**\n\nNon inserire mai alcuna logica nei file di route.\n\nRidurre al minimo l'utilizzo di vanilla PHP nei template Blade.\n\n[Torna ai contenuti](#contents)\n"
  },
  {
    "path": "japanese.md",
    "content": "![Laravel ベストプラクティス](/images/logo-japanese.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n翻訳:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## コンテンツ\n\n[単一責任の原則](#単一責任の原則)\n\n[ファットモデル、スキニーコントローラ](#ファットモデルスキニーコントローラ)\n\n[バリデーション](#バリデーション)\n\n[ビジネスロジックはサービスクラスの中に書く](#ビジネスロジックはサービスクラスの中に書く)\n\n[繰り返し書かない (DRY)](#繰り返し書かない-dry)\n\n[クエリビルダや生のSQLクエリよりもEloquentを優先して使い、配列よりもコレクションを優先する](#クエリビルダや生のsqlクエリよりもeloquentを優先して使い配列よりもコレクションを優先する)\n\n[マスアサインメント](#マスアサインメント)\n\n[Bladeテンプレート内でクエリを実行しない。Eager Lodingを使う(N + 1問題)](#bladeテンプレート内でクエリを実行しないeager-lodingを使うn--1問題)\n\n[コメントを書く。ただしコメントよりも説明的なメソッド名と変数名を付けるほうが良い](#コメントを書くただしコメントよりも説明的なメソッド名と変数名を付けるほうが良い)\n\n[JSとCSSをBladeテンプレートの中に入れない、PHPクラスの中にHTMLを入れない](#jsとcssをbladeテンプレートの中に入れないphpクラスの中にhtmlを入れない)\n\n[コード内の文字列の代わりにconfigファイルとlanguageのファイル、定数を使う](#コード内の文字列の代わりにconfigファイルとlanguageのファイル定数を使う)\n\n[コミュニティに受け入れられた標準のLaravelツールを使う](#コミュニティに受け入れられた標準のLaravelツールを使う)\n\n[Laravelの命名規則に従う](#Laravelの命名規則に従う)\n\n[できるだけ短く読みやすい構文で書く](#できるだけ短く読みやすい構文で書く)\n\n[newの代わりにIoCコンテナもしくはファサードを使う](#newの代わりにIoCコンテナもしくはファサードを使う)\n\n[`.env`ファイルのデータを直接参照しない](#envファイルのデータを直接参照しない)\n\n[日付を標準フォーマットで保存する。アクセサとミューテータを使って日付フォーマットを変更する](#日付を標準フォーマットで保存するアクセサとミューテータを使って日付フォーマットを変更する)\n\n[その他 グッドプラクティス](#その他-グッドプラクティス)\n\n### **単一責任の原則**\n\nクラスとメソッドは1つの責任だけを持つべきです。\n\nBad:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGood:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **ファットモデル、スキニーコントローラ**\n\nDBに関連するすべてのロジックはEloquentモデルに入れるか、もしクエリビルダもしくは生のSQLクエリを使用する場合はレポジトリークラスに入れます。\n\nBad:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGood:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **バリデーション**\n\nバリデーションはコントローラからリクエストクラスに移動させます。\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **ビジネスロジックはサービスクラスの中に書く**\n\nコントローラはただ1つの責任だけを持たないといけません、そのためビジネスロジックはコントローラからサービスクラスに移動させます。\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **繰り返し書かない (DRY)**\n\n可能であればコードを再利用します。単一責任の原則は重複を避けることに役立ちます。また、Bladeテンプレートを再利用したり、Eloquentのスコープなどを使用したりします。\n\nBad:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGood:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **クエリビルダや生のSQLクエリよりもEloquentを優先して使い、配列よりもコレクションを優先する**\n\nEloquentにより読みやすくメンテナンスしやすいコードを書くことができます。また、Eloquentには論理削除、イベント、スコープなどの優れた組み込みツールがあります。\n\nBad:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGood:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **マスアサインメント**\n\nBad:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nGood:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **Bladeテンプレート内でクエリを実行しない。Eager Lodingを使う(N + 1問題)**\n\nBad (100ユーザに対して、101回のDBクエリが実行される):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nGood (100ユーザに対して、2回のDBクエリが実行される):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **コメントを書く。ただしコメントよりも説明的なメソッド名と変数名を付けるほうが良い**\n\nBad:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nBetter:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGood:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **JSとCSSをBladeテンプレートの中に入れない、PHPクラスの中にHTMLを入れない**\n\nBad:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nBetter:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nJavaScript ファイルで以下のように記述します:\n\n```javascript\nlet article = $('#article').val();\n```\n\nもっとも良い方法は、データを転送するためJSパッケージに特別なPHPを使用することです。\n\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **コード内の文字列の代わりにconfigファイルとlanguageのファイル、定数を使う**\n\nBad:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nGood:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **コミュニティに受け入れられた標準のLaravelツールを使う**\n\nサードパーティ製のパッケージやツールの代わりに、Laravel標準機能とコミュニティパッケージを使うことを推奨します。将来あなたと共に働くことになるどの開発者も新しいツールを学習する必要があります。また、サードパーティ製のパッケージやツールを使用している場合は、Laravelコミュニティから助けを得る機会が大幅に少なくなります。あなたのクライアントにその代金を払わせないでください。\n\nタスク | 標準ツール | サードパーティ製ツール\n------------ | ------------- | -------------\n認可 | Policies | Entrust, Sentinel または他のパッケージ\nアセットコンパイル | Laravel Mix, Vite | Grunt, Gulp, サードパーティ製パッケージ\n開発環境 | Laravel Sail, Homestead | Docker\nデプロイ | Laravel Forge | Deployer またはその他ソリューション\n単体テスト| PHPUnit, Mockery | Phpspec, Pest\nブラウザテスト | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nテンプレート | Blade | Twig\nデータの取り扱い | Laravel collections | Arrays\nフォームバリデーション | Request classes | サードパーティ製パッケージ、コントローラ内でバリデーション\n認証 | 標準組み込み | サードパーティ製パッケージ、独自実装\nAPI 認証 | Laravel Passport, Laravel Sanctum | サードパーティ製の JWT や OAuth パッケージ\nAPI作成 | 標準組み込み | Dingo API や類似パッケージ\nDB構造の取り扱い | Migrations | 直接DB構造を扱う\nローカライゼーション | 標準組み込み | サードパーティ製パッケージ\nリアルタイムユーザインターフェース | Laravel Echo, Pusher | サードパーティ製パッケージ または直接Webソケットを扱う\nテストデータ生成 | Seeder classes, Model Factories, Faker | 手動でテストデータを作成\nタスクスケジューリング | Laravel Task Scheduler | スクリプトやサードパーティ製パッケージ\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **Laravelの命名規則に従う**\n\n[PSR](https://www.php-fig.org/psr/psr-12/)に従います。\n\nまた、Laravelコミュニティに受け入れられた命名規則に従います。\n\n対象 | 規則 | Good | Bad\n------------ | ------------- | ------------- | -------------\nコントローラ | 単数形 | ArticleController | ~~ArticlesController~~\nルート | 複数形 | articles/1 | ~~article/1~~\n名前付きルート | スネークケースとドット表記 | users.show_active | ~~users.show-active, show-active-users~~\nモデル | 単数形 | User | ~~Users~~\nhasOne または belongsTo 関係 | 単数形 | articleComment | ~~articleComments, article_comment~~\nその他すべての関係 | 複数形 | articleComments | ~~articleComment, article_comments~~\nテーブル | 複数形 | article_comments | ~~article_comment, articleComments~~\nPivotテーブル | 単数形 モデル名のアルファベット順 | article_user | ~~user_article, articles_users~~\nテーブルカラム | スネークケース モデル名は含めない | meta_title | ~~MetaTitle; article_meta_title~~\nモデルプロパティ | スネークケース | $model->created_at | ~~$model->createdAt~~\n外部キー | 単数形 モデル名の最後に_idをつける | article_id | ~~ArticleId, id_article, articles_id~~\n主キー | - | id | ~~custom_id~~\nマイグレーション | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nメソッド | キャメルケース | getAll | ~~get_all~~\nリソースコントローラのメソッド | [一覧](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nテストクラスのメソッド | キャメルケース | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\n変数 | キャメルケース | $articlesWithAuthor | ~~$articles_with_author~~\nコレクション | 説明的、 複数形 | $activeUsers = User::active()->get() | ~~$active, $data~~\nオブジェクト | 説明的, 単数形 | $activeUser = User::active()->first() | ~~$users, $obj~~\n設定ファイルと言語ファイルのインデックス | スネークケース | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nビュー | ケバブケース | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nコンフィグ | スネークケース | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\n契約 (インターフェイス) | 形容詞または名詞 | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | 形容詞 | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **できるだけ短く読みやすい構文で書く**\n\nBad:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGood:\n\n```php\nsession('cart');\n$request->name;\n```\n\nさらなる例:\n\n一般的な構文 | 短く読みやすい構文\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **newの代わりにIoCコンテナもしくはファサードを使う**\n\nnew構文はクラス間の密結合を生み出し、テストすることを難しくします。IoCコンテナまたはファサードを代わりに使います。\n\nBad:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGood:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **`.env`ファイルのデータを直接参照しない**\n\n代わりにconfigファイルへデータを渡します。そして、アプリケーション内でデータを参照する場合は`config()`ヘルパー関数を使います。\n\nBad:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGood:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// データを使用する\n$apiKey = config('api.key');\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **日付を標準フォーマットで保存する。アクセサとミューテータを使って日付フォーマットを変更する**\n\nBad:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGood:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 コンテンツに戻る](#コンテンツ)\n\n### **その他 グッドプラクティス**\n\nルートファイルにはロジックを入れないでください。\n\nBladeテンプレートの中でVanilla PHP(標準のPHPコードを記述すること)の使用は最小限にします。\n\n[🔝 コンテンツに戻る](#コンテンツ)\n"
  },
  {
    "path": "pashto.md",
    "content": "![د لاراول د ښه کوډ لیکلو نمونې او مثالونه](/images/logo-pashto.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app) and [Eloquent SQL reference](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nژباړې:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[Indonesia](indonesia.md) (by [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[简体中文](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[繁體中文](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Română](romanian.md) (by [als698](https://github.com/als698))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## د منځپانګو یا مطالبو نوملړ\n\n[د یوه مسؤلیت اصل](#د-یوه-مسؤلیت-اصل)\n\n[میتودونه باید یوازې یو کار ترسره کړي](#میتودونه-باید-یوازې-یو-کار-ترسره-کړي)\n\n[لوی ماډلونه، کوچني کنټرولرونه!](#لوی-ماډلونه-کوچني-کنټرولرونه)\n\n[ډېټا تصدیق یا اعتبار](#ډېټا-تصدیق-یا-اعتبار)\n\n[د پروګرام منطق باید په service class کې وي.](#د-پروګرام-منطق-باید-په-service-class-کې-وي)\n\n[د DRY اصل یا خپل ځان مه تکراروه!](#د-dry-اصل-یا-خپل-ځان-مه-تکراروه)\n\n[د Query Builder او raw SQL queries پر ځای باید د Eloquent ORM څخه کار واخیستل شي. او همچنان د Arrays پر ځای د Collections څخه کار واخیستل شي.](#د-query-builder-او-raw-sql-queries-پر-ځای-باید-د-eloquent-orm-څخه-کار-واخیستل-شي-او-همچنان-د-arrays-پر-ځای-د-collections-څخه-کار-واخیستل-شي)\n\n[(Mass assignment) ډله ایزه دنده](#mass-assignment-ډله-ایزه-دنده)\n\n\n[د دې پر ځای چې query په blade کې ولیکئ د eager loading څخه کار واخلئ. (N+1 مسئله)](#د-دې-پر-ځای-چې-query-په-blade-کې-ولیکئ-د-eager-loading-څخه-کار-واخلئ-n1-مسئله)\n\n[د ډېرې ډېټا لپاره د ډېټا چنک (data chunk) نه استفاده وکړئ](#د-ډېرې-ډېټا-لپاره-د-ډېټا-چنک-data-chunk-نه-استفاده-وکړئ)\n\n\n[تبصرې وکړئ (Comments)، مګر د متودونو یا متغیرونو نومونه توضیحي او معنی لرونکي په پام کې ونیسئ.](#تبصرې-وکړئ-comments-مګر-د-متودونو-یا-متغیرونو-نومونه-توضیحي-او-معنی-لرونکي-په-پام-کې-و-نیسئ)\n\n[په Blade ټیمپلیټونو کې له js او css څخه کار مه اخلئ او هېڅ HTML کوډ په PHP class کې مه کاروئ.](#په-blade-ټیمپلیټونو-کې-له-js-او-css-څخه-کار-مه-اخلئ-او-هېڅ-html-کوډ-په-php-class-کې-مه-کاروئ)\n\n[پر ځای د مستقیم متنونو څخه په کوډ کې، د config او languages فایلونو څخه کار واخلئ!](#پر-ځای-د-مستقیم-متنونو-څخه-په-کوډ-کې-د-config-او-languages-فایلونو-څخه-کار-واخلئ)\n\n[د لاراول د معیاري وسایلو څخه چې د لاراول ټولنې یا کمیونیټي لخوا تایید شوي دي کار واخلئ](#د-لاراول-د-معیاري-وسایلو-څخه-چې-د-لاراول-ټولنې-یا-کمیونیټي-لخوا-تایید-شوي-دي-کار-واخلئ)\n\n[د لاراول د نومونو له اصولو څخه کار واخلئ.](#د-لاراول-د-نومونو-له-اصولو-څخه-کار-واخلئ)\n\n[کنوانسیون د تنظیماتو په پرتله غوره دی](#کنوانسیون-د-تنظیماتو-په-پرتله-غوره-دی)\n\n[تر حده پورې په خپل کوډ کې، د معنی لرونکي او لنډ Syntax څخه کار واخلئ.](#تر-حده-پورې-په-خپل-کوډ-کې-د-معنی-لرونکي-او-لنډ-syntax-څخه-کار-واخلئ)\n\n[د object د جوړولو په وخت کې د new کیورد پر ځای IoC container او facades څخه کار واخلئ](#د-object-د-جوړولو-په-وخت-کې-د-new-کیورد-پر-ځای-ioc-container-او-facades-څخه-کار-واخلئ)\n\n[له .env فایل څخه هېڅ وخت مستقیم ډېټا مه ترلاسه کوئ.](#له-env-فایل-څخه-هېڅ-وخت-مستقیم-ډېټا-مه-ترلاسه-کوئ)\n\n[تاریخ او وخت په معیاري بڼه کې ذخیره کړئ. د تاریخ او وخت د ښودلو لپاره له Accessors & Mutators څخه کار واخلئ.](#تاریخ-او-وخت-په-معیاري-بڼه-کې-ذخیره-کړئ-د-تاریخ-او-وخت-د-ښودلو-لپاره-له-accessors--mutators-څخه-کار-واخلئ)\n\n\n[ډاټ بلاک مه استفاده کوئ](#ډاټ-بلاک-مه-استفاده-کوئ)\n\n[نورې ښې طریقې](#نورې-ښې-طریقې)\n\n\n### **د یوه مسؤلیت اصل**\n\nیو کلس باید یوه وظیفه ولري.\n\nبد کوډ:\n\n```php\npublic function update(Request $request): string\n{\n    $validated = $request->validate([\n        'title' => 'required|max:255',\n        'events' => 'required|array:date,type'\n    ]);\n\n    foreach ($request->events as $event) {\n        $date = $this->carbon->parse($event['date'])->toString();\n\n        $this->logger->log('Update event ' . $date . ' :: ' . $);\n    }\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\nښه کوډ:\n\n```php\npublic function update(UpdateRequest $request): string\n{\n    $this->logService->logEvents($request->events);\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **میتودونه باید یوازې یو کار ترسره کړي**\n\nیو فنکشن باید یوازې یو کار ترسره کړي او باید په ښه شکل یې ترسره کړي.\n\nبد کوډ:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nښه کوډ:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **لوی ماډلونه، کوچني کنټرولرونه!**\n\n\nد ډېټابیس مربوط شیان په Eloquent models کې ولیکئ.\n\nبد کوډ:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nښه کوډ:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders(): Collection\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **ډېټا تصدیق یا اعتبار**\n\nد ډېټا تصدیق یا ولیدیشن د کنټرولرونو پر ځای په Request classess کې ولیکئ.\n\nبد کوډ:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nښه کوډ:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules(): array\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د پروګرام منطق باید په service class کې وي**\n\nیو کنټرولر باید یوازې یو مسؤلیت ولري، نو د کوډ منطق په د کنټرولرونو پر ځای باید په service classes کې ولیکئ.\n\nبد کوډ:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nښه کوډ:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image): void\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د DRY اصل یا خپل ځان مه تکراروه**\n\nکوډ یو وار ولیکئ او ډېر ځایه یې استعمال کړی د کوډ د تکرار څخه ډډه وکړئ.\nهمچنان Blade templates کوډ‌ هم مه تکراروئ، د Eloquent scope څخه استفاده وکړئ.\n\nبد کوډ:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nښه کوډ:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', true)->whereNotNull('deleted_at');\n}\n\npublic function getActive(): Collection\n{\n    return $this->active()->get();\n}\n\npublic function getArticles(): Collection\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n\n### **د Query Builder او raw SQL queries پر ځای باید د Eloquent ORM څخه کار واخیستل شي. او همچنان د Arrays پر ځای د Collections څخه کار واخیستل شي**\n\n\nد Eloquent په واسېه تاسې کولی شئ چې کوډ ویونکي او -هغه چا ته چې وروسته په کوډ کې تغییرات راولی- ته آسانه کوډ ولیکئ.\nهمچنان Eloquent مخکې جوړ شوي شیان لري لکه soft deletes, events, scopes وغیره\n[Eloquent SQL ته مرجع](https://github.com/alexeymezenin/eloquent-sql-reference).\n\nبد کوډ:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nښه کوډ:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **(Mass assignment) ډله ایزه دنده**\n\nبد کوډ:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nښه کوډ:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د دې پر ځای چې query په blade کې ولیکئ د eager loading څخه کار واخلئ. (N+1 مسئله)**\n\n\nبد کوډ (د ۱۰۰ کارنو لپاره ۱۰۱ کیوریانې رن کوي):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nښه کوډ (for 100 users, 2 DB queries will be executed):\nښه کوډ (د ۱۰۰ کارنو لپاره ۲ کیوریانې رن کوي):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د ډېرې ډېټا لپاره د ډېټا چنک (data chunk) نه استفاده وکړئ**\n\nبد کوډ:\n\n```php\n$users = $this->get();\n\nforeach ($users as $user) {\n    ...\n}\n```\n\nښه کوډ:\n\n```php\n$this->chunk(500, function ($users) {\n    foreach ($users as $user) {\n        ...\n    }\n});\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **تبصرې وکړئ comments مګر د متودونو یا متغیرونو نومونه توضیحي او معنی لرونکي په پام کې و نیسئ**\n\n\nبد کوډ:\n\n```php\n// Determine if there are any joins\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nښه کوډ:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **په Blade ټیمپلیټونو کې له js او css څخه کار مه اخلئ او هېڅ HTML کوډ په PHP class کې مه کاروئ**\n\n\nبد کوډ:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nبهتر کوډ:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nیا\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nپه یو جاواسکریپت فایل کې\n\n```javascript\nlet article = $('#article').val();\n```\n\nبهتره داده چې د PHP to JS .پکیج څخه د ډیټا د لېږلو په خاطر استفاده وشي\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **پر ځای د مستقیم متنونو څخه په کوډ کې، د config او languages فایلونو څخه کار واخلئ!**\n\nبد کوډ:\n\n```php\npublic function isNormal(): bool\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nښه کوډ:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د لاراول د معیاري وسایلو څخه چې د لاراول ټولنې یا کمیونیټي لخوا تایید شوي دي کار واخلئ**\n\nد دې پر ځای چې دریمګړي پکیجونو او ټولس نه استفاده وکړئ دا به ښه وي چې د لاراول او د لاراول ټولنې له خوا  چې کوم پکیجونه او ټولس جوړ شوي، نه استفاده وشي.\nهر هغه ډېوېلوپر چې په راتلونکي کې ستاسو له اپلکیشن څخه استفاده کوی، باید نوي ټولس زده کړي.\nاو که چېرې تاسې دا پکیجونه او ټولس چې رسمي د لاراول ټولنې څخه نه وي، استفاده کوئ. که چېرې کومې ستونزې سره مخامخ کېږئ نه حلول به یې ګران کار وي.  \n\nوظیفه | معیاري وسایل | (غیر معیاری) 3 ګړي وسایل\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د لاراول د نومونو له اصولو څخه کار واخلئ**\n\nدا تعقیب کړئ [PSR standards](https://www.php-fig.org/psr/psr-12/).\n\nهمچنان هغه اصول چې د لاراول د ټولنې لخوا منل شوي دي، تعقیب کړئ\n\nڅه | څنګه | ښه کوډ | بد کوډ\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **کنوانسیون د تنظیماتو په پرتله غوره دی**\n\n\nکه تاسو دې اصولو ته پاملرنه وکړئ نو اضافي کانفیګ او کوډونو ته به اړتیا و نه لرئ\n\nبد کوډ:\n\n```php\n// Table name 'Customer'\n// Primary key 'customer_id'\nclass Customer extends Model\n{\n    const CREATED_AT = 'created_at';\n    const UPDATED_AT = 'updated_at';\n\n    protected $table = 'Customer';\n    protected $primaryKey = 'customer_id';\n\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');\n    }\n}\n```\n\nښه کوډ:\n\n```php\n// Table name 'customers'\n// Primary key 'id'\nclass Customer extends Model\n{\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class);\n    }\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **تر حده پورې په خپل کوډ کې، د معنی لرونکي او لنډ Syntax څخه کار واخلئ**\n\nبد کوډ:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nښه کوډ:\n\n```php\nsession('cart');\n$request->name;\n```\n\nنور مثالونه:\n\nعام Syntax | لنډ او کوډ لوستونکي ته آسانه کوډ\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id` (in PHP 8: `$object->relation?->id`)\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **د object د جوړولو په وخت کې د new کیورد پر ځای IoC container او facades څخه کار واخلئ**\n\n\nد کلسونو نوی syntax کوډ پېچلی کوي د هغه پر ځای د  IoC container  او  Facades نه استفاده وکړئ.\n\nبد کوډ:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nښه کوډ:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **له .env فایل څخه هېڅ وخت مستقیم ډېټا مه ترلاسه کوئ**\n\n\nد .env فایل څخه مستقیما ډېټا مه را اخلئ دهغه پر ځای د config() helper function  نه استفاده وکړئ.\n\nبد کوډ:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nښه کوډ:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **تاریخ او وخت په معیاري بڼه کې ذخیره کړئ. د تاریخ او وخت د ښودلو لپاره له Accessors & Mutators څخه کار واخلئ**\n\n\nیو نېټه د string په بڼه د یو object  لږ باوري ده، لکه د کاربون (Carbon) بیلګه. \nسپارښتنه کیږي چې د نیټې د strings پر ځای د کاربون بیلګې د ټولګیو ترمنځ ولېږدوئ. ښودنه باید په  view  برخه د اپلکېشن  (templates) کې وشي.\n\n\n\nبد کوډ:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nښه کوډ:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\n// Blade view\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->format('m-d') }}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **ډاټ بلاک مه استفاده کوئ**\n\nDotBlocks  د کوډ ویل سختوي، نو باید د descriptive method  او د php  د نوي خصوصیاتو (features) څخه استفاده وشي لکه return type hints.\n\nبد کوډ:\n\n```php\n/**\n * The function checks if given string is a valid ASCII string\n *\n * @param string $string String we get from frontend which might contain\n *                       illegal characters. Returns True is the string\n *                       is valid.\n *\n * @return bool\n * @author  John Smith\n *\n * @license GPL\n */\n\npublic function checkString($string)\n{\n}\n```\n\nښه کوډ:\n\n```php\npublic function isValidAsciiString(string $string): bool\n{\n}\n```\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n\n### **نورې ښې طریقې**\n\n هغه patterns  له هغو وسایلو نه چې له لاراول ته له مشابهو فریمورکونو څخه دي لکه جانګو یا روبي، نه ډډه وکړئ.\n که ستاسو Symfony یا  Sprint فریمورکونه خوښیږي، دا به ښه وي چې د لاراول پر ځای له هغو نه استفاده وکړئ.  \n\nپه Route فایلونو کې هیڅکله خپل د اپلکیشن کډ مه لیکئ.\n\nله خالص پي اچ پي یا vanilla php څخه به blade templates کې کوشش وکړئ چې تر آخری خده ډډه وکړئ. \n\nد ټسټنګ لپاره د in-memory DB نه استفاده وکړئ\n\nد فریمورک په کود او features کې تغییر مه راولئ.\n\nد عصري پی اچ پي‌ څخه استفاده وکړئ، داسې کوډ ولیکئ‌ چې په راتلونکي کې، ویل یې آسانه وي.\n\nد View Composers د استعمال څخه ډډه وکړئ تر هغه پورې چې سل سلنه پرې پوه نشئ. پهر ډېرو حالاتو کې ډېرې نورې آسانې لارې هم شته چې هغه کار تر سره کړئ.\n\n[🔝 بېرته تګ منځپانګو ته](#د-منځپانګو-یا-مطالبو-نوملړ\n)\n"
  },
  {
    "path": "persian.md",
    "content": "![روش های روش قابل قبول توسعه پروژه های مبتنی بر فریم ورک لاراول](/images/logo-persian.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n<div dir=\"rtl\">\n\nترجمه ها:\n\n</div>\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[Indonesia](indonesia.md) (by [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[简体中文](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[繁體中文](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [Amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n<div dir=\"rtl\">\n\n## فهرست مطالب\n\n- [اصل تک وظیفه ای بودن](#اصل-تک-وظیفه-ای-بودن)\n\n- [مدل های بزرگ،‌ کنترلرهای کوچک!](#مدل-های-بزرگ-کنترلرهای-کوچک)\n\n- [اعتبارسنجی](#اعتبارسنجی)\n\n- [منطق برنامه باید در service class باشد.](#منطق-برنامه-باید-در-service-class-باشد)\n\n- [اصل DRY یا خودت را تکرار نکن!](#اصل-dry-یا-خودت-را-تکرار-نکن)\n\n- [به جای استفاده از Query Builder و raw SQL queries از Eloquent ORM استفاده کنید. همچنین به جای استفاده از Arrays از Collections استفاده کنید.](#به-جای-استفاده-از-query-builder-و-raw-sql-queries-از-eloquent-orm-استفاده-کنید-همچنین-به-جای-استفاده-از-arrays-از-collections-استفاده-کنید)\n\n- [ایجاد یک مدل](#ایجاد-یک-مدل)\n\n- [به جای نوشتن query ها در blade از eager loading استفاده کنید. (مسئله N+1)](#به-جای-نوشتن-query-ها-در-blade-از-eager-loading-استفاده-کنید-مسئله-n1)\n\n- [کامنت گذاری بکنید، ولی اسامی متدها یا متغیرها را توصیفی و معنادار در نظر بگیرید. ](#کامنت-گذاری-بکنید-ولی-اسامی-متدها-یا-متغیرها-را-توصیفی-و-معنادار-در-نظر-بگیرید)\n\n- [در تمپلیت های Blade از js و css استفاده نکنید و هیچگونه کد HTML ای را در class های PHP استفاده نکنید.](#در-تمپلیت-های-blade-از-js-و-css-استفاده-نکنید-و-هیچگونه-کد-html-ای-را-در-class-های-php-استفاده-نکنید)\n\n- [به جای استفاده مستقیم از متن ها در کد، از فایل های config و languages استفاده کنید!](#به-جای-استفاده-مستقیم-از-متن-ها-در-کد-از-فایل-های-config-و-langugeus-استفاده-کنید)\n\n- [از ابزارهای استاندارد لاراول که مورد تایید جامعه کاربری آن میباشد، استفاده کنید.](#از-ابزارهای-استاندارد-لاراول-که-مورد-تایید-جامعه-کاربری-آن-میباشد-استفاده-کنید)\n\n- [از قرارداد های لاراول برای نامگذاری ها استفاده کنید.](#از-قرارداد-های-لاراول-برای-نامگذاری-ها-استفاده-کنید)\n\n- [تا حد ممکن در کدتان، از Syntax های معنادار و کوتاه استفاده کنید.](#تا-حد-ممکن-در-کدتان-از-syntax-های-معنادار-و-کوتاه-استفاده-کنید)\n\n- [به جای ایجاد یک object با new، از IoC container و facades استفاده کنید.](#به-جای-ایجاد-یک-object-با-new-از-ioc-container-و-facades-استفاده-کنید)\n\n- [از فایل .env هیچ وقت مستقیم داده ای دریافت نکنید.](#از-فایل-env-هیچ-وقت-مستقیم-داده-ای-دریافت-نکنید)\n\n- [تاریخ و زمان را در قالب استاندارد ذخیره کنید. از Accessors & Mutators ها برای دستکاری در نمایش تاریخ و زمان استفاده کنید.](#تاریخ-و-زمان-را-در-قالب-استاندارد-ذخیره-کنید-از-accessors--mutators-ها-برای-دستکاری-در-نمایش-تاریخ-و-زمان-استفاده-کنید)\n\n- [دیگر روش ها](#دیگر-قواعد-توسعه-روش-قابل-قبول-بدون-فهرست)\n</div>\n\n<div dir=\"rtl\">\n\n### **اصل تک وظیفه ای بودن**\n\nهر class و هر method باید یک وظیفه داشته باشد.\n\n❌ روش اشتباه:\n\n</div>\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **مدل های بزرگ،‌ کنترلرهای کوچک!**\n\nاگر از Query Builder یا raw SQL queries استفاده میکنید، تمام منطق پایگاه داده را در model ها یا Repository classes قرار بدهید.\n\n❌ روش اشتباه:\n\n</div>\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **اعتبارسنجی**\n\nاعتبارسنجی ها را در Request classes انجام دهید نه در controllers.\n\n❌ روش اشتباه:\n\n</div>\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **منطق برنامه باید در service class باشد**\n\nهر کنترلر باید یک وظیفه داشته باشد، بنابراین منطق برنامه را در service classes بنویسید.\n\n❌ روش اشتباه:\n\n</div>\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **اصل DRY یا خودت را تکرار نکن!**\n\nتا حد ممکن از کد ها بازاستفاده کنید. تک وظیفه ای شدن به شما کمک میکند تا کار تکراری نکنید. همچنین در Blade template حتما این اصل را رعایت کنید و در model ها از Eloquent scopes استفاده کنید و ... .\n\n❌ روش اشتباه:\n\n</div>\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **به جای استفاده از Query Builder و raw SQL queries از Eloquent ORM استفاده کنید. همچنین به جای استفاده از Arrays از Collections استفاده کنید.**\n\nEloquent به شما این قابلیت را میدهد که کدهای خوانا و قابل توسعه بنویسید. همچنین دارای ویژگی های داخلی کاربردی مثل soft deletes یا events  یا scopes و .. میباشد.\n\n❌ روش اشتباه:\n\n</div>\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **ایجاد یک مدل**\n\n[منظور نویسنده از این بخش این میباشد که برای راحتی کار و کوتاه تر شدن کد، در html طوری مقادیر name هر input یا ... را نامگذاری کنید که با column های جدول مربوطه یکسان باشد تا laravel آن ها رو با یک کامند خیلی سریع مپ و ذخیره کند.]\n\n❌ روش اشتباه:\n\n</div>\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\n$category->article()->create($request->all());\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **به جای نوشتن query ها در blade از eager loading استفاده کنید. (مسئله N+1)**\n\n❌ روش اشتباه (برای ۱۰۰ کاربر، ما ۱۰۱ کوئری را اجرا میکنیم!):\n\n</div>\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول (برای ۱۰۰ کاربر، ما فقط ۲ کوئری اجرا کردیم!):\n\n</div>\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **کامنت گذاری بکنید، ولی اسامی متدها یا متغیرها را توصیفی و معنادار در نظر بگیرید.**\n\n❌ روش اشتباه:\n\n</div>\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n<div dir=\"rtl\">\n\n❗️ قابل قبول:\n\n</div>\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\nif ($this->hasJoins())\n```\n\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **در تمپلیت های Blade از js و css استفاده نکنید و هیچگونه کد HTML ای را در class های PHP استفاده نکنید.**\n\n❌ روش اشتباه:\n\n</div>\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n<div dir=\"rtl\">\n\n❗️ قابل قبول:\n\n</div>\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n<div dir=\"rtl\">\n\nدر فایل JavaScript:\n\n</div>\n\n```javascript\nlet article = $('#article').val();\n```\n<div dir=\"rtl\">\n\nبهترین راه استفاده از پکیج مخصوص انتقال داده از php به js میباشد.\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **به جای استفاده مستقیم از متن ها در کد، از فایل های config و languages استفاده کنید!**\n\n❌ روش اشتباه:\n\n</div>\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'مقاله شما ایجاد گردید!');\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **از ابزارهای استاندارد لاراول که مورد تایید جامعه کاربری آن میباشد، استفاده کنید.**\n\nبه جای استفاده از ابزارها و پکیج های غیررسمی لاراول از ابزارها و قابلیت های داخلی و رسمی لاراول استفاده کنید. هر توسعه دهنده [لاراولی] ای که در آینده بخواهد با کدهای شما کار کند، باید ابزارهای جدید یاد بگیرد. همچنین اگر شما از ابزارهای غیررسمی استفاده کنید، شانس دریافت کمک از جامعه کاربری لاراول به طرز قابل توجهی  کمتر میشود. این هزینه را به گردن مشتریان خود نیندازید! [منظور نویسنده این هست که تو کارهای بزرگ و مهمتون از ابزارهای جدید و پراکنده استفاده نکنید. همیشه سعی کنید از ابزارهای داخلی و یا پکیج های رسمی لاراول استفاده کنید مگر این که چاره دیگری نباشد! شما با پراکنده کردن ابزارها هزینه فنی/مالی توسعه محصول را برای آینده زیادتر میکنید!]\n\n</div>\n\nنیاز | ابزارهای رسمی لاراول | ابزارهای غیررسمی لاراول\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **از قرارداد های لاراول برای نامگذاری ها استفاده کنید**\n\nاز استاندارد های php که به [PSR](https://www.php-fig.org/psr/psr-12/) معروف است استفاده کنید.\n \nهمچنین روش های نامگذاری مورد قبول جامعه کاربری لاراول:\n\n</div>\n\nبخش مربوطه | قاعده اسم گذاری | ✔️ روش قابل قبول | ❌ روش اشتباه\n------------ | ------------- | ------------- | -------------\nController | اسامی مفرد | ArticleController | ~~ArticlesController~~\nRoute | اسامی جمع | articles/1 | ~~article/1~~\nRoute name | روش snake_case همراه با نقاط اتصال | users.show_active | ~~users.show-active, show-active-users~~\nModel | اسامی مفرد | User | ~~Users~~\nhasOne or belongsTo relationship | اسامی مفرد | articleComment | ~~articleComments, article_comment~~\nAll other relationships | اسامی جمع | articleComments | ~~articleComment, article_comments~~\nTable | اسامی جمع | article_comments | ~~article_comment, articleComments~~\nPivot table | نام مدل ها با اسامی مفرد و ترتیب الفبایی | article_user | ~~user_article, articles_users~~\nTable column | روش snake_case بدون اسم مدل| meta_title | ~~MetaTitle; article_meta_title~~\nModel property | روش snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | اسامی مفرد model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | روش camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | روش camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | روش camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | توصیفی و اسامی جمع | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | توصیفی و اسامی مفرد | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | صفت یا اسم | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | صفت | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **تا حد ممکن در کدتان، از Syntax های معنادار و کوتاه استفاده کنید**\n\n❌ روش اشتباه:\n\n</div>\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\nsession('cart');\n$request->name;\n```\n<div dir=\"rtl\">\n\nمثال های بیشتر:\n\n</div>\n\nسینتکس متداول | سینتکس کوتاه‌تر و خواناتر\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **به جای ایجاد یک object با new، از IoC container و facades استفاده کنید.**\n\nبه‌جای این که آبجکت‌ها را مستقیما در هرجایی با کلمه کلیدی new ایجاد و استفاده کنید، از امکان IoC لاراول استفاده کنید. چرا که با این کار عملا dependency های هر چیزی را در صورت نیاز آن inject‌ میکنیم که به ما قدرت استفاده از مفهوم تست‌نویسی را میدهد.\n\n❌ روش اشتباه:\n\n</div>\n\n```php\n$user = new User;\n$user->create($request->all());\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->all());\n```\n\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **از فایل .env هیچ وقت مستقیم داده ای دریافت نکنید.**\n\nاطلاعات موجود در .env را در صورت نیاز به استفاده، در فایل های config لود کنید و سپس با استفاده از helper function فایل های کانفیگ یعنی config() با آن در نرم افزار خود کار کنید.\n\n❌ روش اشتباه:\n</div>\n\n```php\n$apiKey = env('API_KEY');\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **تاریخ و زمان را در قالب استاندارد ذخیره کنید. از Accessors & Mutators ها برای دستکاری در نمایش تاریخ و زمان استفاده کنید.**\n\n❌ روش اشتباه:\n\n</div>\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n<div dir=\"rtl\">\n\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **در فایل های route خود هیچوقت منطق برنامه را قرار ندهید.**\n\n\n❌ روش اشتباه:\n\n</div>\n\n```php\n// Route\nRoute::get('user/1', function (User $user) {\n    return new UserResource($user);\n});\n```\n<div dir=\"rtl\">\n\n✔️ روش قابل قبول:\n\n</div>\n\n```php\n// Route\nRoute::get('users/1', 'UserController@show');\n\n// Controlelr\nclass UserController extends Controller \n{\n    public function show(User $user) {\n        return new UserResource($user);\n    }\n}\n```\n<div dir=\"rtl\">\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n### **تا حد ممکن از PHP خام در فایل های blade استفاده نکنید.**\n\n[🔝 بازگشت به فهرست](#فهرست-مطالب)\n\n</div>\n"
  },
  {
    "path": "polish.md",
    "content": "![Laravel - najlepsze praktyki](/images/logo-polish.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nTłumaczenia:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[Indonesia](indonesia.md) (by [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[简体中文](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[繁體中文](traditional-chinese.md) (by [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Spis treści\n\n[Zasada pojedynczej odpowiedzialności](#zasada-pojedynczej-odpowiedzialności)\n\n[Grube modele, chude kontrolery](#grube-modele-chude-kontrolery)\n\n[Walidacja](#walidacja)\n\n[Logika biznesowa powinna znajdować się w klasie Service](#logika-biznesowa-powinna-znajdować-się-w-klasie-service)\n\n[Nie powtarzaj się (DRY)](#nie-powtarzaj-się-dry)\n\n[Preferuj używanie modeli Eloquent-a ponad klasy Query Builder lub surowe zapytania SQL. Staraj się używać kolecji zamist tablic](#preferuj-używanie-modeli-eloquent-a-ponad-klasy-query-builder-lub-surowe-zapytania-sql-staraj-się-używać-kolecji-zamist-tablic)\n\n[Masowe przypisywanie](#masowe-przypisywanie)\n\n[Nie wykonuj zapytań w szablonach Blade oraz używaj eager loading-u (problem N + 1)](#nie-wykonuj-zapytań-w-szablonach-blade-oraz-używaj-eager-loading-u-problem-n--1)\n\n[Komentuj swój kod, ale preferuj opisowe nazwy metod i zmiennych zamiast komentarzy](#komentuj-swój-kod-ale-preferuj-opisowe-nazwy-metod-i-zmiennych-zamiast-komentarzy)\n\n[Nie umieszczaj kodu JS i CSS w szablonach Blade oraz nie osadzaj żadnego kodu HTML wewnątrz klas PHP.](#nie-umieszczaj-kodu-js-i-css-w-szablonach-blade-oraz-nie-osadzaj-żadnego-kodu-html-wewnątrz-klas-php)\n\n[Używaj plików konfiguracyjnych oraz językowych, stałych zamiast tekstu w kodzie](#używaj-plików-konfiguracyjnych-oraz-językowych-stałych-zamiast-tekstu-w-kodzie)\n\n[Używaj standardowych narzędzi Laravel-a zaakceptowanych przez społeczność](#używaj-standardowych-narzędzi-laravel-a-zaakceptowanych-przez-społeczność)\n\n[Postępuj zgodnie z konwencją nazewniczą Laravel-a](#postępuj-zgodnie-z-konwencją-nazewniczą-laravel-a)\n\n[W miarę możliwości używaj krótszej i bardziej czytelnej składni](#w-miarę-możliwości-używaj-krótszej-i-bardziej-czytelnej-składni)\n\n[Użyj kontenera IoC lub fasad zamiast nowych klas](#użyj-kontenera-ioc-lub-fasad-zamiast-nowych-klas)\n\n[Nie pobieraj wartości z pliku `.env` bezpośrednio](#nie-pobieraj-wartości-z-pliku-env-bezpośrednio)\n\n[Przechowuj daty w standardowym formacie. Używaj akcesorów i mutatorów do modyfikacji formatów.](#przechowuj-daty-w-standardowym-formacie-używaj-akcesorów-i-mutatorów-do-modyfikacji-formatów)\n\n[Inne dobre praktyki](#inne-dobre-praktyki)\n\n### **Zasada pojedynczej odpowiedzialności**\n\nKlasa i metoda powinny mieć tylko jedną odpowiedzialność.\n\nŹle:\n\n```php\npublic function getFullNameAttribute()\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Pan ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nDobrze:\n\n```php\npublic function getFullNameAttribute()\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient()\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong()\n{\n    return 'Pan ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort()\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Grube modele, chude kontrolery**\n\nUmieszczaj całą logikę związaną z DB w modelach Eloquent-a lub w klasach Repozytorium.\n\nŹle:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nDobrze:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Walidacja**\n\nPrzenieś walidację z kontrolerów do klas Request.\n\nŹle:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nDobrze:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Logika biznesowa powinna znajdować się w klasie Service**\n\nKontroler musi mieć tylko jedną odpowiedzialność, więc przenieś logikę biznesową z kontrolerów do klas Service.\n\nŹle:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nDobrze:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Nie powtarzaj się (DRY)**\n\nUżywaj kod ponownie, kiedy tylko możesz. SRP pomoże Ci uniknąć duplikatów. Ponadto, ponownie używaj szablonów Blade, używaj scope-ów Eloquent-a itp.\n\nŹle:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nDobrze:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Preferuj używanie modeli Eloquent-a ponad klasy Query Builder lub surowe zapytania SQL. Staraj się używać kolecji zamist tablic**\n\nEloquent pozwala na pisanie czytelnego i łatwego w utrzymaniu kodu. Ponadto, Eloquent ma wbudowane świetne narzędzia, takie jak miękkie usuwanie, zdarzenia, scope-y itp.\n\nŹle:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nDobrze:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Masowe przypisywanie**\n\nŹle:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Dodaj kategorię do artykułu\n$article->category_id = $category->id;\n$article->save();\n```\n\nDobrze:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Nie wykonuj zapytań w szablonach Blade oraz używaj eager loading-u (problem N + 1)**\n\nŹle (dla 100 użytkowników zostanie wykonanych 101 zapytań do bazy danych):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nDobrze (dla 100 użytkowników zostaną wykonane tylko 2 zapytania do bazy danych):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Komentuj swój kod, ale preferuj opisowe nazwy metod i zmiennych zamiast komentarzy**\n\nŹle:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nLepiej:\n\n```php\n// Ustal czy istnieją jakieś join-y\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nDobrze:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Nie umieszczaj kodu JS i CSS w szablonach Blade oraz nie osadzaj żadnego kodu HTML wewnątrz klas PHP.**\n\nŹle:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nLepiej:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nLub\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nW pliku Javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nNajlepszym sposobem jest użycie specialnego obiektu do transferu danych pomiędzy PHP i JS.\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Używaj plików konfiguracyjnych oraz językowych, stałych zamiast tekstu w kodzie**\n\nŹle:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Twój artykuł został dodany!');\n```\n\nDobrze:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Używaj standardowych narzędzi Laravel-a zaakceptowanych przez społeczność**\n\nPreferuj używanie wbudowanych funkcjonalności Laravel-a i paczek społecznościowych zamiast używania paczek i narzędzi innych podmiotów.\nKażdy programista, który będzie pracował z twoją aplikacją w przyszłości, będzie musiał nauczyć się nowych narzędzi.\nPonadto, szanse na uzyskanie pomocy od społeczności Laravel-a są znacznie mniejsze, gdy używasz paczek lub narzędzi innych podmiotów.\nNie każ swojemu klientowi za to płacić.\n\nZadanie | Standardowe narzędzia | Narzędzia innych podmiotów\n------------ | ------------- | -------------\nAutoryzacja | Laravel Policies | Entrust, Sentinel i inne paczki\nKompilowanie zasobów | Laravel Mix, Vite | Grunt, Gulp oraz inne\nŚrodowisko pracy | Laravel Sail, Homestead | Docker\nWdrażanie | Laravel Forge | Deployer i inne rozwiązania\nTesty jednostkowe | PHPUnit, Mockery | Phpspec, Pest\nTesty przeglądarkowe | Laravel Dusk | Codeception\nBaza danych | Eloquent | SQL, Doctrine\nSzablony widoków | Blade | Twig\nPraca z danymi | kolekcje Laravel-a | natywne tablice\nWalidacja formularzy | klasy Request | inne paczki, walidacja w kontrolerze\nUwierzytelnianie | wbudowane | inne paczki, Twoje własne rozwiązanie\nUwierzytelnianie API | Laravel Passport, Laravel Sanctum | inne paczki JWT oraz paczki OAuth\nTworzenie API | wbudowane | Dingo API oraz podobne paczki\nPraca ze strukturą bazy danych | wbudowane migracje | bezpośrednia praca ze strukturą\nLokalizacja | wbudowane | inne paczki\nInterfejsy użytkownika w czasie rzeczywistym | Laravel Echo, Pusher | inne paczki oraz bezpośrednia praca z WebSockets\nGenerowanie danych testowych | klasy Seeder-ów, fabryki modeli, Faker | manualne tworzenie danych testowych\nPlanowanie zadań | Laravel Task Scheduler | skrypty oraz inne paczki\nBaza danych | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Postępuj zgodnie z konwencją nazewniczą Laravel-a**\n\nStosuj [standardy PSR](https://www.php-fig.org/psr/psr-12/).\n\nPrzestrzegaj również konwencji nazewniczych przyjętych przez społeczność Laravel-a\n\nZagadnienie | Konwencja | Dobrze | Źle\n------------ | ------------- | ------------- | -------------\nKontrolery | liczba pojedyncza | ArticleController | ~~ArticlesController~~\nŚcieżka URL | liczba mnoga | articles/1 | ~~article/1~~\nNazwana ścieżka URL | snake_case wraz z notacją kropkową | users.show_active | ~~users.show-active, show-active-users~~\nModel | liczba pojedyncza | User | ~~Users~~\nrelacje hasOne lub belongsTo | liczba pojedyncza | articleComment | ~~articleComments, article_comment~~\nWszystkie pozostałe relacje | liczba mnoga | articleComments | ~~articleComment, article_comments~~\nTable | liczba mnoga | article_comments | ~~article_comment, articleComments~~\nTabela przestawna (Pivot) | nazwy modeli w liczbie pojedynczej w kolejności alfabetycznej | article_user | ~~user_article, articles_users~~\nKolumna w tabeli | snake_case bez nazwy modelu | meta_title | ~~MetaTitle; article_meta_title~~\nWłaściwość modelu | snake_case | $model->created_at | ~~$model->createdAt~~\nKlucz obcy | nazwa modelu w liczbie pojedynczej z przyrostkiem _id | article_id | ~~ArticleId, id_article, articles_id~~\nKlucz podstawowy | - | id | ~~custom_id~~\nMigracja | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMetoda | camelCase | getAll | ~~get_all~~\nMetoda w kontrolerze zasobu | [zobacz tabelę](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMetoda w klasie testowania | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nZmienna | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nKolekcja | opisowo, liczba mnoga | $activeUsers = User::active()->get() | ~~$active, $data~~\nObiekt | opisowo, liczba pojedyncza | $activeUser = User::active()->first() | ~~$users, $obj~~\nIndeks plików konfiguracyjnych i językowych | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nWidok | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nPliki konfiguracyjne | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nKontrakt (interfejs) | przymiotnik lub rzeczownik | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nCecha (trait) | przymiotnik | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **W miarę możliwości używaj krótszej i bardziej czytelnej składni**\n\nŹle:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nDobrze:\n\n```php\nsession('cart');\n$request->name;\n```\n\nWięcej przykładów:\n\nPowszechna składnia | Krótsza i bardziej czytelna składnia\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id` (w PHP 8: `$object->relation?->id`)\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Użyj kontenera IoC lub fasad zamiast nowych klas**\n\nSkładnia tworzenia nowych klas tworzy ścisłe sprzężenie pomiędzy nimi i komplikuje testowanie.\nZamiast tego używaj kontenera IoC lub fasad.\n\nŹle:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nDobrze:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Nie pobieraj wartości z pliku `.env` bezpośrednio**\n\nZamiast tego wstaw je do plików konfiguracyjnych, a następnie użyj funkcji pomocniczej `config()` aby użyć tych danych w aplikacji.\n\nŹle:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nDobrze:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Użyj danych\n$apiKey = config('api.key');\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Przechowuj daty w standardowym formacie. Używaj akcesorów i mutatorów do modyfikacji formatów.**\n\nŹle:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nDobrze:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// Widok\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Wróć do spisu treści](#spis-treści)\n\n### **Inne dobre praktyki**\n\nNigdy nie umieszczaj żadnej logiki w plikach ścieżek URL (routes/*.php).\n\nZminimalizuj użycie natywnego kodu PHP w szablonach Blade.\n\n[🔝 Wróć do spisu treści](#spis-treści)\n"
  },
  {
    "path": "romanian.md",
    "content": "![Practici recomandate Laravel](/images/logo-english.png?raw=true)\n\nTe poate interesa si [exemplul real de aplicație Laravel](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nTraduceri:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[Română](romanian.md) (by [als698](https://github.com/als698))\n\n\n[![Exemplu aplicație Laravel](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Cuprins\n\n[Principiul responsabilității unice](#single-responsibility-principle)\n\n[Modele voluminoase, controllere scurte](#fat-models-skinny-controllers)\n\n[Validare](#validation)\n\n[Păstrează functiile logice in clase de serviciu](#business-logic-should-be-in-service-class)\n\n[Evita duplicarea codului (DRY)](#dont-repeat-yourself-dry)\n\n[Preferă utilizarea Eloquent în locul Query Builder și a interogărilor SQL brute. Preferă colecțiile în locul matricilor](#prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays)\n\n[Atribuire in masă](#mass-assignment)\n\n[Evită executarea interogărilor în template-urile Blade și utilizează încărcarea eager (problema N + 1)](#do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem)\n\n[Fragmentează datele pentru sarcini care necesită multe date](#chunk-data-for-data-heavy-tasks)\n\n[Preferă denumiri descriptive pentru metode și variabile în loc de comentarii](#comment-your-code-but-prefer-descriptive-method-and-variable-names-over-comments)\n\n[Nu include cod JS și CSS în șabloanele Blade și nu include HTML în clasele PHP](#do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes)\n\n[Utilizează fișierele de configurație și de limbă, folosește constante în loc de text în cod](#use-config-and-language-files-constants-instead-of-text-in-the-code)\n\n[Utilizează instrumentele standard Laravel acceptate de comunitate](#use-standard-laravel-tools-accepted-by-community)\n\n[Respectă convențiile de denumire Laravel](#follow-laravel-naming-conventions)\n\n[Folosește sintaxa mai scurtă și mai ușor de citit, acolo unde este posibil](#use-shorter-and-more-readable-syntax-where-possible)\n\n[Folosește Containerul IoC / Service în loc de new Class](#use-ioc-container-or-facades-instead-of-new-class)\n\n[Evită să stochezi date direct în fișierele `.env`](#do-not-get-data-from-the-env-file-directly)\n\n[Stochează datele în format standard. Folosește accesorii și mutatori pentru a modifica formatul datelor](#store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format)\n\n[Alte bune practici](#other-good-practices)\n\n### **Principiul responsabilității unice**\n\nO clasă și o metodă ar trebui să aibă doar o singură responsabilitate.\n\nGreșit:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nCorect:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Modele voluminoase, controllere scurte**\n\nMută toată logica legată de baza de date în modelele Eloquent.\n\nGreșit:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nCorect:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders(): Collection\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Validare**\n\nMută validarea din controllere în clase de tip Request.\n\nGreșit:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nCorect:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules(): array\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Păstrează functiile logice in clase de serviciu**\n\nUn controller trebuie să aibă doar o singură responsabilitate, așa că mută logica aplicației din controllere în clase de serviciu.\n\nGreșit:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nCorect:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image): void\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Evita duplicarea codului (DRY)**\n\nReutilizează codul atunci când poți. SRP te ajută să eviți duplicarea. De asemenea, reutilizează template-urile Blade, utilizează Eloquent scopes, etc.\n\nGreșit:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nCorect:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', true)->whereNotNull('deleted_at');\n}\n\npublic function getActive(): Collection\n{\n    return $this->active()->get();\n}\n\npublic function getArticles(): Collection\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Preferă utilizarea Eloquent în locul Query Builder și a interogărilor SQL brute. Preferă colecțiile în locul matricilor**\n\nEloquent îți permite să scrii cod ușor de citit și întreținut. De asemenea, Eloquent dispune de unelte încorporate excelente, cum ar fi ștergerile soft, evenimentele, domeniile etc.\n\nGreșit:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nCorect:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Atribuire în masă**\n\nGreșit:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nCorect:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Evită executarea interogărilor în template-urile Blade și utilizează încărcarea eager (problema N + 1)**\n\nGreșit (pentru 100 de utilizatori, vor fi executate 101 interogări în baza de date):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nCorect (pentru 100 de utilizatori, vor fi executate doar 2 interogări în baza de date):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Fragmentează datele pentru sarcini care necesită multe date**\n\nGreșit:\n\n```php\n$users = $this->get();\n\nforeach ($users as $user) {\n    ...\n}\n```\n\nCorect:\n\n```php\n$this->chunk(500, function ($users) {\n    foreach ($users as $user) {\n        ...\n    }\n});\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Preferă denumiri descriptive pentru metode și variabile în loc de comentarii**\n\nGreșit:\n\n```php\n// Determină dacă există vreun join\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nCorect:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Nu include cod JS și CSS în șabloanele Blade și nu include HTML în clasele PHP**\n\nGreșit:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nCorect:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nAlternativ:\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nÎntr-un fișier Javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nCea mai bună metodă este de a utiliza un pachet specializat PHP către JS pentru a transfera datele.\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Utilizează fișierele de configurație și de limbă, folosește constante în loc de text în cod**\n\nGreșit:\n\n```php\npublic function isNormal(): bool\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nCorect:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Utilizează instrumentele standard Laravel acceptate de comunitate**\n\nPreferă să utilizezi funcționalitățile integrate ale Laravel și pachetele comunității în locul folosirii pachetelor și instrumentelor terțe. Orice dezvoltator care va lucra cu aplicația ta în viitor va trebui să învețe noi instrumente. De asemenea, șansele de a primi ajutor din partea comunității Laravel sunt semnificativ mai mici atunci când folosești un pachet sau un instrument terț. Nu face clientul să plătească pentru asta.\n\nTask | Instrumente standard | Instrumente terțe\n------------ | ------------- | -------------\nAutorizare | Politici | Entrust, Sentinel și alte pachete\nCompilarea asset-urilor | Laravel Mix, Vite | Grunt, Gulp, pachete terțe\nMediu de dezvoltare | Laravel Sail, Homestead | Docker\nImplementare | Laravel Forge | Deployer și alte soluții\nTestare unitară | PHPUnit, Mockery | Phpspec, Pest\nTestare în browser | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nȘabloane | Blade | Twig\nLucrul cu date | Laravel collections | Matrice\nValidarea formularului | Clasele Request | Pachete terțe, validarea în controlor\nAutentificare | Încorporată | Pachete terțe, soluția proprie\nAutentificare API | Laravel Passport, Laravel Sanctum | Pachete terțe JWT și OAuth\nCrearea API-ului | Încorporată | Dingo API și pachete similare\nLucrul cu structura DB-ului | Migrații | Lucrul direct cu structura DB-ului\nLocalizare | Încorporată | Pachete terțe\nInterfețe în timp real pentru utilizatori | Laravel Echo, Pusher | Pachete terțe și lucrul direct cu WebSockets\nGenerarea datelor de testare | Clase Seeder, Fabrici de modele, Faker | Crearea manuală a datelor de testare\nProgramarea sarcinilor | Planificator de sarcini Laravel | Scripturi și pachete terțe\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Respectă convențiile de denumire Laravel**\n\nRespectă standardele [PSR](https://www.php-fig.org/psr/psr-12/).\n\nDe asemenea, respectă convențiile de denumire acceptate de comunitatea Laravel:\n\nCe | Cum | Corect | Greșit\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nNume rută | snake_case cu notație punctată | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nRelație hasOne sau belongsTo | singular | articleComment | ~~articleComments, article_comment~~\nToate celelalte relații | plural | articleComments | ~~articleComment, article_comments~~\nTabel | plural | article_comments | ~~article_comment, articleComments~~\nTabel pivot | nume model singular în ordine alfabetică | article_user | ~~user_article, articles_users~~\nColoană tabel | snake_case fără numele modelului | meta_title | ~~MetaTitle; article_meta_title~~\nProprietate model | snake_case | $model->created_at | ~~$model->createdAt~~\nCheie străină | nume model singular cu sufixul _id | article_id | ~~ArticleId, id_article, articles_id~~\nCheie primară | - | id | ~~custom_id~~\nMigrare | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMetodă | camelCase | getAll | ~~get_all~~\nMetodă în controller-ul de resurse | [tabel](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMetodă în clasă de testare | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariabilă | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nColecție | descripțivă, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObiect | descripțiv, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nFișiere de configurație și limbă - index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nVizualizare | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfigurație | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interfață) | adjectiv sau substantiv | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjectiv | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjectiv | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Folosește sintaxa mai scurtă și mai ușor de citit, acolo unde este posibil**\n\nGreșit:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nCorect:\n\n```php\nsession('cart');\n$request->name;\n```\n\nMai multe exemple:\n\nSintaxă comună | Sintaxă mai scurtă și mai ușor de citit\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id` (în PHP 8: `$object->relation?->id`)\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Folosește Containerul IoC / Service în loc de new Class**\n\nSintaxa new Class creează un cuplaj strâns între clase și complică testarea. Folosește Containerul IoC / Service pentru a rezolva dependințele.\n\nGreșit:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nCorect:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\nSau mai bine, utilizează injecția de dependențe:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\npublic function store(Request $request)\n{\n    $this->user->create($request->all());\n}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Evită să stochezi date direct în fișierele `.env`**\n\nÎn loc să preiei datele direct din fișierul `.env`, este mai indicat să le treci în fișierele de configurare și să utilizezi funcția ajutătoare `config()` pentru a accesa aceste date în aplicație.\n\nGreșit:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nCorect:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Stochează datele în format standard. Folosește accesorii și mutatori pentru a modifica formatul datelor**\n\nUn șir de caractere ca dată este mai puțin fiabil decât o instanță de obiect, cum ar fi o instanță Carbon. Se recomandă trecerea obiectelor Carbon între clase în locul șirurilor de caractere. Redarea ar trebui să se facă în stratul de prezentare (template-uri):\n\nGreșit:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nCorect:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\n// Blade view\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->format('m-d') }}\n```\n\n[🔝 Înapoi la cuprins](#contents)\n\n### **Alte bune practici**\n\nEvită utilizarea de modele și instrumente străine pentru Laravel și framework-urile similare (cum ar fi RoR, Django). Dacă îți place abordarea Symfony (sau Spring) pentru construirea aplicațiilor, este o idee bună să folosești aceste framework-uri în schimb.\n\nNu pune niciodată logica în fișierele de rute.\n\nMinimizează utilizarea PHP vanilla în template-urile Blade.\n\nFolosește o bază de date în memorie pentru teste.\n\nEvită suprascrierea funcționalităților standard ale framework-ului pentru a evita probleme legate de actualizarea versiunii framework-ului și multe alte probleme.\n\nFolosește sintaxa PHP modernă acolo unde este posibil, dar nu uita de lizibilitate.\n\nEvită utilizarea View Composers și a altor instrumente similare, cu excepția cazului în care cunoști cu adevărat ce faci. În majoritatea cazurilor, există o modalitate mai bună de rezolvare a problemei.\n\n[🔝 Înapoi la cuprins](#contents)\n"
  },
  {
    "path": "russian.md",
    "content": "![Хорошие практики Laravel](/images/logo-russian.png?raw=true)\n\nВозможно, вам также будет интересно взглянуть на [пример Laravel приложения](https://github.com/alexeymezenin/laravel-realworld-example-app) и на [SQL запросы, генерируемые Eloquent ORM](https://github.com/alexeymezenin/eloquent-sql-reference)\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Содержание\n\n[Принцип единственной ответственности (Single responsibility principle)](#Принцип-единственной-ответственности-single-responsibility-principle)\n\n[Методы должны делать что-то одно](#Методы-должны-делать-что-то-одно)\n\n[Тонкие контроллеры, толстые модели](#Тонкие-контроллеры-толстые-модели)\n\n[Валидация](#Валидация)\n\n[Бизнес логика в сервис-классах](#Бизнес-логика-в-сервис-классах)\n\n[Не повторяйся (DRY)](#Не-повторяйся-dry)\n\n[Предпочитайте Eloquent конструктору запросов (query builder) и сырым запросам в БД. Предпочитайте работу с коллекциями работе с массивами](#Предпочитайте-eloquent-конструктору-запросов-query-builder-и-сырым-запросам-в-БД-Предпочитайте-работу-с-коллекциями-работе-с-массивами)\n\n[Используйте массовое заполнение (mass assignment)](#Используйте-массовое-заполнение-mass-assignment)\n\n[Не выполняйте запросы в представлениях и используйте нетерпеливую загрузку (проблема N + 1)](#Не-выполняйте-запросы-в-представлениях-и-используйте-нетерпеливую-загрузку-проблема-n--1)\n\n[Используйте метод chunk при работе с большим количеством данных](#используйте-метод-chunk-при-работе-с-большим-количеством-данных)\n\n[Комментируйте код, предпочитайте читаемые имена методов комментариям](#Комментируйте-код-предпочитайте-читаемые-имена-методов-комментариям)\n\n[Выносите JS и CSS из шаблонов Blade и HTML из PHP кода](#Выносите-js-и-css-из-шаблонов-blade-и-html-из-php-кода)\n\n[Используйте инструменты и практики принятые сообществом](#Используйте-инструменты-и-практики-принятые-сообществом)\n\n[Соблюдайте соглашения сообщества об именовании](#Соблюдайте-соглашения-сообщества-об-именовании)\n\n[Конфиги, языковые файлы и константы вместо текста в коде](#Конфиги-языковые-файлы-и-константы-вместо-текста-в-коде)\n\n[Короткий и читаемый синтаксис там, где это возможно](#Короткий-и-читаемый-синтаксис-там-где-это-возможно)\n\n[Используйте IoC или фасады вместо new Class](#Используйте-ioc-или-фасады-вместо-new-class)\n\n[Не работайте с данными из файла `.env` напрямую](#Не-работайте-с-данными-из-файла-env-напрямую)\n\n[العربية](persian.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[Храните даты в стандартном формате. Используйте читатели и преобразователи для преобразования формата](#Храните-даты-в-стандартном-формате-Используйте-читатели-и-преобразователи-для-преобразования-формата)\n\n[Другие советы и практики](#Другие-советы-и-практики)\n\n### **Принцип единственной ответственности (Single responsibility principle)**\n\nКаждый класс должен иметь только одну обязанность.\n\nПлохо:\n\n```php\npublic function update(Request $request): string\n{\n    $validated = $request->validate([\n        'title' => 'required|max:255',\n        'events' => 'required|array:date,type'\n    ]);\n\n    foreach ($request->events as $event) {\n        $date = $this->carbon->parse($event['date'])->toString();\n\n        $this->logger->log('Update event ' . $date . ' :: ' . $);\n    }\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\nХорошо:\n\n```php\npublic function update(UpdateRequest $request): string\n{\n    $this->logService->logEvents($request->events);\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n\n### **Методы должны делать что-то одно**\n\nФункция должна делать что-то одно и делать это хорошо.\n\nПлохо:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nХорошо:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Тонкие контроллеры, толстые модели**\n\nВыносите работу с данными в модели.\n\nПлохо:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nХорошо:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders(): Collection\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Валидация**\n\nСледуя принципам тонкого контроллера и SRP, выносите валидацию из контроллера в Request классы.\n\nПлохо:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nХорошо:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules(): array\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Бизнес логика в сервис-классах**\n\nКонтроллер должен выполнять только свои прямые обязанности, поэтому выносите всю бизнес логику в отдельные классы и сервис классы.\n\nПлохо:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nХорошо:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image): void\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Не повторяйся (DRY)**\n\nЭтот принцип призывает вас переиспользовать код везде, где это возможно. Если вы следуете принципу SRP, вы уже избегаете повторений, но Laravel позволяет вам также переиспользовать представления, части Eloquent запросов и т.д.\n\nПлохо:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nХорошо:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', true)->whereNotNull('deleted_at');\n}\n\npublic function getActive(): Collection\n{\n    return $this->active()->get();\n}\n\npublic function getArticles(): Collection\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Предпочитайте Eloquent конструктору запросов (query builder) и сырым запросам в БД. Предпочитайте работу с коллекциями работе с массивами**\n\nEloquent позволяет писать максимально читаемый код, а изменять функционал приложения несоизмеримо легче. У Eloquent также есть ряд удобных и мощных инструментов. Вам может быть интересен [справочник перевода Eloquent запросов в SQL](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nПлохо:\n\n```php\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nХорошо:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Используйте массовое заполнение (mass assignment)**\n\nПлохо:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Привязать статью к категории.\n$article->category_id = $category->id;\n$article->save();\n```\n\nХорошо:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Не выполняйте запросы в представлениях и используйте нетерпеливую загрузку (проблема N + 1)**\n\nПлохо (будет выполнен 101 запрос в БД для 100 пользователей):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nХорошо (будет выполнено 2 запроса в БД для 100 пользователей):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Используйте метод chunk при работе с большим количеством данных**\n\nBad:\n\n```php\n$users = $this->get();\n\nforeach ($users as $user) {\n    ...\n}\n```\n\nGood:\n\n```php\n$this->chunk(500, function ($users) {\n    foreach ($users as $user) {\n        ...\n    }\n});\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Предпочитайте читаемые имена переменных и методов комментариям**\n\nПлохо:\n\n```php\n// Determine if there are any joins\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nХорошо:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Выносите JS и CSS из шаблонов Blade и HTML из PHP кода**\n\nПлохо:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nЛучше:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nИли\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nВ Javascript файле:\n\n```javascript\nlet article = $('#article').val();\n```\n\nЕще лучше использовать специализированный пакет для передачи данных из бэкенда во фронтенд.\n\n[🔝 Наверх](#Содержание)\n\n### **Конфиги, языковые файлы и константы вместо текста в коде**\n\nНепосредственно в коде не должно быть никакого текста.\n\nПлохо:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Ваша статья была успешно добавлена');\n```\n\nХорошо:\n\n```php\npublic function isNormal(): bool\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Используйте инструменты и практики принятые сообществом**\n\nLaravel имеет встроенные инструменты для решения часто встречаемых задач. Предпочитайте пользоваться ими использованию сторонних пакетов и инструментов. Laravel разработчику, пришедшему в проект после вас, придется изучать и работать с новым для него инструментом, со всеми вытекающими последствиями. Получить помощь от сообщества будет также гораздо труднее. Не заставляйте клиента или работодателя платить за ваши велосипеды.\n\nЗадача | Стандартные инструмент | Нестандартные инструмент\n------------ | ------------- | -------------\nАвторизация | Политики | Entrust, Sentinel и др. пакеты, собственное решение\nРабота с JS, CSS и пр. | Laravel Mix, Vite | Grunt, Gulp, сторонние пакеты\nСреда разработки | Laravel Sail, Homestead | Docker\nРазворачивание приложений | Laravel Forge | Deployer и многие другие\nТестирование | Phpunit, Mockery | Phpspec, Pest\ne2e тестирование | Laravel Dusk | Codeception\nРабота с БД | Eloquent | SQL, построитель запросов, Doctrine\nШаблоны | Blade | Twig\nРабота с данными | Коллекции Laravel | Массивы\nВалидация форм | Request классы | Сторонние пакеты, валидация в контроллере\nАутентификация | Встроенный функционал | Сторонние пакеты, собственное решение\nАутентификация API | Laravel Passport, Laravel Sanctum | Сторонние пакеты, использующие JWT, OAuth\nСоздание API | Встроенный функционал | Dingo API и другие пакеты\nРабота со структурой БД | Миграции | Работа с БД напрямую\nЛокализация | Встроенный функционал | Сторонние пакеты\nОбмен данными в реальном времени | Laravel Echo, Pusher | Пакеты и работа с веб сокетами напрямую\nГенерация тестовых данных | Seeder классы, фабрики моделей, Faker | Ручное заполнение и пакеты\nПланирование задач | Планировщик задач Laravel | Скрипты и сторонние пакеты\nБД | MySQL, PostgreSQL, SQLite, SQL Server | MongoDb\n\n[🔝 Наверх](#Содержание)\n\n### **Соблюдайте соглашения сообщества об именовании**\n\nСледуйте [стандартам PSR](https://www.php-fig.org/psr/psr-12/) при написании кода.\n\nТакже, соблюдайте другие cоглашения об именовании:\n\nЧто | Правило | Принято | Не принято\n------------ | ------------- | ------------- | -------------\nКонтроллер | ед. ч. | ArticleController | ~~ArticlesController~~\nМаршруты | мн. ч. | articles/1 | ~~article/1~~\nИмена маршрутов | snake_case | users.show_active | ~~users.show-active, show-active-users~~\nМодель | ед. ч. | User | ~~Users~~\nОтношения hasOne и belongsTo | ед. ч. | articleComment | ~~articleComments, article_comment~~\nВсе остальные отношения | мн. ч. | articleComments | ~~articleComment, article_comments~~\nТаблица | мн. ч. | article_comments | ~~article_comment, articleComments~~\nPivot таблица | имена моделей в алфавитном порядке в ед. ч. | article_user | ~~user_article, articles_users~~\nСтолбец в таблице | snake_case без имени модели | meta_title | ~~MetaTitle; article_meta_title~~\nСвойство модели | snake_case | $model->created_at | ~~$model->createdAt~~\nВнешний ключ | имя модели ед. ч. и _id | article_id | ~~ArticleId, id_article, articles_id~~\nПервичный ключ | - | id | ~~custom_id~~\nМиграция | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nМетод | camelCase | getAll | ~~get_all~~\nМетод в контроллере ресурсов | [таблица](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nМетод в тесте | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nПеременные | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nКоллекция | описательное, мн. ч. | $activeUsers = User::active()->get() | ~~$active, $data~~\nОбъект | описательное, ед. ч. | $activeUser = User::active()->first() | ~~$users, $obj~~\nИндексы в конфиге и языковых файлах | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nПредставление | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nКонфигурационный файл | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nКонтракт (интерфейс) | прилагательное или существительное | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nТрейт | прилагательное | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | единственное число | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Наверх](#Содержание)\n\n### **Приоритет соглашений над конфигурацией**\n\nПока вы следуете принятым соглашениям, вам не нужно добавлять в код дополнительную конфигурацию.\n\nПлохо:\n\n```php\n// Название таблицы 'Customer'\n// Первичный ключ 'customer_id'\nclass Customer extends Model\n{\n    const CREATED_AT = 'created_at';\n    const UPDATED_AT = 'updated_at';\n\n    protected $table = 'Customer';\n    protected $primaryKey = 'customer_id';\n\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');\n    }\n}\n```\n\nХорошо:\n\n```php\n// Название таблицы 'customers'\n// Первичный ключ 'id'\nclass Customer extends Model\n{\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class);\n    }\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Короткий и читаемый синтаксис там, где это возможно**\n\nПлохо:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nХорошо:\n\n```php\nsession('cart');\n$request->name;\n```\n\nЕще примеры:\n\nЧасто используемый синтаксис | Более короткий и читаемый синтаксис\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Наверх](#Содержание)\n\n### **Используйте IoC или фасады вместо new Class**\n\nВнедрение классов через синтаксис new Class создает сильное сопряжение между частями приложения и усложняет тестирование. Используйте контейнер или фасады.\n\nПлохо:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nХорошо:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Не работайте с данными из файла `.env` напрямую**\n\nПередайте данные из `.env` файла в кофигурационный файл и используйте `config()` в приложении, чтобы использовать эти данными.\n\nПлохо:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nХорошо:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Используйте данные в приложении\n$apiKey = config('api.key');\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Храните даты в стандартном формате. Используйте читатели и преобразователи для преобразования формата**\n\nПлохо:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nХорошо:\n\n```php\n// Модель\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n// Читатель (accessor)\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// Шаблон\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Не используйте DocBlock**\n\nDocBlock ухудшают читаемость кода. Вместо них используйте хорошие именя для методов и современный синтаксис PHP, например описание возвращаемых типов (return type hints).\n\nПлохо:\n\n```php\n/**\n * Функция проверяет есть ли в строке символы, которые остутствуют в ASCII.\n *\n * @param string $string Строка, которую мы получаем с фронтенда и которая\n *                       может содержать символы, не входящие в ASCII.\n *                       Возвращает True, если таких символов в строке нет.\n *\n * @return bool\n * @author  Василий Иванов\n *\n * @license GPL\n */\n\npublic function checkString($string)\n{\n}\n```\n\nХорошо:\n\n```php\npublic function isValidAsciiString(string $string): bool\n{\n}\n```\n\n[🔝 Наверх](#Содержание)\n\n### **Другие советы и практики**\n\nНе используйте паттерны и инструменты чужеродные по отношению к Laravel и подобным фреймворкам (RoR, Django). Если вам нравятся подходы, используемые в Symfony (Spring и др.), использовать эти фреймворки для создания веб приложений будет намного разумнее.\n\nНе размещайте логику в маршрутах.\n\nСтарайтесь не использовать \"сырой\" PHP в шаблонах Blade.\n\nИспользуйте базу данных, размещенную в памяти (in-memory DB) при тестировании.\n\nНе меняйте стандартные инструменты фреймворка, иначе у вас могут возникнуть проблемы при обновлении фреймворка и другие сложности.\n\nИспользуйте современный синтаксис PHP, но при этом не забывайте, что читаемость важнее.\n\nИспользуйте такие инструменты, как View Composers, с большой осторожностью. В большинстве случаев, есть возможность найти другое решение проблемы.\n\n[🔝 Наверх](#Содержание)\n"
  },
  {
    "path": "spanish.md",
    "content": "![Laravel best practices](/images/logo-spanish.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nTraducciones:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (por [Protoqol](https://github.com/Protoqol))\n\n[Indonesia](indonesia.md) (por [P0rguy](https://github.com/p0rguy), [Doni Ahmad](https://github.com/donyahmd))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (por [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (por [2bo](https://github.com/2bo))\n\n[简体中文](chinese.md) (por [xiaoyi](https://github.com/Shiloh520))\n\n[繁體中文](traditional-chinese.md) (por [woeichern](https://github.com/woeichern))\n\n[ภาษาไทย](thai.md) (por [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (por [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (por [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (por [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (por [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (por [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (por [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (por [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (por [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (por [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (por [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (por [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (por [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Índice de contenido\n\n[Principio de propósito único](#principio-de-propósito-único)\n\n[Modelos gordos, controladores delgados](#modelos-gordos-controladores-delgados)\n\n[Validación](#validación)\n\n[La lógica de negocios debe estar en una clase de servicio](#la-lógica-de-negocios-debe-estar-en-una-clase-de-servicio)\n\n[No te repitas (DRY)](#no-te-repitas-dry)\n\n[Prioriza el uso de Eloquent por sobre el constructor de consultas y consultas puras. Prioriza las colecciones sobre los arreglos](#prioriza-el-uso-de-eloquent-por-sobre-el-constructor-de-consultas-y-consultas-puras-prioriza-las-colecciones-sobre-los-arreglos)\n\n[Asignación en masa](#asignación-en-masa)\n\n[No ejecutes consultas en las plantillas Blade y utiliza el cargado prematuro (Problema N + 1)](#no-ejecutes-consultas-en-las-plantillas-blade-y-utiliza-el-cargado-prematuro-problema-n--1))\n\n[Comenta tu código, pero prioriza los métodos y nombres de variables descriptivas por sobre los comentarios](#comenta-tu-código-pero-prioriza-los-métodos-y-nombres-de-variables-descriptivas-por-sobre-los-comentarios)\n\n[No coloques JS ni CSS en las plantillas Blade y no coloques HTML en clases de PHP](#no-coloques-js-ni-css-en-las-plantillas-blade-y-no-coloques-html-en-clases-de-php)\n\n[Utiliza los archivos de configuración y lenguaje en lugar de texto en el código](#utiliza-los-archivos-de-configuración-y-lenguaje-en-lugar-de-texto-en-el-código)\n\n[Utiliza las herramientas estándar de Laravel aceptadas por la comunidad](#utiliza-las-herramientas-estándar-de-laravel-aceptadas-por-la-comunidad)\n\n[Sigue la convención de Laravel para los nombres](#sigue-la-convención-de-laravel-para-los-nombres)\n\n[Utiliza sintaxis cortas y legibles siempre que sea posible](#utiliza-sintaxis-cortas-y-legibles-siempre-que-sea-posible)\n\n[Utiliza contenedores IoC o fachadas en lugar de new Class](#utiliza-contenedores-ioc-o-fachadas-en-lugar-de-new-class)\n\n[No saques información directamente del archivo .env](#no-saques-información-directamente-del-archivo-env)\n\n[Guarda las fechas en los formatos estándares. Utiliza los accessors y mutators para modificar el formato](#guarda-las-fechas-en-los-formatos-estándares-utiliza-los-accessors-y-mutators-para-modificar-el-formato)\n\n[Otras buenas prácticas](#otras-buenas-prácticas)\n\n### **Principio de propósito único**\n\nLas clases y los métodos deben tener un solo propósito.\n\nMalo:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nBueno:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Modelos gordos, controladores delgados**\n\nColoca toda la lógica relacionada a la base de datos en los modelos de Eloquent o en una clase Repositorio si estás utilizando el constructor de consultas o consultas SQL puras.\n\nMalo:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nBueno:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Validación**\n\nQuita las validaciones de los controladores y colócalas en clases Request\n\nMalo:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nBueno:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **La lógica de negocios debe estar en una clase de servicio**\n\nUn controlador solo debe tener un propósito, así que mueve la lógica de negocio fuera de los controladores y colócala en clases de servicio.\n\nMalo:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nBueno:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **No te repitas (DRY)**\n\nReutiliza código cada vez que puedas.  El SRP (Principio de Propósito Único) te ayuda a evitar la duplicación. Reutiliza también las plantillas Blade, utiliza scopes de Eloquent, etcétera.\n\nMalo:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nBueno:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Prioriza el uso de Eloquent por sobre el constructor de consultas y consultas puras. Prioriza las colecciones sobre los arreglos**\n\nEloquent te permite escribir código legible y mantenible. Eloquent también tiene muy buenas herramientas preconstruidas como los borrados leves, eventos, scopes, etcétera.\n\nMalo:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nBueno:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Asignación en masa**\n\nMalo:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nBueno:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **No ejecutes consultas en las plantillas Blade y utiliza el cargado prematuro (Problema N + 1)**\n\nMalo (Para 100 usuarios, se ejecutarán 101 consultas):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nBueno (Para 100 usuarios, se ejecutarán 2 consultas):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Comenta tu código, pero prioriza los métodos y nombres de variables descriptivas por sobre los comentarios**\n\nMalo:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nMejor:\n\n```php\n// Determina si hay alguna unión\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nBueno:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **No coloques JS ni CSS en las plantillas Blade y no coloques HTML en clases de PHP**\n\nMalo:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nMejor:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nEn un archivo JavaScript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nLa mejor ruta es utilizar algún paquete especializado para transferir información de PHP a JS.\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Utiliza los archivos de configuración y lenguaje en lugar de texto en el código**\n\nMalo:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('mensaje', '¡Su artículo ha sido agregado!');\n```\n\nBueno:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('mensaje', __('app.articulo_agregado'));\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Utiliza las herramientas estandar de Laravel aceptadas por la comunidad**\n\nPrioriza la utilización de funcionalidades integradas y los paquetes de la comunidad en lugar de utilizar paquetes o herramientas de terceros ya que cualquier desarrollador que vaya a trabajar a futuro en tu aplicación necesitará aprender a utilizar nuevas herramientas. También, las probabilidades de recibir ayuda de la comunidad son significativamente más bajas cuando utilizas herramientas o paquetes de terceros. No hagas que tu cliente pague por ello.\n\nTarea | Herramienta estándar | Herramientas de terceras personas\n------------ | ------------- | -------------\nAutorización | Policies | Entrust, Sentinel y otros paquetes\nCompilar assets | Laravel Mix, Vite | Grunt, Gulp, paquetes de terceros\nEntorno de desarrollo | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer y otras soluciones\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nTesteo en el navegador | Laravel Dusk | Codeception\nBase de datos | Eloquent | SQL, Doctrine\nPlantillas | Blade | Twig\nTrabajar con data | Laravel collections | Arreglos\nValidación de formularios | Clases Request | Paquetes de terceros, validación en el controlador\nAutenticación | Integrada | Paquetes de terceros, solución propia\nAutenticación para API's | Laravel Passport, Laravel Sanctum | Paquetes oAuth y JWT de terceros\nCreación de API's | Integrado | Dingo API y paquetes similares\nEstructura de la base de datos | Migraciones | Trabajar directamente con la estructura\nLocalización | Integrada | Paquetes de terceros\nInterfaces en tiempo real | Laravel Echo, Pusher | Paquetes de terceros y trabajar directamente con WebSockets.\nGeneración de información de prueba | Clases Seeder, Fábricas de modelos, Faker | Crear la información manualmente\nProgramación de tareas | Laravel Task Scheduler | Scripts y paquetes de terceros\nBase de datos | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Sigue la convención de Laravel para los nombres**\n\nSigue los [estándares PSR](https://www.php-fig.org/psr/psr-12/).\n\nTambién, sigue la convención aceptada por la comunidad:\n\nQué | Cómo | Bueno | Malo\n------------ | ------------- | ------------- | -------------\nControlador | singular | ControladorArticulo | ~~ControladorArticulos~~\nRuta | plural | articulos/1 | ~~articulo/1~~\nNombres de rutas | snake_case con notación de puntos | usuarios.mostrar_activos | ~~usuarios.mostrar-activos, mostrar-usuarios-activos~~\nModelo | singular | Usuario | ~~Usuarios~~\nRelaciones hasOne o belongsTo | singular | comentarioArticulo | ~~comentariosArticulo, comentario_articulo~~\nCualquier otra relación | plural | comentariosArticulo | ~~comentarioArticulo, comentarios_articulo~~\nTabla | plural | comentarios_articulo | ~~comentario_articulo, comentariosArticulo~~\nTabla de pivote | Nombres de modelos en singular y en orden alfabético | articulo_usuario | ~~usuario_articulo, articulos_usuarios~~\nColumna de tabla | snake_case sin el nombre del modelo | meta_titulo | ~~MetaTitulo; articulo_meta_titulo~~\nPropiedad de modelo | snake_case | $model->created_at | ~~$model->createdAt~~\nClave foránea | Nombre en singular del modelo con el sufijo _id | articulo_id | ~~articuloId, id_articulo, articulos_id~~\nClave primaria | - | id | ~~id_personalizado~~\nMigración | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMétodo | camelCase | traerTodo | ~~traer_todo~~\nMétodo en controlador de recursos | [table](https://laravel.com/docs/master/controllers#resource-controllers) | guardar | ~~guardarArticulo~~\nMétodo en clase de pruebas | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articulosConAutor | ~~$articulos_con_autor~~\nColección | descriptiva, plural | $usuariosActivos = Usuario::active()->get() | ~~$activo, $data~~\nObjeto | descriptivo, singular | $usuarioActivo = Usuario::active()->first() | ~~$usuarios, $obj~~\nÍndice de archivos de configuración y lenguaje | snake_case | articulos_habilitados | ~~articulosHabilitados; articulos-habilitados~~\nVistas | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfiguración | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContrato (interface) | adjetivo o sustantivo | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjetivo | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Utiliza sintaxis cortas y legibles siempre que sea posible**\n\nMalo:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nBueno:\n\n```php\nsession('cart');\n$request->name;\n```\n\nMás ejemplos\n\nSintaxis común | Sintaxis corta y legible\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Utiliza contenedores IoC o fachadas en lugar de new Class**\n\nLa sintaxis new Class crea acoplamientos estrechos y complica las pruebas. Utiliza contenedores IoC o fachadas en lugar de ello.\n\nMalo:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nBueno:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **No saques información directamente del archivo `.env`**\n\nEn lugar de ello, pasa la información a un archivo de configuración y luego utiliza el ayudante `config()` para obtener la información en tu aplicación.\n\nMalo:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nBueno:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Utiliza la información\n$apiKey = config('api.key');\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Guarda las fechas en los formatos estándares. Utiliza los accessors y mutators para modificar el formato**\n\nMalo:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nBueno:\n\n```php\n// Modelo\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// Vista\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Volver al índice](#índice-de-contenido)\n\n### **Otras buenas prácticas**\n\nNo coloques ningún tipo de lógica en los archivos de rutas.\n\nMinimiza el uso de PHP vanilla en las plantillas Blade.\n\n[🔝 Volver al índice](#índice-de-contenido)\n"
  },
  {
    "path": "thai.md",
    "content": "![Laravel best practices](./images/logo-thai.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nการแปลภาษา:\n\n[English](README.md)\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[ภาษาไทย](thai.md) (by [Kongvut Sangkla](https://github.com/kongvut/laravel-best-practices))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## เนื้อหา<a name=\"contents\"></a>\n\n[1. แนวทางรูปแบบการตอบกลับเพียงที่เดียว](#single-responsibility-principle)\n\n[2. ความอ้วนของ Models และ Controllers ขนาดเล็ก](#fat-models-skinny-controllers)\n\n[3. การตรวจสอบค่า](#validation)\n\n[4. Business logic ควรจะอยู่ใน Service คลาส](#business-logic-should-be-in-service-class)\n\n[5. อย่าเรียกตัวเองซ้ำ](#dont-repeat-yourself-dry)\n\n[6. ควรจะใช้ Eloquent มากกว่า Query Builder หรือ Raw SQL queries และชอบที่จะใช้ Collections มากกว่า Arrays แบบปกติ](#prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays)\n\n[7. ความอ้วนเบอะบะของการกำหนดค่า](#mass-assignment)\n\n[8. ไม่ควรที่จะเรียกรัน SQL Queries ในเทมเพลต Blade และใช้เทคนิค Eager loading แทน (เพราะปัญหา N + 1)](#do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem)\n\n[9. หมั่นคอมเมนต์โค้ดของคุณ อีกทั้งควรจะอธิบายการทำงานของเมธอด และชื่อตัวแปร มากกว่าการคอมเมนต์เฉย ๆ](#comment-your-code-but-prefer-descriptive-method-and-variable-names-over-comments)\n\n[10. อย่าใส่ JS และ CSS ในเทมเพลต Blade และอย่าใส่ HTML ใด ๆ ในคลาส PHP](#do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes)\n\n[11. ใช้ค่าคงที่ Config และค่าคงที่ของข้อความไฟล์ภาษา แทนการใส่ข้อความตรง ๆ ลงในโค้ด](#use-config-and-language-files-constants-instead-of-text-in-the-code)\n\n[12. ใช้เครื่องมือมาตรฐานของ Laravel ที่ชุมชนยอมรับ](#use-standard-laravel-tools-accepted-by-community)\n\n[13. ปฏิบัติตามแนวทางการตั้งชื่อต่าง ๆ ตามกรอบกติกา Laravel](#follow-laravel-naming-conventions)\n\n[14. ใช้ไวยากรณ์ที่สั้นกว่าและอ่านง่ายกว่าถ้าเป็นไปได้](#use-shorter-and-more-readable-syntax-where-possible)\n\n[15. ใช้ชุดรูปแบบ IoC หรือ Facades แทนเรียกคลาสใหม่](#use-ioc-container-or-facades-instead-of-new-class)\n\n[16. อย่าเรียกข้อมูลจากไฟล์ `.env` โดยตรง](#do-not-get-data-from-the-env-file-directly)\n\n[17. เก็บวันที่ในรูปแบบมาตรฐาน อีกทั้งใช้ Accessors และ Mutators เพื่อแก้ไขรูปแบบวันที่](#store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format)\n\n[- แนวทางการปฏิบัติที่ดีอื่น ๆ](#other-good-practices)\n\n##\n\n### <a name=\"single-responsibility-principle\">1. แนวทางรูปแบบการตอบกลับเพียงที่เดียว</a>\n\nภายในคลาส ซึ่งในเมธอดควรมีการ Return ค่าเพียงที่เดียว\n\nที่แย่:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nที่ดี:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"fat-models-skinny-controllers\">2. ความอ้วนของ Models และ Controllers ขนาดเล็ก</a>\n\nเขียนความสัมพันธ์ฐานข้อมูลทั้งหมด (รวมทั้งแบบ Query Builder หรือ raw SQL queries) ลงใน Model Eloquent หรือในคลาส Repository สร้างเป็น Method สำหรับเรียกใช้งาน เพื่อลดความซ้ำซ้อนของ Logic และขนาด Controllers เพื่อให้มีขนาดเล็กลง\n\nที่แย่:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nที่ดี:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"validation\">3. การตรวจสอบค่า</a>\n\nย้ายการตรวจสอบ Validation จาก Controllers ไปที่ Request classes แทน\n\nที่แย่:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nที่ดี:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"business-logic-should-be-in-service-class\">4. Business logic ควรจะอยู่ใน Service คลาส</a>\n\nเพื่อให้ Method ภายใน Controller มีขนาดที่เล็กลง ดังนั้นควรย้าย Business logic จาก Controllers ไปที่คลาส Service แทน\n\nที่แย่:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nที่ดี:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"dont-repeat-yourself-dry\">5. อย่าเรียกตัวเองซ้ำ</a>\n\nทำการ Reuse โค้ดเพื่อช่วยหลีกเลี่ยงโค้ดที่ซ้ำซ้อน เช่นเดียวกันกับการ Reuse เทมเพลต Blade โดยสำหรับ Model ให้ใช้ Eloquent scopes ช่วยเป็นต้น\n\nที่แย่:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nที่ดี:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"prefer-to-use-eloquent-over-using-query-builder-and-raw-sql-queries-prefer-collections-over-arrays\">6. ควรที่จะใช้ Eloquent มากกว่า Query Builder หรือ Raw SQL queries และชอบที่จะใช้ Collections มากกว่า Arrays แบบปกติ</a>\n\nEloquent ช่วยให้คุณสามารถอ่านโค้ดเข้าใจง่าย\nและบำรุงรักษาได้ง่าย นอกจากนี้ Eloquent ยังมีเครื่องมือในตัวที่ยอดเยี่ยม เช่น soft deletes, events, scopes เป็นต้น\n\nที่แย่:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nที่ดี:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"mass-assignment\">7. ความอ้วนเบอะบะของการกำหนดค่า</a>\n\nที่แย่:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nที่ดี:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"do-not-execute-queries-in-blade-templates-and-use-eager-loading-n--1-problem\">8. ไม่ควรที่จะเรียกรัน SQL Queries ในเทมเพลต Blade และใช้เทคนิค Eager loading แทน (เพราะปัญหา N + 1)</a>\n\nที่แย่: (สำหรับข้อมูลตารางผู้ใช้ 100 users โดยจะมีการรันคำสั่ง Queries 101 ครั้ง):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nที่ดี: (สำหรับข้อมูลตารางผู้ใช้ 100 users โดยจะมีการรันคำสั่ง Queries 2 ครั้ง):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"comment-your-code-but-prefer-descriptive-method-and-variable-names-over-comments\">9. หมั่นคอมเมนต์โค้ดของคุณ อีกทั้งควรจะอธิบายการทำงานของเมธอด และชื่อตัวแปร มากกว่าการคอมเมนต์เฉย ๆ</a>\n\nที่แย่:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nที่ควร:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nที่ดี:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"do-not-put-js-and-css-in-blade-templates-and-do-not-put-any-html-in-php-classes\">10. อย่าใส่ JS และ CSS ในเทมเพลต Blade และอย่าใส่ HTML ใด ๆ ในคลาส PHP</a>\n\nที่แย่:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nที่ควร:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nหรือ\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nที่ไฟล์ Javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"use-config-and-language-files-constants-instead-of-text-in-the-code\">11. ใช้ค่าคงที่ Config และค่าคงที่ของข้อความไฟล์ภาษา แทนการใส่ข้อความตรง ๆ ลงในโค้ด</a>\n\nที่แย่:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nที่ดี:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"use-standard-laravel-tools-accepted-by-community\">12. ใช้เครื่องมือมาตรฐานของ Laravel ที่ชุมชนยอมรับ</a>\n\nควรที่จะใช้ฟังก์ชันมาตรฐานที่ Built-in มาใน Laravel และแพ็คเกจคอมมิวนิตี้ยอดนิยม แทนการใช้แพ็คเกจและเครื่องมือของ 3rd party ปัญหาก็คือนักพัฒนาใหม่ ๆ ที่จะมาพัฒนาร่วมกับแอพของคุณในอนาคต จะต้องเรียนรู้เครื่องมือใหม่ ๆ (3rd party packages) นอกจากนี้โอกาสที่จะได้รับความช่วยเหลือจากชุมชน Laravel จะน้อยอย่างมากเมื่อคุณใช้แพ็คเกจหรือเครื่องมือของ 3rd party อีกทั้งอย่าทำให้ลูกค้าของคุณจ่ายเงินเพิ่มเติมสำหรับสิ่งพวกนั้น (Licenses)\n\n*เพิ่มเติม:\nปัญหาอีกอย่างของการใช้ 3rd party packages คืออาจจะถูกละเลยการอัพเดทแพ็คเกจสำหรับคุณสมบัติใหม่ ๆ หรือฟังก์ชันความปลอดภัย\n\nฟังก์ชัน | เครื่องมือมาตรฐาน | เครื่องมือ 3rd party\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"follow-laravel-naming-conventions\">13. ปฏิบัติตามแนวทางการตั้งชื่อต่าง ๆ ตามกรอบกติกา Laravel</a>\n\nปฏิบัติตามแนวทาง [มาตรฐาน PSR](https://www.php-fig.org/psr/psr-12/).\n \nนอกจากนี้ให้ปฏิบัติตามแบบแผนการตั้งชื่อที่ชุมชน Laravel ยอมรับ:\n\nเกี่ยวกับ | แนวทาง | ที่ดี | ที่แย่\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"use-shorter-and-more-readable-syntax-where-possible\">14. ใช้ไวยากรณ์ที่สั้นกว่าและอ่านง่ายกว่าถ้าเป็นไปได้</a>\n\nที่แย่:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nที่ดี:\n\n```php\nsession('cart');\n$request->name;\n```\n\nตัวอย่างอื่น ๆ เพิ่มเติม:\n\nSyntax ทั่วไป | Syntax ที่สั้นและอ่านง่ายกว่า\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"use-ioc-container-or-facades-instead-of-new-class\">15. ใช้ชุดรูปแบบ IoC หรือ Facades แทนเรียกคลาสใหม่</a>\n\nการเรียกคลาสใหม่ระหว่างคลาสเป็นอะไรที่ซับซ้อนและซ้ำซ้อน แนะนำให้ใช้หลัก IoC หรือ Facades แทน\n\n*เพิ่มเติม: ในกรณี Controller ของ Model เดียวกันแนะนำให้ทำดังตัวอย่าง\n\nที่แย่:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nที่ดี:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"do-not-get-data-from-the-env-file-directly\">16. อย่าเรียกข้อมูลจากไฟล์ `.env` โดยตรง</a>\n\nแนะนำให้ส่งผ่านข้อมูลเพื่อกำหนดค่าจากไฟล์ Config แทน จากนั้นเรียกใช้ฟังก์ชันตัวช่วย `config ()` เพื่อเรียกใช้ข้อมูลในแอปพลิเคชัน\n\nที่แย่:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nที่ดี:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"store-dates-in-the-standard-format-use-accessors-and-mutators-to-modify-date-format\">17. เก็บวันที่ในรูปแบบมาตรฐาน อีกทั้งใช้ Accessors และ Mutators เพื่อแก้ไขรูปแบบวันที่</a>\n\nที่แย่:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nที่ดี:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Back to contents](#contents)\n\n### <a name=\"other-good-practices\">- แนวทางการปฏิบัติที่ดีอื่น ๆ</a>\n\n- อย่าใส่ Logic ใด ๆ ในไฟล์ routes\n- ลดการใช้ Vanilla PHP ให้น้อยที่สุดในเทมเพลต Blade\n\n[🔝 Back to contents](#contents)\n"
  },
  {
    "path": "traditional-chinese.md",
    "content": "![Laravel best practices](/images/logo-traditional-chinese.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## 內容\n\n[單一職責原則](#單一職責原則)\n\n[使 Controller 簡潔，Model 肥大](#使-controller-簡潔model-肥大)\n\n[驗證](#驗證)\n\n[商業邏輯應放置於 Service 類別內](#商業邏輯應放置於-service-類別內)\n\n[DRY 原則 - 不要重覆自己](#dry-原則---不要重覆自己)\n\n[優先使用 Eloquent 而不是 Query Builder 與原始 SQL 語句；優先使用 Collection 而不是陣列](#優先使用-eloquent-而不是-query-builder-與原始-sql-語句；優先使用-collection-而不是陣列)\n\n[Mass Assignement - 大量賦值](#mass-assignement---大量賦值)\n\n[不要在 Blade 樣板中執行查詢，並使用 Eager Loading (N + 1 問題)](#不要在-blade-樣板中執行查詢並使用-eager-loading-n--1-問題)\n\n[在程式碼中加上註解，但比起註解應儘量使用描述性的方法與變數名稱](#在程式碼中加上註解但比起註解應儘量使用描述性的方法與變數名稱)\n\n[不要將 JS 與 CSS 放到 Blade 樣板內，也不要把 HTML 放到 PHP 內](#不要將-js-與-css-放到-blade-樣板內也不要把-html-放到-php-內)\n\n[使用設定檔與語系檔，並在程式碼中使用常數來代替文字](#使用設定檔與語系檔並在程式碼中使用常數來代替文字)\n\n[使用社群認可的標準 Laravel 工具](#使用社群認可的標準-laravel-工具)\n\n[遵循 Laravel 命名規範](#遵循-laravel-命名規範)\n\n[盡可能使用簡短且可讀性更好的語法](#盡可能使用簡短且可讀性更好的語法)\n\n[使用 IoC Container 或 Facade 而不是直接 new Class](#使用-ioc-container-或-facade-而不是直接-new-class)\n\n[不要直接從 .env 檔案取得資料](#不要直接從-env-檔案取得資料)\n\n[以標準格式來儲存日期時間，並以 Accesor 或 Mutator 來修改日期格式](#以標準格式來儲存日期時間並以-accesor-或-mutator-來修改日期格式)\n\n[其他優良實踐](#其他優良實踐)\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n### **單一職責原則**\n\n一個類別與方法應只有一個職責。\n\n例如:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGood:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 回到目錄](#內容)\n\n### **使 Controller 簡潔，Model 肥大**\n\n如果使用 Query Builder 或原始 SQL 查詢，則請將所有 DB 關聯的邏輯放在 Eloquent Model 或 Repository 類別中。\n\nBad:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGood:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 回到目錄](#內容)\n\n### **驗證**\n\n將資料類別從 Controller 中移到 Request 類別內。\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 回到目錄](#內容)\n\n### **商業邏輯應放置於 Service 類別內**\n\nController 必須只能有單一職責，因此將商業邏輯移到 Service 類別內。\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n\n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 回到目錄](#內容)\n\n### **DRY 原則 - 不要重覆自己**\n\n盡可能重複使用程式碼。通過 SRP (單一職責原則) 有助於避免重複。另外，請重複使用 Blade 樣板，並使用 Eloquent Scope 等。\n\nBad:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGood:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 回到目錄](#內容)\n\n### **優先使用 Eloquent 而不是 Query Builder 與原始 SQL 語句；優先使用 Collection 而不是陣列**\n\n使用 Eloquent 可以寫出有較高可讀性與可維護性的程式碼。另外，Eloquent 還內建了許多不錯的工具，如軟刪除、事件、Scope 等功能。\n\nBad:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`)\n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGood:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 回到目錄](#內容)\n\n### **Mass Assignement - 大量賦值**\n\nBad:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nGood:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 回到目錄](#內容)\n\n### **不要在 Blade 樣板中執行查詢，並使用 Eager Loading (N + 1 問題)**\n\n例子 (若有 100 個使用者，則會執行 101 次 DB 查詢):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n更優的寫法 (若有 100 個使用者，則會執行 2 次 DB 查詢):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 回到目錄](#內容)\n\n### **在程式碼中加上註解，但比起註解應儘量使用描述性的方法與變數名稱**\n\nBad:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n加上註釋:\n\n```php\n// 確定是否有任何 Join\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGood:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 回到目錄](#內容)\n\n### **不要將 JS 與 CSS 放到 Blade 樣板內，也不要把 HTML 放到 PHP 內**\n\nBad:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nGood:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\n或\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\n在 JavaScript 檔案中:\n\n```javascript\nlet article = $('#article').val();\n```\n\n最好的方法是用專門的 PHP 或 JS 套件來傳遞資料。\n\n[🔝 回到目錄](#內容)\n\n### **使用設定檔與語系檔，並在程式碼中使用常數來代替文字**\n\nBad:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\nGood:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 回到目錄](#內容)\n\n### **使用社群認可的標準 Laravel 工具**\n\n\n儘量使用內建的 Laravel 功能以及社群套件，而不是使用第三方套件與工具。若未來有哪位開發者接手你的專案，就必須要再學習新工具。另外，若使用第三方套件或工具，那麼從 Laravel 社群中取得協助的機會也會減少。請避免增加客戶的成本。\n\n任務 | 標準工具 | 第三方工具\n------------ | ------------- | -------------\n權限控制 | Policies | Entrust, Sentinel 或其他套件\n編譯資源 | Laravel Mix, Vite | Grunt, Gulp, 或其他第三方套件\n開發環境 | Laravel Sail, Homestead | Docker\n部署 | Laravel Forge | Deployer 或其他解決方案\n單元測試 | PHPUnit, Mockery | Phpspec, Pest\n瀏覽器測試 | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\n樣板 | Blade | Twig\n資料操作 | Laravel Collection | 陣列\n表單驗證 | Request 類別 | 第三方套件、在 Controller 中驗證\n登入驗證 | 內建 | 其他第三方套件或自製解決方案\nAPI 登入驗證 | Laravel Passport, Laravel Sanctum | 第三方 JWT 或 OAuth 套件\n建立 API | 內建 | Dingo API 或類似套件\n處理 DB 結構 | Migrations | 直接操作 DB 結構\n本地化 | 內建 | 第三方套件\n即時使用者界面 | Laravel Echo, Pusher | 第三方套件或直接使用 WebSocket\n建立測試資料 | Seeder 類別, Model Factories, Faker | 手動建立測試資料\n任務排程 | Laravel Task Scheduler | 腳本或第三方套件\n資料庫 | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 回到目錄](#內容)\n\n### **遵循 Laravel 命名規範**\n\n遵守 [PSR 標準 (英語)](https://www.php-fig.org/psr/psr-12/)。\n\n另外，請遵守 Laravel 社群認可的命名規範:\n\n東西 | 命名方式 | Good | Bad\n------------ | ------------- | ------------- | -------------\nController | 單數 | ArticleController | ~~ArticlesController~~\nRoute - 路由 | 複數 | articles/1 | ~~article/1~~\nRoute name - 路由命名| 使用點標記的 snake_case | users.show_active | ~~users.show-active, show-active-users~~\nModel | 單數 | User | ~~Users~~\nhasOne 或 belongsTo 關聯 | 單數 | articleComment | ~~articleComments, article_comment~~\n所有其他關聯 | 複數 | articleComments | ~~articleComment, article_comments~~\n資料表 | 複數 | article_comments | ~~article_comment, articleComments~~\nPivot Table 透視表 | 以字母順序排列的單數 Model 名稱 | article_user | ~~user_article, articles_users~~\n資料表欄位| 使用 snake_case，並且不包含 Model 名稱 | meta_title | ~~MetaTitle; article_meta_title~~\nModel 屬性 | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign Key - 外鍵 | 以單數 Model 名稱後方加上 _id | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary Key - 主鍵 | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\n方法 | camelCase | getAll | ~~get_all~~\nResource Controller 中的方法 | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\n測試類別中的方法| camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\n變數 | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | 描述性名稱、複數 | $activeUsers = User::active()->get() | ~~$active, $data~~\n物件 | 秒屬性名稱、單數 | $activeUser = User::active()->first() | ~~$users, $obj~~\n設定檔與語系檔的索引鍵 | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\n設定檔 | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (界面) | 形容詞或名詞 | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | 形容詞 | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 回到目錄](#內容)\n\n### **盡可能使用簡短且可讀性更好的語法**\n\nBad:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGood:\n\n```php\nsession('cart');\n$request->name;\n```\n\n更多範例:\n\n一般語法 | Good\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 回到目錄](#內容)\n\n### **使用 IoC Container 或 Facade 而不是直接 new Class**\n\nnew Class 語法增加物件間的耦合度，且會讓測試更複雜。應使用 IoC Container 或 Facade 來代替。\n\nBad:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGood:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 回到目錄](#內容)\n\n### **不要直接從 `.env` 檔案取得資料**\n\n請改而將資料傳至設定檔並使用 `config()` helper 函式來在應用程式中使用資料。\n\nBad:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGood:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 回到目錄](#內容)\n\n### **以標準格式來儲存日期時間，並以 Accesor 或 Mutator 來修改日期格式**\n\nBad:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGood:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 回到目錄](#內容)\n\n### **其他優良實踐**\n\n絕對不要在路由檔案中撰寫任何邏輯。\n\n在 Blade 樣板中避免使用原始 PHP。\n\n[🔝 回到目錄](#內容)\n\n\n"
  },
  {
    "path": "turkish.md",
    "content": "![Laravel best practices](/images/logo-turkish.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\nÇeviriler:\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/ohmydevops))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## İçerik\n\n[Single responsibility principle (Tek sorumluluk prensibi)](#single-responsibility-principle-tek-sorumluluk-prensibi)\n\n[Büyük modeller, çirkin controllerlar](#büyük-modeller-çirkin-controllerlar)\n\n[Validation (Veri Doğrulama)](#veri-doğrulama-validasyon)\n\n[Business logic servis class'ında bulunmalıdır](#business-logic-servis-classında-bulunmalıdır)\n\n[Kendini tekrar etme (DRY: Don't repeat yourself)](#kendini-tekrar-etme-dry-dont-repeat-yourself)\n\n[Query Builder ve düz queryler kullanmak yerine Eloquent, array kullanmak yerine Collection kullanın](#query-builder-ve-düz-queryler-kullanmak-yerine-eloquent-array-kullanmak-yerine-collection-kullanın)\n\n[Mass assignment (Toplu atama)](#mass-assignment-toplu-atama)\n\n[Blade templatelerinde asla query çalıştırmayın, eager loading kullanın (N + 1 problemi)](#blade-templatelerinde-asla-query-çalıştırmayın-eager-loading-kullanın-n--1-problemi)\n\n[Koda yorum yazın ancak öncelikli olarak anlamlı method ve değişken isimleri seçin](#koda-yorum-yazın-ancak-öncelikli-olarak-anlamlı-method-ve-değişken-isimleri-seçin)\n\n[Blade içinde JS ve CSS kullanmayın ve PHP classlarına HTML yazmayın](#blade-içinde-js-ve-css-kullanmayın-ve-php-classlarına-html-yazmayın)\n\n[Config ve language dosyalarını kullanın, kod içinde ise metin kullanmak yerine constant kullanın](#config-ve-language-dosyalarını-kullanın-kod-içinde-ise-metin-kullanmak-yerine-constant-kullanın)\n\n[Laravel topluluğu tarafından kabul edilen standart araçları kullanın](#laravel-topluluğu-tarafından-kabul-edilen-standart-araçları-kullanın)\n\n[Laravel'de isimlendirme](#laravelde-isimlendirme)\n\n[Mümkün olduğunca daha kısa ve okunabilir syntax kullanın](#mümkün-olduğunca-daha-kısa-ve-okunabilir-syntax-kullanın)\n\n[new Class kullanımı yerine IoC container ya da facade kullanın](#new-class-kullanımı-yerine-ioc-container-ya-da-facade-kullanın)\n\n[`.env` dosyasından doğrudan veri çekmeyin](#env-dosyasından-doğrudan-veri-çekmeyin)\n\n[Tarihleri standart formatta kaydedin. Tarihleri formatlamak için accessor ve mutator kullanın](#tarihleri-standart-formatta-kaydedin-tarihleri-formatlamak-için-accessor-ve-mutator-kullanın)\n\n[Diğer iyi pratikler](#diğer-iyi-pratikler)\n\n### **Single responsibility principle (Tek sorumluluk prensibi)**\n\nBir class ya da method'un tek bir görevi ve amacı olmalıdır.\n\nKötü:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nİyi:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Büyük modeller, çirkin controllerlar**\n\nBütün database ile ilişkili Query Builder ve raw SQL işlemlerini Eloquent modelinde ya da Repository class'ında tanımlayarak kullanın. \n\n\nKötü:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nİyi:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Veri Doğrulama, Validasyon**\n\nValidation işlemlerini controller içinde değil, Request classları içinde yapın.\n\nKötü:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nİyi:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Business logic servis class'ında bulunmalıdır**\n\nBir controller sadece bir görevden sorumlu olmalıdır. Bu nedenle business logic, controllerlar yerine servis classında tanımlanmalıdır.\n\nKötü:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nİyi:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Kendini tekrar etme (DRY: Don't repeat yourself)**\n\nKullanabildiğiniz sürece kodu tekrar kullanın. SRP (single responsibility principle - tek sorumluluk ilkesi) size kod \ntekrarını azaltmanıza da katkı sunar. Blade templatelerini, Eloqunt scopelarını tekrar kullanın.\n\nKötü:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nİyi:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Query Builder ve düz queryler kullanmak yerine Eloquent, array kullanmak yerine Collection kullanın**\n\nEloquent okunabilir ve sürdürülebilir kod yazmanıza izin verir. Ayrıca, Eloquent güzel dahili araçlara sahiptir. Örnek olarak;\nsoft delete, event ve scope özellikleri verilebilir.\n\nKötü:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nİyi:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Mass assignment (Toplu atama)**\n\nKötü:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\nİyi:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Blade templatelerinde asla query çalıştırmayın, eager loading kullanın (N + 1 problemi)**\n\nKötü (100 kullanıcı için, 101 DB tane query çalıştırılacak):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nİyi (100 kullanıcı için, 2 DB tane query çalıştırılacak):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Koda yorum yazın ancak öncelikli olarak anlamlı method ve değişken isimleri seçin**\n\nKötü:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGörece İyi:\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nİyi:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Blade içinde JS ve CSS kullanmayın ve PHP classlarına HTML yazmayın**\n\nKötü:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nDaha İyi:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nYa da\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nJavascript dosyasında:\n\n```javascript\nlet article = $('#article').val();\n```\n\nData transferi için en iyi yol amaca özel programlanmış PHP'den JS'ye veri aktaran paketleri kullanmaktır.\n\n[🔝 Başa dön](#içerik)\n\n### **Config ve language dosyalarını kullanın, kod içinde ise metin kullanmak yerine constant kullanın**\n\nKötü:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Makaleniz eklendi!');\n```\n\nİyi:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Laravel topluluğu tarafından kabul edilen standart araçları kullanın**\n\nGeniş topluluk desteği olmayan paketler yerine Laravel'e dahili gelen ya da topluluk tarafından kabul edilmiş araç ve paketleri kullanın.\nBu şekilde koda dahil olan herkesin rahatlıkla projeye katkı sunmasını sağlayabilirsiniz. Ayrıca topluluk desteği düşük olan\npaketleri ya da araçları kullandığınızda yardım alabilme ihtimaliniz önemli derecede azalmaktadır. Bu paketleri tekrar\nhazırlamak yerine, tercih edilen paketleri kullanın ve bu maliyetlerden kaçının.\n\n*Çevirmen Notu #2: Tercihen en çok forklanmış, en çok yıldızlanmış, en çok takip edilen repositorylerden faydalanabilirsiniz. Koda yapılan son commit tarihi ve issue kısmında yanıtlanan soru ve cevap dökümleri paketin güncelliği ve ne kadar güncel kalacağı ile ilgili size bilgi sunar.*\n\nYapılacak | Standart araç | 3rd party araçlar\n------------ | ------------- | -------------\nAuthorization (Yetkilendirme) | Policies | Entrust, Sentinel vb.\nCompiling assets (CSS ve JS Derleme) | Laravel Mix, Vite | Grunt, Gulp, 3rd party paketler\nGeliştirme Ortamı | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplate | Blade | Twig\nVeri işleme | Laravel collections | Arrays\nForm doğrulama | Request classları | 3rd party paketler, controllerda doğrulama\nAuthentication (Doğrulama) | Dahili | 3rd party paketler ya da kendi çözümünüz\nAPI authentication (Doğrulama) | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packetleri\nAPI Oluşturma | Dahili | Dingo API vb.\nDB Yapısı | Migrations | Doğrudan DB yönetimi\nLokalizasyon (Yerelleştirme) | Dahili | 3rd party paketler\nGerçek zamanlı kullanıcı etkileşimi | Laravel Echo, Pusher | Doğrudan WebSocket kullanan 3rd party paketler\nTest verisi oluşturmak | Seeder classları, Model Factoryleri, Faker | Oluşturup manuel test etmek\nGörev Zamanlama | Laravel Task Scheduler | Scriptler ve 3rd party paketler\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Başa dön](#içerik)\n\n### **Laravel'de isimlendirme**\n\n[PSR standards](https://www.php-fig.org/psr/psr-12/) takip edin.\n\nAyrıca, topluluk tarafından kabul gören isimlendirmeler:\n\nNe | Nasıl | İyi | Kötü\n------------ | ------------- | ------------- | -------------\nController | tekil | ArticleController | ~~ArticlesController~~\nRoute | çoğul | articles/1 | ~~article/1~~\nRoute name | snake_case ve dot notation (nokta kullanımı) | users.show_active | ~~users.show-active, show-active-users~~\nModel | tekil | User | ~~Users~~\nhasOne or belongsTo relationship | tekil | articleComment | ~~articleComments, article_comment~~\nAll other relationships | çoğul | articleComments | ~~articleComment, article_comments~~\nTable | çoğul | article_comments | ~~article_comment, articleComments~~\nPivot table | tekil model isimleri alfabetik sırada | article_user | ~~user_article, articles_users~~\nTable column | snake_case ve model adı olmadan | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | tekil model adı ve _id suffix'i (soneki) | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | tanımlayıcı, çoğul | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | tanımlayıcı, tekil | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | sıfat ya da isim | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | sıfat | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | tekil | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | tekil | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Başa dön](#içerik)\n\n### **Mümkün olduğunca daha kısa ve okunabilir syntax kullanın**\n\nKötü:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nİyi:\n\n```php\nsession('cart');\n$request->name;\n```\n\nDaha çok örnek:\n\nOrtak syntax | Kısa ve daha okunabilir syntax\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Başa dön](#içerik)\n\n### **new Class kullanımı yerine IoC container ya da facade kullanın**\n\nnew Class kullanımı classlar arası bağlantıları doğrudan kurar ve test sürecini karmaşıklaştırır. IoC container ya da facade kullanın.\n\nKötü:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nİyi:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Başa dön](#içerik)\n\n### **`.env` dosyasından doğrudan veri çekmeyin**\n\nVeriyi config dosyasında çağırın ve `config()` helper fonksiyonunu kullanarak uygulama içinde erişin.\n\nKötü:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nİyi:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Tarihleri standart formatta kaydedin. Tarihleri formatlamak için accessor ve mutator kullanın**\n\nKötü:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nİyi:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// View\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Başa dön](#içerik)\n\n### **Diğer iyi pratikler**\n\nRoute dosyalarına asla logic yazmayın.\n\nBlade template dosyalarında vanilya PHP (düz PHP) kullanmayın.\n\n[🔝 Başa dön](#içerik)\n"
  },
  {
    "path": "turkmen.md",
    "content": "![Laravel best practices](/images/logo-english.png?raw=true)\n\nŞeýle hem, [Laravel mysal programmasyna](https://github.com/alexeymezenin/laravel-realworld-example-app) we [Eloquent ORM tarapyndan döredilen SQL soraglaryna](https://github.com/alexeymezenin/eloquent-sql-reference) göz aýlamak gyzykly bolup biler.\n\n[![Laravel mysal programmasy](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## Mazmuny\n\n[Ýeke-täk jogapkärçilik prinsipi (Single responsibility principle)](#ýeke-täk-jogapkärçilik-prinsipi-single-responsibility-principle)\n\n[Usullar diňe bir zady etmeli](#usullar-diňe-bir-zady-etmeli)\n\n[Ýarpaýy kontrolýorlar, ýagly modeller](#ýarpaýy-kontrolýorlar-ýagly-modeller)\n\n[Vallidasiýa](#vallidasiýa)\n\n[Işewürlik logikasy hyzmat-klasslarynda](#işewürlik-logikasy-hyzmat-klasslarynda)\n\n[Täzeden gaýtalama (DRY)](#täzeden-gaýtalama-dry)\n\n[Eloquent'yi soraglar konstruktory (query builder) we database'ye çygly soraglar bilen işlemekden has gowy saýlaň. Koleksiýalar bilen işlemekni massiwler bilen işlemekden has gowy saýlaň.](#eloquentyi-soraglar-konstruktory-query-builder-we-databaseye-çygly-soraglar-bilen-işlemekden-has-gowy-saýlaň-koleksiýalar-bilen-işlemekni-massiwler-bilen-işlemekden-has-gowy-saýlaň)\n\n[Massa täzelenmesini ulanyň (mass assignment)](#massa-täzelenmesini-ulanyň-mass-assignment)\n\n[Soraglary görkezmelerde ýerine ýetirmäň we sabyrsyz ýüklemäni ulanyň (mesele N + 1)](#soraglary-görkezmelerde-ýerine-ýetirmäň-we-sabyrsyz-ýüklemäni-ulanyň-mesele-n--1)\n\n[Çok sanly maglumatlar bilen işleşende chunk usulyny ulanyň](#çok-sanly-maglumatlar-bilen-işleşende-chunk-usulyny-ulanyň)\n\n[JS we CSS-i Blade şablonlaryndan çykaryp, HTML-i PHP kodundan aýyryň](#js-we-css-i-blade-şablonlaryndan-çykaryp-html-i-php-kodundan-aýyryň)\n\n[Topar tarapyndan kabul edilen gurallary we amalyýetleri ulanyň](#topar-tarapyndan-kabul-edilen-gurallary-we-amalyýetleri-ulanyň)\n\n[Jemgyýetiniň atlandyrma baradaky ylalaşyklaryna eýeriň.](#jemgyýetiniň-atlandyrma-baradaky-ylalaşyklaryna-eýeriň)\n\n[new Class ulanmagyň ýerine IoC ýa-da fasadlary ulanyň.](#new-class-ulanmagyň-ýerine-ioc-ýa-da-fasadlary-ulanyň)\n\n[.env faýlyndan maglumatlar bilen gönüden-göni işlemäň](#env-faýlyndan-maglumatlar-bilen-gönüden-göni-işlemäň)\n\n### **Ýeke-täk jogapkärçilik prinsipi (Single responsibility principle)**\n\nHer bir klass diňe bir borçly bolmaly.\n\nGoýy:\n\n```php\npublic function update(Request $request): string\n{\n    $validated = $request->validate([\n        'title' => 'required|max:255',\n        'events' => 'required|array:date,type'\n    ]);\n\n    foreach ($request->events as $event) {\n        $date = $this->carbon->parse($event['date'])->toString();\n\n        $this->logger->log('Update event ' . $date . ' :: ' . $);\n    }\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\nGowy:\n\n```php\npublic function update(UpdateRequest $request): string\n{\n    $this->logService->logEvents($request->events);\n\n    $this->event->updateGeneralEvent($request->validated());\n\n    return back();\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n\n### **Usullar diňe bir zady etmeli**\n\nFunksiýa diňe bir zady etmeli we ony gowy etmeli.\n\nGoýy:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nGowy:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Ýarpaýy kontrolýorlar, ýagly modeller**\n\nMaglumaty işlemek işini modellere çykaryň\n\nGoýy:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nGowy:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders(): Collection\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Vallidasiýa**\n\nÝarpaýy kontrolýor we SRP prinsiplerine laýyklykda, validasiýany kontrolýordan Request klasslaryna çykaryň.\n\nGoýy:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nGowy:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules(): array\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Işewürlik logikasy hyzmat-klasslarynda.**\n\nKontrolýor diňe özniň doly borçlaryny ýerine ýetirmeli, şonuň üçin ähli işewürlik logikasyny aýratyn klasslara we hyzmat-klasslaryna çykaryň.\n\nGoýy:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGowy:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image): void\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Täzeden gaýtalama (DRY)**\n\nBu prinsip size kody mümkin boldugyça her ýerde gaýtadan ulanmaga çagyryş edýär. Egerde siz SRP prinsipine eýerýän bolsaňyz, siz öňünden gaýtalanmalardan gaça durýarsyňyz, emma Laravel size şonuň ýaly hem görkezmeleri, Eloquent soraglarynyň böleklerini we ş.m. gaýtadan ulanmak mümkinçiligini berýär.\n\nGoýy:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nGowy:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', true)->whereNotNull('deleted_at');\n}\n\npublic function getActive(): Collection\n{\n    return $this->active()->get();\n}\n\npublic function getArticles(): Collection\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Eloquent'yi soraglar konstruktory (query builder) we database'ye çygly soraglar bilen işlemekden has gowy saýlaň. Koleksiýalar bilen işlemekni massiwler bilen işlemekden has gowy saýlaň.**\n\nEloquent, mümkin boldugyça okalýan kody ýazmaga mümkinçilik berýär, programmanyň funksionallygyny üýtgetmek bolsa mukdarsyz has ýeňildir. Eloquent'de şeýle hem bir topar amatly we güýçli gurallar bar. Siziň üçin gyzykly bolup biler [Eloquent soraglaryny SQL'ye terjime etmek üçin gollanma.](https://github.com/alexeymezenin/eloquent-sql-reference)\n\nGoýy:\n\n```php\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nGowy:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Massa täzelenmesini ulanyň (mass assignment)**\n\nGoýy:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Makalany kategoriýaga baglaň.\n$article->category_id = $category->id;\n$article->save();\n```\n\nGowy:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Soraglary görkezmelerde ýerine ýetirmäň we sabyrsyz ýüklemäni ulanyň (mesele N + 1)**\n\nGoýy (100 ulanyjy üçin 101 sorag database'ye ýerine ýetiriler):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nGowy (100 ulanyjy üçin 2 sorag database'ye ýerine ýetirilýär):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Çok sanly maglumatlar bilen işleşende chunk usulyny ulanyň**\n\nGoýy:\n\n```php\n$users = $this->get();\n\nforeach ($users as $user) {\n    ...\n}\n```\n\nGowy:\n\n```php\n$this->chunk(500, function ($users) {\n    foreach ($users as $user) {\n        ...\n    }\n});\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Okalýan atlary we metodlary, düşündirişlere (kommentariýalara) garanda has gowy saýlaň**\n\nGoýy:\n\n```php\n// Determine if there are any joins\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nGowy:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **JS we CSS-i Blade şablonlaryndan çykaryp, HTML-i PHP kodundan aýyryň.**\n\nGoýy:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nGowy:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nÝa-da\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nJavascript faýlynda:\n\n```javascript\nlet article = $('#article').val();\n```\n\nHas gowusy, maglumatlary backend-den frontend'e geçirmek üçin ýöriteleşdirilen paket ulanyň.\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Konfigurasiýalar, dil faýllary we sabitler kodyň içinde tekstiň ýerine ulanylmaly**\n\nKodda hiç bir tekst bolanok bolmaly.\n\nGoýy:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Makalaňyz üstünlikli goşuldy');\n```\n\nGowy:\n\n```php\npublic function isNormal(): bool\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Topar tarapyndan kabul edilen gurallary we amalyýetleri ulanyň**\n\nLaravel, giňden duş gelýän meseleleri çözmek üçin utgaşdyrylan gurallara eýe. Şol gurallary ulanmaga has köp üns beriň, daşarky paketler we gurallary ulanyp çözgüt tapmaga garanda. Laravel-de täze gelen bir geliştirici, size degişli bolan bir projede işlärkä, täze bir guralla işleşmegi öwrenmeli bolar, bu bolsa ähli netijelere getirip biler. Hem-de jemgyýetden kömek almak has kyn bolar. Müşderä ýa-da iş berijiňiz üçin öz \"velosipedleriňizi\" döretmäň.\n\nWazifa | Adaty gurallar | Adaty bolmadyk gurallar\n------------ | ------------- | -------------\nAutentifikasiýa | Syýasatlar | Entrust, Sentinel we beýleki paketler, öz çözgüt\nIşlemek JS, CSS we ş.m. bilen | Laravel Mix, Vite | Grunt, Gulp, daşarky paketler\nÖnümçilik gurşawy | Laravel Sail, Homestead | Docker\nArza ýerleşdirmek | Laravel Forge | Deployer we köp sanly başga\nSynaglar | Phpunit, Mockery | Phpspec, Pest\ne2e synaglary | Laravel Dusk | Codeception\nIşlemek Baza bilen | Eloquent | SQL, soraglar gurluşy, Doctrine\nŞablonlar | Blade | Twig\nMaglumatlar bilen işlemek | Laravel Koleksiýalary | Massiwler\nFormanyň validasiýasy | Request klasslary | Daşarky paketler, kontrolýorda validasiýa\nAutentifikasiýa | Iňňän funksionallyk | Daşarky paketler, öz çözgüt\nAPI autentifikasiýasy | Laravel Passport, Laravel Sanctum | Daşarky paketler, JWT, OAuth ulanýanlar\nAPI döretmek | Iňňän funksionallyk | Dingo API we beýleki paketler\nBaza gurluşy bilen işlemek | Migrasiýalar | Baza bilen doğrudan işlemek\nLokalizasiýa | Iňňän funksionallyk | Daşarky paketler\nMaglumat alyş-berişi reňkde ýerine ýetirmek | Laravel Echo, Pusher | Paketler we websoketler bilen doğrudan işlemek\nSynag maglumatlaryny döretmek | Seeder klasslary, model fabrikalary, Faker | El bilen doldurmak we paketler\nWezipe planlamasy | Laravel wezipe planlagyjy | Skriptler we daşarky paketler\nBaza | MySQL, PostgreSQL, SQLite, SQL Server | MongoDb\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Jemgyýetiniň atlandyrma baradaky ylalaşyklaryna eýeriň.**\n\nEýeriň [PSR standartlaryna](https://www.php-fig.org/psr/psr-12/) kody ýazanyňyzda.\n\nŞeýle hem, beýleki atlandyrma ylalaşyklaryna eýeriň:\n\nNäme | Düzgün | Kabullanýan | Kabullanmaýan\n------------ | ------------- | ------------- | -------------\nKontrolýor | Ýeke-täk | ArticleController | ~~ArticlesController~~\nMarşrutlar | Köp | articles/1 | ~~article/1~~\nMarşrut atlary | snake_case | users.show_active | ~~users.show-active, show-active-users~~\nModel | Ýeke-täk | User | ~~Users~~\nHasOne we belongsTo aragatnaşyklary | Ýeke-täk | articleComment | ~~articleComments, article_comment~~\nBaşga ähli aragatnaşyklary | Köp | articleComments | ~~articleComment, article_comments~~\nTablisa | Köp | article_comments | ~~article_comment, articleComments~~\nPivot tablisa | Modelleriň atlary alfabetik tertipde, ýeke-täk görnüşde | article_user | ~~user_article, articles_users~~\nTablisadaky sütün | snake_case modeliň ady bolmazdan | meta_title | ~~MetaTitle; article_meta_title~~\nModeliň häsiýeti | snake_case | $model->created_at | ~~$model->createdAt~~\nDaşary açar| Modelleriň atlary Ýeke-täk we _id | article_id | ~~ArticleId, id_article, articles_id~~\nEsasy açar | - | id | ~~custom_id~~\nMigrasiýa | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMetod | camelCase | getAll | ~~get_all~~\nResurs kontrolýoryndaky metod | [tablisa](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nSynagdaky metod | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nÜýtgeýänler | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nKoleksiýa | düşündiriji, Köp | $activeUsers = User::active()->get() | ~~$active, $data~~\nObýekt | düşündiriji, Ýeke-täk | $activeUser = User::active()->first() | ~~$users, $obj~~\nKonfigurasiýa we dil faýllaryndaky indeksler | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nGörkezme | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nKonfigurasiýa faýly | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nKontrakt (interfeýs) | Sıfat ýa-da naýar | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | Sıfat | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | Ýeke-täk | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Ylalaşyklaryň konfigurasiýadan üstünligine üns beriň**\n\nKabul edilen ylalaşyklara eýerýän bolsaňyz, koda goşmaça konfigurasiýa goşmaga zerurlyk ýok.\n\nGoýy:\n\n```php\n// Tabliçanyň ady 'customers'\n// Esasy açar 'id'\nclass Customer extends Model\n{\n    const CREATED_AT = 'created_at';\n    const UPDATED_AT = 'updated_at';\n\n    protected $table = 'Customer';\n    protected $primaryKey = 'customer_id';\n\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');\n    }\n}\n```\n\nGowy:\n\n```php\n// Tabliçanyň ady 'customers'\n// Esasy açar 'id'\nclass Customer extends Model\n{\n    public function roles(): BelongsToMany\n    {\n        return $this->belongsToMany(Role::class);\n    }\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Mümkin ýerlerde gysga we okalýan sintaksis ulanyň**\n\nGoýy:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nGowy:\n\n```php\nsession('cart');\n$request->name;\n```\n\nÝene mysallar:\n\nKöp ulanylýan sintaksis | Has gysga we has okalýan sintaksis\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Ýokary](#Mazmuny)\n\n### **new Class ulanmagyň ýerine IoC ýa-da fasadlary ulanyň.**\n\nnew Class arkaly klasslary ornaşdyrmak programmanyň bölekleriniň arasynda berk baglanyşyk döredýär we synag işlerini kynlaşdyrýar.\nŞonuň üçin konteýneri ýa-da fasadlary ulanyň.\n\nGoýy:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nGowy:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **`.env` faýlyndan maglumatlar bilen gönüden-göni işlemäň**\n\n`.env` faýlyndan maglumatlary konfigurasiýa faýlyna geçiriň we ol maglumatlary ulamak üçin programmada `config()` funksiýasyny ulanyň.\n\nGoýy:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nGowy:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Programmada maglumatlary ulanyň\n$apiKey = config('api.key');\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Sene-maglumaty standart formatda saklaň. Formaty üýtgetmek üçin okyjylar (reader) we öwrüjiler (mutator) ulanyň.**\n\nGoýy:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nGowy:\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n// Okap bilýän (accessor)\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// Şablon\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **DocBlock ulanmaň**\n\nDocBlock'lar kodyň okalýanyny peseldýär.\nOlaryň ýerine metodlar üçin gowy atlary we häzirki zaman PHP sintaksisini ulanyň, mysal üçin gaýdýan tiplere düşündiriş goşuň.\n\nGoýy:\n\n```php\n/**\n * Funksiýa setirde ASCII düzümine girmeýän belgileriň barlygyny barlaýar.\n *\n * @param string $string Frontend-den alýan setirimiz bolup, \n *                       şol setirde ASCII düzümine girmeýän belgiler bolup biler.\n *                       Funksiýa, eger setirde beýle belgiler ýok bolsa, True gaýtarýar.\n *\n * @return bool\n * @author\n *\n * @license GPL\n */\n\npublic function checkString($string)\n{\n}\n```\n\nGowy:\n\n```php\npublic function isValidAsciiString(string $string): bool\n{\n}\n```\n\n[🔝 Ýokary](#Mazmuny)\n\n### **Beýleki maslahatlar we amalyýetler**\n\nLaravel we oňa meňzeş freýmworklar (RoR, Django) üçin ýat bolan patterneri we gurallary ulanmaň. Symfony (ýa-da Spring we ş.m.) ýaly ýollary halasaňyz, şeýle freýmworklary web programmalary döretmek üçin ulanmak has akylly bolar.\n\nLogikany marşrutlarda ýerleşdirmäň.\n\nBlade şablonlarynda \"çygyly\" PHP ulanmazlyga çalyşyň.\n\nSynag işlerinde ýatda ýerleşdirilen maglumat bazasyny (in-memory DB) ulanyň.\n\nFreýmworkyň standart gurallaryny üýtgetmäň, sebäbi bu freýmworky täzeläniňizde kynçylyk döredip biler.\n\nPHP-nyň häzirki zaman sintaksisini ulanyň, emma şonuň bilen birlikde kodyň okalýandygyna üns beriň — okalýan kod elmydama möhüm.\n\nView Composer ýaly gurallary ulanyň, ýöne ýokary seresaplyk bilen. Köplenç ýagdaýda, meseläniň başga bir çözgüdi tapmak mümkinçiligi bolýar.\n\n[🔝 Ýokary](#Mazmuny)\n"
  },
  {
    "path": "ukrainian.md",
    "content": "![Найкращі практики Laravel](/images/logo-ukrainian.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n[Back to English version](README.md)\n\n## Зміст\n\n[Принцип єдиної відповідальності (Single responsibility principle)](#Принцип-єдиної-відповідальності-single-responsibility-principle)\n\n[Товсті моделі, тонкі контролери](#Товсті-моделі-тонкі-контролери)\n\n[Перевірка даних](#Перевірка-даних)\n\n[Бізнес-логіка лише в сервісних класах](#Бізнес-логіка-лише-в-сервісних-класах)\n\n[Не повторюйтеся (DRY)](#Не-повторюйтеся-dry)\n\n[Віддавайте перевагу Eloquent понад використанням Query Builder та сирих SQL запитів. Перевага у колекцій, а не масивів](#Віддавайте-перевагу-eloquent-понад-використанням-query-builder-та-сирих-SQL-запитів-Перевага-у-колекцій-а-не-масивів)\n\n[Масове призначення](#Масове-призначення)\n\n[Ніяких запитів у шаблонах Blade та використовуйте жадібне завантаження (проблема N + 1)](#Ніяких-запитів-у-шаблонах-blade-та-використовуйте-жадібне-завантаження-проблема-n--1)\n\n[Коментуйте свій код, але описові назви методів та змінних краще](#Коментуйте-свій-код-але-описові-назви-методів-та-змінних-краще)\n\n[Жодних JS та CSS у шаблонах Blade та HTML у PHP класах](#Жодних-js-та-css-у-шаблонах-blade-та-html-у-php-класах)\n\n[Конфігурації, мовні файли та константи замість тексту в коді](#Конфігурації-мовні-файли-та-константи-замість-тексту-в-коді)\n\n[Використовуйте стандартні інструменти Laravel, що прийняті спільнотою](#Використовуйте-стандартні-інструменти-laravel-що-прийняті-спільнотою)\n\n[Дотримуйтеся домовленостей Laravel з найменування](#Дотримуйтеся-домовленостей-laravel-з-найменування)\n\n[Використовуйте, де можливо, короткий та читабельний синтаксис](#Використовуйте-де-можливо-короткий-та-читабельний-синтаксис)\n\n[Використовуйте контейнер IoC або фасади замість new Class](#Використовуйте-контейнер-ioc-або-фасади-замість-new-class)\n\n[Не отримуйте дані безпосередньо з файлу `.env`](#Не-отримуйте-дані-безпосередньо-з-файлу-env)\n\n[Зберігайте дати в стандартному форматі. Використовуйте методи доступу та зміни даних для зміни формату](#Зберігайте-дати-в-стандартному-форматі-Використовуйте-методи-доступу-та-зміни-даних-для-зміни-формату)\n\n[Інші хороші практики](#Інші-хороші-практики)\n\n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n### **Принцип єдиної відповідальності (Single responsibility principle)**\n\nКлас та метод повинні мати лише одну відповідальність.\n\nПогано:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\nДобре:\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[🔝 Назад до змісту](#зміст)\n\n### **Товсті моделі, тонкі контролери**\n\nЗа своєю суттю це лише один з прикладів принципа єдиної відповідальності. Працюйте з даними в моделі при роботі з Eloquent або в репозиторії при роботі з Query Builder або \"сирими\" SQL запитами.\n\nПогано:\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\nДобре:\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Перевірка даних**\n\nВідповідно принципам тонкого контролера та SRP, пишіть [перевірку даних (валідацію)](https://laravel.com/docs/5.8/validation) у Request класах, а не контролерах.\n\nПогано:\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\nДобре:\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Бізнес-логіка лише в сервісних класах**\n\nКонтролер має виконувати свої прямі обов’язки, тож перемістіть бізнес-логіку з контролерів до [сервісних класів](https://laravel.com/docs/5.8/providers).\n\nBad:\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\nGood:\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Не повторюйтеся (DRY)**\n\nПовторно використовуйте код де можете. SRP вже допомагатиме вам уникати задвоєнь, але Laravel дозволяє також повторно використовувати [шаблони Blade](https://laravel.com/docs/5.8/blade), [області дії Eloquent](https://laravel.com/docs/5.8/eloquent#query-scopes) тощо.\n\nПогано:\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\nДобре:\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Віддавайте перевагу Eloquent понад використанням Query Builder та сирих SQL запитів. Перевага у колекцій, а не масивів**\n\nEloquent дозволяє вам писати читабельний та підтримний код. Також, Eloquent має чудові вбудовані інструменти, як-от: [м’які видалення](https://laravel.com/docs/5.8/eloquent#soft-deleting), [події](https://laravel.com/docs/5.8/eloquent#events), [області дії](https://laravel.com/docs/5.8/eloquent#query-scopes) тощо.\n\nПогано:\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\nДобре:\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Масове призначення**\n\n[Масове призначення](https://laravel.com/docs/5.8/eloquent#mass-assignment)\n\nПогано:\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Додати категорію до статті\n$article->category_id = $category->id;\n$article->save();\n```\n\nДобре:\n\n```php\n$category->article()->create($request->validated());\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Ніяких запитів у шаблонах Blade та використовуйте жадібне завантаження (проблема N + 1)**\n\nПогано (на 100 користувачів 101 запит у БД (базу даних)):\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\nДобре (на 100 користувачів лише 2 запити у БД):\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Коментуйте свій код, але описові назви методів та змінних краще**\n\nПогано:\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nНормально:\n\n```php\n// Визначає наявність join-ів.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\nДобре:\n\n```php\nif ($this->hasJoins())\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Жодних JS та CSS у шаблонах Blade та HTML у PHP класах**\n\nПогано:\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\nКраще:\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nАбо\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nУ файлі Javascript:\n\n```javascript\nlet article = $('#article').val();\n```\n\nНайкращий варіант — використовувати спеціалізований пакунок для передачі даних з PHP до JS.\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Конфігурації, мовні файли та константи замість тексту в коді**\n\n[Про це в документації Laravel](https://laravel.com/docs/5.8/localization)\n\nПогано:\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Вашу статтю було додано!');\n```\n\nДобре:\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Використовуйте стандартні інструменти Laravel, що прийняті спільнотою**\n\nВіддавайте перевагу вбудованому функціоналу Laravel та пакункам від спільноти використанню сторонніх пакунків та інструментів. Будь-якому розробнику, що працюватиме з вашим застосунком у майбутньому, знадобиться вивчати нові інструменти. Окрім того, в разі використання сторонніх пакунків чи інструментів шанси отримати допомогу від спільноти Laravel відчутно менші. Не змушуйте свого клієнта платити за це.\n\nЗавдання | Стандартні інструменти | Сторонні інструменти\n------------ | ------------- | -------------\nАвторизація | Policies | Entrust, Sentinel and other packages\nКомпіляція засобів | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nСередовище розробки | Laravel Sail, Homestead | Docker\nРозгортання застосунків | Laravel Forge | Deployer and other solutions\nUnit тестування | PHPUnit, Mockery | Phpspec, Pest\nТестування браузера | Laravel Dusk | Codeception\nБаза даних | Eloquent | SQL, Doctrine\nШаблони | Blade | Twig\nРобота з даними | Laravel collections | Arrays\nПеревірка даних форми | Request classes | 3rd party packages, validation in controller\nАвтентифікація | Built-in | 3rd party packages, your own solution\nAPI автентифікація | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nСтворення API | Built-in | Dingo API and similar packages\nРобота зі структурою БД | Migrations | Working with DB structure directly\nЛокалізація | Built-in | 3rd party packages\nКористувацькі інтерфейси в реальному часі | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nГенерування тестових даних | Seeder classes, Model Factories, Faker | Creating testing data manually\nПланування завдань | Laravel Task Scheduler | Scripts and 3rd party packages\nБаза даних | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Дотримуйтеся домовленостей Laravel з найменування**\n\nДотримуйтеся [стандартів PSR](https://www.php-fig.org/psr/psr-12/).\n\nТакож, дотримуйтеся домовленостей з найменування прийнятих спільнотою Laravel:\n\nЩо | Написання | Добре | Погано\n------------ | ------------- | ------------- | -------------\nКонтролер | однина | ArticleController | ~~ArticlesController~~\nМаршрути | множина | articles/1 | ~~article/1~~\nНазви маршрутів | snake_case з позначенням крапкою | users.show_active | ~~users.show-active, show-active-users~~\nМодель | однина | User | ~~Users~~\nЗв’язки hasOne або belongsTo | однина | articleComment | ~~articleComments, article_comment~~\nРешта зв’язків | множина | articleComments | ~~articleComment, article_comments~~\nТаблиця | множина | article_comments | ~~article_comment, articleComments~~\nЗведена таблиця | ім’я моделі в однині в алфавітному порядку | article_user | ~~user_article, articles_users~~\nСтовпчик таблиці | snake_case без імені моделі | meta_title | ~~MetaTitle; article_meta_title~~\nВластивість моделі | snake_case | $model->created_at | ~~$model->createdAt~~\nЗовнішній ключ | ім’я моделі в однині з суфіксом _id | article_id | ~~ArticleId, id_article, articles_id~~\nПервинний ключ | - | id | ~~custom_id~~\nМіграція | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nМетод | camelCase | getAll | ~~get_all~~\nМетод у ресурсному контролері| [таблиця](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nМетод у тестовому класі | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nЗмінна | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nЗібрання | описове, множина | $activeUsers = User::active()->get() | ~~$active, $data~~\nОб’єкт | описове, однина | $activeUser = User::active()->first() | ~~$users, $obj~~\nІндекси в конфігураційних та мовних файлах | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nВигляд | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nКонфігурація | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nДомовленість (інтерфейс) | прикметник або іменник | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | прикметник | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | однини | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Використовуйте, де можливо, короткий та читабельний синтаксис**\n\nПогано:\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\nДобре:\n\n```php\nsession('cart');\n$request->name;\n```\n\nБільше прикладів:\n\nПочатковий синтаксис | Короткий й читабельний синтаксик\n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Використовуйте контейнер IoC або фасади замість new Class**\n\nСинтаксис new Class створює міцні з’єднання між класами та ускладнює тестування. Використовуйте натомість контейнер IoC або фасади.\n\nПогано:\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\nДобре:\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Не отримуйте дані безпосередньо з файлу .env**\n\nНатомість, передавайте дані до конфігураційних файлів та потім використовуйте допоміжну функцію `config()` для використання даних в застосунку.\n\nПогано:\n\n```php\n$apiKey = env('API_KEY');\n```\n\nДобре:\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Використайте дані\n$apiKey = config('api.key');\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Зберігайте дати в стандартному форматі. Використовуйте методи доступу та зміни даних для зміни формату**\n\nПогано:\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\nДобре:\n\n```php\n// Модель\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// Вигляд\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[🔝 Назад до змісту](#Зміст)\n\n### **Інші хороші практики**\n\nЖодної логіки в маршрутах.\n\nЗменшіть використання чистого PHP у шаблонах Blade.\n\n[🔝 Назад до змісту](#Зміст)\n"
  },
  {
    "path": "urdu.md",
    "content": "![Laravel best practices](/images/logo-english.png?raw=true)\n\nYou might also want to check out the [real-world Laravel example application](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## <p dir=\"rtl\">ترجمے:</p>\n\n[Nederlands](https://github.com/Protoqol/Beste-Laravel-Praktijken) (by [Protoqol](https://github.com/Protoqol))\n\n[한국어](https://github.com/xotrs/laravel-best-practices) (by [cherrypick](https://github.com/xotrs))\n\n[日本語](japanese.md) (by [2bo](https://github.com/2bo))\n\n[漢語](chinese.md) (by [xiaoyi](https://github.com/Shiloh520))\n\n[ภาษาไทย](thai.md) (by [kongvut sangkla](https://github.com/kongvut))\n\n[বাংলা](bangla.md) (by [Anowar Hossain](https://github.com/AnowarCST))\n\n[فارسی](persian.md) (by [amirhossein baghaie](https://github.com/amirbagh75))\n\n[Português](https://github.com/jonaselan/laravel-best-practices) (by [jonaselan](https://github.com/jonaselan))\n\n[Українська](ukrainian.md) (by [Tenevyk](https://github.com/tenevyk))\n\n[Русский](russian.md)\n\n[Tiếng Việt](https://chungnguyen.xyz/posts/code-laravel-lam-sao-cho-chuan) (by [Chung Nguyễn](https://github.com/nguyentranchung))\n\n[Español](spanish.md) (by [César Escudero](https://github.com/cedaesca))\n\n[Français](french.md) (by [Mikayil S.](https://github.com/mikayilsrt))\n\n[Polski](polish.md) (by [Karol Pietruszka](https://github.com/pietrushek))\n\n[Türkçe](turkish.md) (by [Burak](https://github.com/ikidnapmyself))\n\n[Deutsch](german.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Italiana](italian.md) (by [Sujal Patel](https://github.com/sujalpatel2209))\n\n[Azərbaycanca](https://github.com/Maharramoff/laravel-best-practices-az) (by [Maharramoff](https://github.com/Maharramoff))\n\n[العربية](arabic.md) (by [ahmedsaoud31](https://github.com/ahmedsaoud31))\n\n[اردو](urdu.md) (by [RizwanAshraf1](https://github.com/RizwanAshraf1))\n \n[![Laravel example app](/images/laravel-real-world-banner.png?raw=true)](https://github.com/alexeymezenin/laravel-realworld-example-app)\n\n## <p dir=\"rtl\">انڈیکس</p>\n\n[<p dir=\"rtl\">واحد ذمہ داری کا اصول</p>](#1)\n\n[<p dir=\"rtl\">بڑے models ، چھوٹے controllers!</p>](#2)\n\n[<p dir=\"rtl\">توثیق</p>](#3)\n\n[<p dir=\"rtl\">کاروباری منطق service class میں ہونی چاہیے۔</p>](#4)\n\n[<p dir=\"rtl\">اپنے آپ کو نہ دہرائیں (DRY)</p>](#5)\n،\n[<p dir=\"rtl\">Query Builderاور raw SQL queries پر Eloquent استعمال کرنے کو  ترجیح دیں ۔ arrays پر collections کو ترجیح دیں۔</p>](#6)\n\n[<p dir=\"rtl\">بڑے پیمانے پر تفویض</p>](#7)\n\n[<p dir=\"rtl\">Blade templates میں queries نہ  چلایئں اور eager loading کا استعمال کریں (N + 1 مسئلہ)</p>](#8)\n\n[<p dir=\"rtl\">اپنے کوڈ پر تبصرہ کریں ، لیکن تبصرے پر وضاحتی method اور variables ناموں کو ترجیح دیں</p>](#9)\n\n[<p dir=\"rtl\">Blade templates میں JS اور CSS نہ ڈالیں اور PHP Classes میں کوئی HTML نہ ڈالیں۔</p>](#10)\n\n[<p dir=\"rtl\">کوڈ میں ٹیکسٹ کی بجائے config، لینگویج فائلز اور constants استعمال کریں۔</p>](#11)\n\n[<p dir=\"rtl\">Laravel کے معیاری ٹولز کا استعمال کریں جو کمیونٹی نے قبول کیے ہیں۔</p>](#12)\n\n[<p dir=\"rtl\">Laravel کےاپنے نام رکھنے کے  طریقوں پر عمل کریں .</p>](#13)\n\n[<p dir=\"rtl\">جہاں ممکن ہو مختصر اور زیادہ پڑھنے کے قابل syntax کا استعمال کریں۔</p>](#14)\n\n[<p dir=\"rtl\">نئی Class کے بجائے IoC کنٹینر یا facades استعمال کریں۔</p>](#15)\n\n[<p dir=\"rtl\">`.env` فائل سے غلطہ راست ڈیٹا حاصل نہ کریں۔</p>](#16)\n\n[<p dir=\"rtl\">تاریخوں کو معیاری شکل میں محفوظ کریں۔ ڈیٹ فارمیٹ میں ترمیم کرنے کے لیے accessors اور mutators کا استعمال کریں۔</p>](#17)\n\n[<p dir=\"rtl\">دوسرے اچھے طریقے۔</p>](#18)\n\n### <p dir=\"rtl\">1</p>\n### **<p dir=\"rtl\">واحد ذمہ داری کا اصول</p>**\n\n<p dir=\"rtl\">ایک class اور ایک method کی صرف ایک ذمہ داری ہونی چاہیے۔</p>\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\npublic function getFullNameAttribute(): string\n{\n    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {\n        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n    } else {\n        return $this->first_name[0] . '. ' . $this->last_name;\n    }\n}\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function getFullNameAttribute(): string\n{\n    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();\n}\n\npublic function isVerifiedClient(): bool\n{\n    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();\n}\n\npublic function getFullNameLong(): string\n{\n    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;\n}\n\npublic function getFullNameShort(): string\n{\n    return $this->first_name[0] . '. ' . $this->last_name;\n}\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">2</p>\n### **<p dir=\"rtl\">بڑے models ، چھوٹے controllers!</p>**\n\n<p dir=\"rtl\">اگر آپ  Query Builder یا raw SQL queries استعمال کر رہے ہیں تو تمام DB سے متعلقہ منطق کو Eloquent models  یا Repository کی classes میں ڈالیں۔</p>\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\npublic function index()\n{\n    $clients = Client::verified()\n        ->with(['orders' => function ($q) {\n            $q->where('created_at', '>', Carbon::today()->subWeek());\n        }])\n        ->get();\n\n    return view('index', ['clients' => $clients]);\n}\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function index()\n{\n    return view('index', ['clients' => $this->client->getWithNewOrders()]);\n}\n\nclass Client extends Model\n{\n    public function getWithNewOrders()\n    {\n        return $this->verified()\n            ->with(['orders' => function ($q) {\n                $q->where('created_at', '>', Carbon::today()->subWeek());\n            }])\n            ->get();\n    }\n}\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">3</p>\n### **<p dir=\"rtl\">توثیق</p>**\n\n<p dir=\"rtl\">توثیق کو controllers سے Request classes میں منتقل کریں۔</p>\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\npublic function store(Request $request)\n{\n    $request->validate([\n        'title' => 'required|unique:posts|max:255',\n        'body' => 'required',\n        'publish_at' => 'nullable|date',\n    ]);\n\n    ...\n}\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function store(PostRequest $request)\n{\n    ...\n}\n\nclass PostRequest extends Request\n{\n    public function rules()\n    {\n        return [\n            'title' => 'required|unique:posts|max:255',\n            'body' => 'required',\n            'publish_at' => 'nullable|date',\n        ];\n    }\n}\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">4</p>\n### **<p dir=\"rtl\">کاروباری منطق service class میں ہونی چاہیے۔</p>**\n\n<p dir=\"rtl\">ایک controller کی صرف ایک ذمہ داری ہونی چاہیے ، لہذا کاروباری منطق کو controller سے service classes  میں منتقل کریں۔</p>\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\npublic function store(Request $request)\n{\n    if ($request->hasFile('image')) {\n        $request->file('image')->move(public_path('images') . 'temp');\n    }\n    \n    ...\n}\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function store(Request $request)\n{\n    $this->articleService->handleUploadedImage($request->file('image'));\n\n    ...\n}\n\nclass ArticleService\n{\n    public function handleUploadedImage($image)\n    {\n        if (!is_null($image)) {\n            $image->move(public_path('images') . 'temp');\n        }\n    }\n}\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">5</p>\n### **<p dir=\"rtl\">اپنے آپ کو نہ دہرائیں (DRY)</p>**\n\n<p dir=\"rtl\">جب ممکن ہو تو کوڈ کو دوبارہ استعمال کریں۔ SRP آپ کو نقل سے بچنے میں مدد دے رہا ہے۔ نیز ، Blade templates کو دوبارہ استعمال کریں </p>\n\n<p dir=\"rtl\"> Eloquent scopesوغیرہ استعمال کریں۔</p>\n \n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\npublic function getActive()\n{\n    return $this->where('verified', 1)->whereNotNull('deleted_at')->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->where('verified', 1)->whereNotNull('deleted_at');\n        })->get();\n}\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function scopeActive($q)\n{\n    return $q->where('verified', 1)->whereNotNull('deleted_at');\n}\n\npublic function getActive()\n{\n    return $this->active()->get();\n}\n\npublic function getArticles()\n{\n    return $this->whereHas('user', function ($q) {\n            $q->active();\n        })->get();\n}\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">6</p>\n### **<p dir=\"rtl\">Query Builderاور raw SQL queries پر Eloquentاستعمال کرنے کو  ترجیح دیں ۔ arrays پر collections کو ترجیح دیں۔</p>**\n\n<p dir=\"rtl\"> Eloquent آپ کو پڑھنے کے قابل اور دیکھ بھال کے قابل کوڈ لکھنے کی اجازت دیتا ہے۔ نیز ، Eloquent کے پاس بلٹ ان ٹولز ہیں جیسے soft deletes, events, scopes وغیرہ۔</p> \n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```sql\nSELECT *\nFROM `articles`\nWHERE EXISTS (SELECT *\n              FROM `users`\n              WHERE `articles`.`user_id` = `users`.`id`\n              AND EXISTS (SELECT *\n                          FROM `profiles`\n                          WHERE `profiles`.`user_id` = `users`.`id`) \n              AND `users`.`deleted_at` IS NULL)\nAND `verified` = '1'\nAND `active` = '1'\nORDER BY `created_at` DESC\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\nArticle::has('user.profile')->verified()->latest()->get();\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">7</p>\n### **<p dir=\"rtl\">بڑے پیمانے پر تفویض</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\n$article = new Article;\n$article->title = $request->title;\n$article->content = $request->content;\n$article->verified = $request->verified;\n\n// Add category to article\n$article->category_id = $category->id;\n$article->save();\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\n$category->article()->create($request->validated());\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">8</p>\n### **<p dir=\"rtl\">Blade templates میں queries نہ  چلایئں اور eager loading کا استعمال کریں (N + 1 مسئلہ)</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n<p dir=\"rtl\">غلط (100 صارفین کے لیے ، 101 DB queries استعمال ہوں گی ):</p>\n\n```blade\n@foreach (User::all() as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n<p dir=\"rtl\">غلط (100 صارفین کے لیے ، 2 DB queries استعمال ہوں گی ):</p>\n\n```php\n$users = User::with('profile')->get();\n\n@foreach ($users as $user)\n    {{ $user->profile->name }}\n@endforeach\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">9</p>\n### **<p dir=\"rtl\">اپنے کوڈ پر تبصرہ کریں ، لیکن تبصرے پر وضاحتی method اور variables ناموں کو ترجیح دیں</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n<p dir=\"rtl\">بہتر طریقہ۔:</p>\n\n```php\n// Determine if there are any joins.\nif (count((array) $builder->getQuery()->joins) > 0)\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\nif ($this->hasJoins())\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">10</p>\n### **<p dir=\"rtl\">Blade templates میں JS اور CSS نہ ڈالیں اور PHP Classes میں کوئی HTML نہ ڈالیں۔</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```javascript\nlet article = `{{ json_encode($article) }}`;\n```\n\n<p dir=\"rtl\">✔️ بہتر طریقہ۔:</p>\n\n```php\n<input id=\"article\" type=\"hidden\" value='@json($article)'>\n\nOr\n\n<button class=\"js-fav-article\" data-article='@json($article)'>{{ $article->name }}<button>\n```\n\nکی Javascript فائل میں۔:\n\n```javascript\nlet article = $('#article').val();\n```\n\n<p dir=\"rtl\">ڈیٹا منتقل کرنے کے لیے خصوصی PHP to JS package  کا استعمال کرنا ہے۔</p>\n\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">11</p>\n### **<p dir=\"rtl\">کوڈ میں ٹیکسٹ کی بجائے config، لینگویج فائلز اور constants استعمال کریں۔</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\npublic function isNormal()\n{\n    return $article->type === 'normal';\n}\n\nreturn back()->with('message', 'Your article has been added!');\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function isNormal()\n{\n    return $article->type === Article::TYPE_NORMAL;\n}\n\nreturn back()->with('message', __('app.article_added'));\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">12</p>\n### **<p dir=\"rtl\">Laravel کے معیاری ٹولز کا استعمال کریں جو کمیونٹی نے قبول کیے ہیں۔</p>**\n\n<p dir=\"rtl\">تھرڈ پارٹی پیکجز اور ٹولز استعمال کرنے کے بجائے بلٹ ان Laravel functionality اور کمیونٹی پیکجز استعمال کرنے کو ترجیح دیں۔ کوئی بھی ڈویلپر جو مستقبل میں آپ کی ایپ کے ساتھ کام کرے گا اسے نئے ٹولز سیکھنے کی ضرورت ہوگی۔ نیز ، جب آپ تھرڈ پارٹی پیکیج یا ٹول استعمال کر رہے ہیں تو Laravel کمیونٹی سے مدد حاصل کرنے کے امکانات نمایاں طور پر کم ہیں۔ اپنے کلائنٹ کو اس کی ادائیگی کرنے پر مجبور نہ کریں ۔\n </p>\n\nکام | معیاری ٹولز  | تھرڈ پارٹی ٹولز\n------------ | ------------- | -------------\nAuthorization | Policies | Entrust, Sentinel and other packages\nCompiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages\nDevelopment Environment | Laravel Sail, Homestead | Docker\nDeployment | Laravel Forge | Deployer and other solutions\nUnit testing | PHPUnit, Mockery | Phpspec, Pest\nBrowser testing | Laravel Dusk | Codeception\nDB | Eloquent | SQL, Doctrine\nTemplates | Blade | Twig\nWorking with data | Laravel collections | Arrays\nForm validation | Request classes | 3rd party packages, validation in controller\nAuthentication | Built-in | 3rd party packages, your own solution\nAPI authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages\nCreating API | Built-in | Dingo API and similar packages\nWorking with DB structure | Migrations | Working with DB structure directly\nLocalization | Built-in | 3rd party packages\nRealtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly\nGenerating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually\nTask scheduling | Laravel Task Scheduler | Scripts and 3rd party packages\nDB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">13</p>\n### **<p dir=\"rtl\">Laravel کےاپنے نام رکھنے کے  طریقوں پر عمل کریں</p>**\n<p dir=\"rtl\">پیروی <a href=\"http://www.php-fig.org/psr/psr-2\">PSR  معیارات </a></p>\n \n <p dir=\"rtl\">نیز ، Laravel کمیونٹی کے ذریعہ قبول کردہ نام رکھنے کے طریقوں  پر عمل کریں:</p>\n\nکیا  | کیسے | درست طریقہ | غلط طریقہ\n------------ | ------------- | ------------- | -------------\nController | singular | ArticleController | ~~ArticlesController~~\nRoute | plural | articles/1 | ~~article/1~~\nRoute name | snake_case with dot notation | users.show_active | ~~users.show-active, show-active-users~~\nModel | singular | User | ~~Users~~\nhasOne or belongsTo relationship | singular | articleComment | ~~articleComments, article_comment~~\nAll other relationships | plural | articleComments | ~~articleComment, article_comments~~\nTable | plural | article_comments | ~~article_comment, articleComments~~\nPivot table | singular model names in alphabetical order | article_user | ~~user_article, articles_users~~\nTable column | snake_case without model name | meta_title | ~~MetaTitle; article_meta_title~~\nModel property | snake_case | $model->created_at | ~~$model->createdAt~~\nForeign key | singular model name with _id suffix | article_id | ~~ArticleId, id_article, articles_id~~\nPrimary key | - | id | ~~custom_id~~\nMigration | - | 2017_01_01_000000_create_articles_table | ~~2017_01_01_000000_articles~~\nMethod | camelCase | getAll | ~~get_all~~\nMethod in resource controller | [table](https://laravel.com/docs/master/controllers#resource-controllers) | store | ~~saveArticle~~\nMethod in test class | camelCase | testGuestCannotSeeArticle | ~~test_guest_cannot_see_article~~\nVariable | camelCase | $articlesWithAuthor | ~~$articles_with_author~~\nCollection | descriptive, plural | $activeUsers = User::active()->get() | ~~$active, $data~~\nObject | descriptive, singular | $activeUser = User::active()->first() | ~~$users, $obj~~\nConfig and language files index | snake_case | articles_enabled | ~~ArticlesEnabled; articles-enabled~~\nView | kebab-case | show-filtered.blade.php | ~~showFiltered.blade.php, show_filtered.blade.php~~\nConfig | snake_case | google_calendar.php | ~~googleCalendar.php, google-calendar.php~~\nContract (interface) | adjective or noun | AuthenticationInterface | ~~Authenticatable, IAuthentication~~\nTrait | adjective | Notifiable | ~~NotificationTrait~~\nTrait [(PSR)](https://www.php-fig.org/bylaws/psr-naming-conventions/) | adjective | NotifiableTrait | ~~Notification~~\nEnum | singular | UserType | ~~UserTypes~~, ~~UserTypeEnum~~\nFormRequest | singular | UpdateUserRequest | ~~UpdateUserFormRequest~~, ~~UserFormRequest~~, ~~UserRequest~~\nSeeder | singular | UserSeeder | ~~UsersSeeder~~\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">14</p>\n### **<p dir=\"rtl\">جہاں ممکن ہو مختصر اور زیادہ پڑھنے کے قابل syntax کا استعمال کریں۔</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\n$request->session()->get('cart');\n$request->input('name');\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\nsession('cart');\n$request->name;\n```\n\n<p dir=\"rtl\">مزید مثالیں:</p>\n\nعام syntax | چھوٹا syntax اور زیادہ پڑھنے کے قابل \n------------ | -------------\n`Session::get('cart')` | `session('cart')`\n`$request->session()->get('cart')` | `session('cart')`\n`Session::put('cart', $data)` | `session(['cart' => $data])`\n`$request->input('name'), Request::get('name')` | `$request->name, request('name')`\n`return Redirect::back()` | `return back()`\n`is_null($object->relation) ? null : $object->relation->id` | `optional($object->relation)->id`\n`return view('index')->with('title', $title)->with('client', $client)` | `return view('index', compact('title', 'client'))`\n`$request->has('value') ? $request->value : 'default';` | `$request->get('value', 'default')`\n`Carbon::now(), Carbon::today()` | `now(), today()`\n`App::make('Class')` | `app('Class')`\n`->where('column', '=', 1)` | `->where('column', 1)`\n`->orderBy('created_at', 'desc')` | `->latest()`\n`->orderBy('age', 'desc')` | `->latest('age')`\n`->orderBy('created_at', 'asc')` | `->oldest()`\n`->select('id', 'name')->get()` | `->get(['id', 'name'])`\n`->first()->name` | `->value('name')`\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">15</p>\n### **<p dir=\"rtl\">نئی Class کے بجائے IoC کنٹینر یا facades استعمال کریں۔</p>**\n\n<p dir=\"rtl\">new Class syntax کے درمیان سخت جوڑا coupling ہے اور testing کو پیچیدہ بناتا ہے۔ اس کے بجائے IoC container یا facades استعمال کریں۔</p>\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\n$user = new User;\n$user->create($request->validated());\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\npublic function __construct(User $user)\n{\n    $this->user = $user;\n}\n\n...\n\n$this->user->create($request->validated());\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">16</p>\n### **<p dir=\"rtl\">`.env` فائل سے غلطہ راست ڈیٹا حاصل نہ کریں۔</p>**\n\n<p dir=\"rtl\">config فائلوں کو ڈیٹا منتقل کریں اور پھر ایپلیکیشن میں ڈیٹا استعمال کرنے کے لیے `()config` ہیلپر فنکشن استعمال کریں۔</p>\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\n$apiKey = env('API_KEY');\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\n// config/api.php\n'key' => env('API_KEY'),\n\n// Use the data\n$apiKey = config('api.key');\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">17</p>\n### **<p dir=\"rtl\">تاریخوں کو معیاری شکل میں محفوظ کریں۔ ڈیٹ فارمیٹ میں ترمیم کرنے کے لیے accessors اور mutators کا استعمال کریں۔</p>**\n\n<p dir=\"rtl\">❌ غلط طریقہ:</p>\n\n```php\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}\n{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}\n```\n\n<p dir=\"rtl\">✔️ درست طریقہ:</p>\n\n```php\n// Model\nprotected $casts = [\n    'ordered_at' => 'datetime',\n];\n\npublic function getSomeDateAttribute($date)\n{\n    return $date->format('m-d');\n}\n\n// ملف العرض\n{{ $object->ordered_at->toDateString() }}\n{{ $object->ordered_at->some_date }}\n```\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n\n### <p dir=\"rtl\">18</p>\n### **<p dir=\"rtl\">دوسرے اچھے طریقے۔</p>**\n\n<p dir=\"rtl\">routes کی فائلوں میں کبھی بھی کوئی منطق نہ ڈالیں۔</p>\n\n<p dir=\"rtl\">Blade templates میں vanilla PHP کا استعمال کم سے کم کریں۔</p>\n\n[<p dir=\"rtl\">🔝 انڈیکس پر واپس جائیں</p>](#انڈیکس)\n"
  }
]