You might also want to check out the real-world Laravel example application and Eloquent SQL reference
Translations:
Nederlands (by Protoqol)
Indonesia (by P0rguy, Doni Ahmad)
한국어 (by cherrypick)
ภาษาไทย (by kongvut sangkla)
বাংলা (by Anowar Hossain)
فارسی (by amirhossein baghaie)
Українська (by Tenevyk)
Tiếng Việt (by Chung Nguyễn)
Español (by César Escudero)
Français (by Mikayil S.)
Polski (by Karol Pietruszka)
Deutsch (by Sujal Patel)
Italiana (by Sujal Patel)
Azərbaycanca (by Maharramoff)
العربية (by ahmedsaoud31)
اردو (by RizwanAshraf1)
မြန်မာဘာသာ (by Kaung Zay Yan)
Method တစ်ခုက အလုပ်တစ်ခုပဲလုပ်သင့်ပါတယ်
Fat models, skinny controllers
Business logic တွေက Service class ထဲမှာပဲ ရှိသင့်တယ်
Query Builder နဲ့ raw SQL queries အစား Eloquent၊ arrays အစား collection ကိုပိုသုံးပါ
Data-heavy tasks တွေအတွက် Chunk data ကိုသုံးပါ
Comment ရေးမဲ့ အစား method နဲ့ variable name တွေကို သေချာပေးခဲ့ပါ
JS နဲ့ CSS ကို blade templates ထဲကို မထည့်ပါနဲ့၊ PHP class တွေထဲမှာ HTML code တွေမထည့်ပါနဲ့
Code ထဲမှာ hard coded စာသားတွေ ထည့်မဲ့အစား config နဲ့ language files တွေကိုသုံးပါ
Community က လက်ခံပြီး အသုံးပြုနေကျ standard laravel tools တွေကိုပဲသုံးပါ
Laravel ရဲ့ အမည်ပေးပုံတွေကိုလိုက်နာပါ
တိုတိုနဲ့ ဖတ်ရလွယ်တဲ့ syntax ကိုတက်နိုင်သမျှသုံးပါ
new Class အစား loC / Service container တွေကိုသုံးပါ
.env
file ကနေ data ကိုတိုက်ရိုက်မယူပါနဲ့
Class တစ်ခုမှာ တာဝန်တစ်ခုပဲရှိသင့်ပါတယ်။
Bad:
public function update(Request $request): string
{
$validated = $request->validate([
'title' => 'required|max:255',
'events' => 'required|array:date,type'
]);
foreach ($request->events as $event) {
$date = $this->carbon->parse($event['date'])->toString();
$this->logger->log('Update event ' . $date . ' :: ' . $);
}
$this->event->updateGeneralEvent($request->validated());
return back();
}
Good:
public function update(UpdateRequest $request): string
{
$this->logService->logEvents($request->events);
$this->event->updateGeneralEvent($request->validated());
return back();
}
Function တစ်ခုက အလုပ်တစ်ခုကိုပဲ သေချာလုပ်သင့်ပါတယ်။
Bad:
public function getFullNameAttribute(): string
{
if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
} else {
return $this->first_name[0] . '. ' . $this->last_name;
}
}
Good:
public function getFullNameAttribute(): string
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerifiedClient(): bool
{
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong(): string
{
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort(): string
{
return $this->first_name[0] . '. ' . $this->last_name;
}
DB logic တွေ အကုန်လုံးကို Eloquent Model ထဲကို ထည့်ပါ။
Bad:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
Good:
public function index()
{
return view('index', ['clients' => $this->client->getWithNewOrders()]);
}
class Client extends Model
{
public function getWithNewOrders(): Collection
{
return $this->verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
}
}
Validation စစ်တာကို controller ထဲမှာ မစစ်ပဲ request class ထဲမှာစစ်ပါ။
Bad:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
...
}
Good:
public function store(PostRequest $request)
{
...
}
class PostRequest extends Request
{
public function rules(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
];
}
}
Controller တစ်ခုက အလုပ်တစ်ခုပဲ လုပ်သင့်တယ်။ Business logic တွေကို သပ်သပ် service class ထဲကိုရွှေ့ပါ။
Bad:
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
...
}
Good:
public function store(Request $request)
{
$this->articleService->handleUploadedImage($request->file('image'));
...
}
class ArticleService
{
public function handleUploadedImage($image): void
{
if (!is_null($image)) {
$image->move(public_path('images') . 'temp');
}
}
}
Code ကိုတက်နိုင်သမျှ ထပ်တစ်လဲလဲပြန်မရေးပဲနဲ့ ပြန်သုံးပါ။ Blade templates၊ Eloquent scopes တွေကိုပြန်သုံးပါ။
Bad:
public function getActive()
{
return $this->where('verified', 1)->whereNotNull('deleted_at')->get();
}
public function getArticles()
{
return $this->whereHas('user', function ($q) {
$q->where('verified', 1)->whereNotNull('deleted_at');
})->get();
}
Good:
public function scopeActive($q)
{
return $q->where('verified', true)->whereNotNull('deleted_at');
}
public function getActive(): Collection
{
return $this->active()->get();
}
public function getArticles(): Collection
{
return $this->whereHas('user', function ($q) {
$q->active();
})->get();
}
Eloquent က code ကို ဖတ်လို့လွယ် ပြုပြင်ဖို့လွယ်စေတယ်။ နောက်ပြီး eloquent မှာ သုံးလို့ကောင်းတဲ့ soft deletes, events, scopes စတဲ့ build-in tools တွေ ပါပါတယ်။ ဒီမှာဝင်ဖတ်ကြည့်လို့ရပါတယ် Eloquent to SQL reference
Bad:
SELECT *
FROM `articles`
WHERE EXISTS (SELECT *
FROM `users`
WHERE `articles`.`user_id` = `users`.`id`
AND EXISTS (SELECT *
FROM `profiles`
WHERE `profiles`.`user_id` = `users`.`id`)
AND `users`.`deleted_at` IS NULL)
AND `verified` = '1'
AND `active` = '1'
ORDER BY `created_at` DESC
Good:
Article::has('user.profile')->verified()->latest()->get();
Bad:
$article = new Article;
$article->title = $request->title;
$article->content = $request->content;
$article->verified = $request->verified;
// Add category to article
$article->category_id = $category->id;
$article->save();
Good:
$category->article()->create($request->validated());
Queries တွေကို Blade Templates တွေထဲမှာ Execute မလုပ်ပဲနဲ့ အဲတာအစား eager loading ကိုအသုံးပြုပါ။ (N + 1 problem)
Bad (user အယောက် ၁၀၀ အတွက် Query ၁၀၁ ခု execute လုပ်ရမယ် ):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
Good (user အယောက် ၁၀၀ အတွက် Query ၂ ခု ပဲ execute ရမယ်):
$users = User::with('profile')->get();
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
Bad:
$users = $this->get();
foreach ($users as $user) {
...
}
Good:
$this->chunk(500, function ($users) {
foreach ($users as $user) {
...
}
});
Bad:
// Determine if there are any joins
if (count((array) $builder->getQuery()->joins) > 0)
Good:
if ($this->hasJoins())
Bad:
let article = `{{ json_encode($article) }}`;
Better:
<input id="article" type="hidden" value='@json($article)'>
Or
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
In a Javascript file:
let article = $('#article').val();
အကောင်းဆုံးကတော့ data transfer ဖို့အတွက် specialized PHP to JS Package တွေကိုသုံးပါ။
Bad:
public function isNormal(): bool
{
return $article->type === 'normal';
}
return back()->with('message', 'Your article has been added!');
Good:
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
3rd party packages နဲ့ tools တွေ သုံးမဲ့အစား build-in laravel functionality တွေနဲ့ community packages တွေကိုသာ ပိုသုံးသင့်ပါတယ်။ မဟုတ်ရင် နောက်ပိုင်းမှာ ကိုယ့် project ကို တစ်ခြား developer တွေ ဆက်ပြီး လုပ်တဲ့အခါမှာ tools အသစ်တွေကိုထပ်ပြီး လေ့လာနေရပါလိမ့်မယ်။ ဒါ့အပြင် တစ်ခြား third party package နဲ့ tool သုံးခဲ့ရင် အဲဒီ tools တွေနဲ့ ပက်သတ်ပြီး community ဆီကနေ အကူအညီရနိုင်ခြေလဲ သိသိသာသာလျော့သွားပါလိမ့်မယ်။ ကိုယ့် client ကို အဲတာအတွက် အပိုမကုန်ပါစေနဲ့။
Task | Standard tools | 3rd party tools |
---|---|---|
Authorization | Policies | Entrust, Sentinel and other packages |
Compiling assets | Laravel Mix, Vite | Grunt, Gulp, 3rd party packages |
Development Environment | Laravel Sail, Homestead | Docker |
Deployment | Laravel Forge | Deployer and other solutions |
Unit testing | PHPUnit, Mockery | Phpspec, Pest |
Browser testing | Laravel Dusk | Codeception |
DB | Eloquent | SQL, Doctrine |
Templates | Blade | Twig |
Working with data | Laravel collections | Arrays |
Form validation | Request classes | 3rd party packages, validation in controller |
Authentication | Built-in | 3rd party packages, your own solution |
API authentication | Laravel Passport, Laravel Sanctum | 3rd party JWT and OAuth packages |
Creating API | Built-in | Dingo API and similar packages |
Working with DB structure | Migrations | Working with DB structure directly |
Localization | Built-in | 3rd party packages |
Realtime user interfaces | Laravel Echo, Pusher | 3rd party packages and working with WebSockets directly |
Generating testing data | Seeder classes, Model Factories, Faker | Creating testing data manually |
Task scheduling | Laravel Task Scheduler | Scripts and 3rd party packages |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
Follow PSR standards.
နောက်ပြီး laravel community က လက်ခံထားတဲ့ အမည်ပေးပုံတွေကိုလိုက်နာပါ။
What | How | Good | Bad |
---|---|---|---|
Controller | singular | ArticleController | |
Route | plural | articles/1 | |
Route name | snake_case with dot notation | users.show_active | |
Model | singular | User | |
hasOne or belongsTo relationship | singular | articleComment | |
All other relationships | plural | articleComments | |
Table | plural | article_comments | |
Pivot table | singular model names in alphabetical order | article_user | |
Table column | snake_case without model name | meta_title | |
Model property | snake_case | $model->created_at | |
Foreign key | singular model name with _id suffix | article_id | |
Primary key | - | id | |
Migration | - | 2017_01_01_000000_create_articles_table | |
Method | camelCase | getAll | |
Method in resource controller | table | store | |
Method in test class | camelCase | testGuestCannotSeeArticle | |
Variable | camelCase | $articlesWithAuthor | |
Collection | descriptive, plural | $activeUsers = User::active()->get() | |
Object | descriptive, singular | $activeUser = User::active()->first() | |
Config and language files index | snake_case | articles_enabled | |
View | kebab-case | show-filtered.blade.php | |
Config | snake_case | google_calendar.php | |
Contract (interface) | adjective or noun | AuthenticationInterface | |
Trait | adjective | Notifiable | |
Trait (PSR) | adjective | NotifiableTrait | |
Enum | singular | UserType | |
FormRequest | singular | UpdateUserRequest | |
Seeder | singular | UserSeeder |
တစ်ချို့ naming conventions တွေကိုလိုက်နာနေရင် တစ်ခြား configuration တွေလုပ်စရာမလိုတော့ဘူး။
Bad:
// Table name 'Customer'
// Primary key 'customer_id'
class Customer extends Model
{
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
protected $table = 'Customer';
protected $primaryKey = 'customer_id';
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');
}
}
Good:
// Table name 'customers'
// Primary key 'id'
class Customer extends Model
{
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class);
}
}
Bad:
$request->session()->get('cart');
$request->input('name');
Good:
session('cart');
$request->name;
နောက်ထပ် ဥပမာများ:
တွေ့မြင်နေကျ syntax | တိုတိုနဲ့ ဖတ်ရလွယ် syntax |
---|---|
Session::get('cart') |
session('cart') |
$request->session()->get('cart') |
session('cart') |
Session::put('cart', $data) |
session(['cart' => $data]) |
$request->input('name'), Request::get('name') |
$request->name, request('name') |
return Redirect::back() |
return back() |
is_null($object->relation) ? null : $object->relation->id |
optional($object->relation)->id (in PHP 8: $object->relation?->id ) |
return view('index')->with('title', $title)->with('client', $client) |
return view('index', compact('title', 'client')) |
$request->has('value') ? $request->value : 'default'; |
$request->get('value', 'default') |
Carbon::now(), Carbon::today() |
now(), today() |
App::make('Class') |
app('Class') |
->where('column', '=', 1) |
->where('column', 1) |
->orderBy('created_at', 'desc') |
->latest() |
->orderBy('age', 'desc') |
->latest('age') |
->orderBy('created_at', 'asc') |
->oldest() |
->select('id', 'name')->get() |
->get(['id', 'name']) |
->first()->name |
->value('name') |
new Class syntax က class တွေအတွင်းမှာ tight coupling ဖြစ်စေတဲ့အပြင် testing လုပ်တဲ့အခါမှာ ပိုပြီး ရှုတ်ထွေးစေတယ်။ အဲ့အစား LoC container နဲ့ facades ကိုသုံးပါ။
Bad:
$user = new User;
$user->create($request->validated());
Good:
public function __construct(protected User $user) {}
...
$this->user->create($request->validated());
အဲလိုလုပ်မဲ့အစား application မှာသုံးရမဲ့ data ကို config files တွေဆီပို့ပြီးတော့ config()
helper function ကိုသုံးပါ။
Bad:
$apiKey = env('API_KEY');
Good:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
ရက်စွဲတွေကို standard format အတိုင်းသိမ်းပါ။ Date format တွေကို modify လုပ်ချင်ရင် accessors နဲ့ mutators ကိုသုံးပါ
ရက်စွဲတစ်ခုကို စာသား(string) အနေနဲ့သိမ်းတာက object instance အနေနဲ့သိမ်းတာလောက် စိတ်မချရဘူး ၊ ဥပမာ Carbon-instance။ Class အချင်းချင်းကြား date string အနေနဲ့ ပေးတာထက် carbon objects အနေနဲ့ပေးတာကို ပိုအားပေးပါတယ်။ ဒေတာပြန်ပြတာကိုတော့ display layer(templates) မှာပဲ လုပ်သင့်ပါတယ်။
Bad:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
Good:
// Model
protected $casts = [
'ordered_at' => 'datetime',
];
// Blade view
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->format('m-d') }}
DocBlocks တွေက ဖတ်ရတာ ပိုခက်စေတယ်။ အဲ့အစား method name ကိုသေချာပေးတာ နဲ့ အသစ်ထွက် PHP feautre တွေဖြစ်တဲ့ return type hints တွေကိုသုံးပါ။
Bad:
/**
* The function checks if given string is a valid ASCII string
*
* @param string $string String we get from frontend which might contain
* illegal characters. Returns True is the string
* is valid.
*
* @return bool
* @author John Smith
*
* @license GPL
*/
public function checkString($string)
{
}
Good:
public function isValidAsciiString(string $string): bool
{
}
Laravel၊ တစ်ခြား ဆင်တူတဲ့(RoR၊ Django)အစရှိတဲ့ frameworks တွေနဲ့စိမ်းတဲ့ patterns တွေ tools တွေကိုမသုံးပါနဲ့။ App တစ်ခုဆောက်ဖို့ကို Symfony (ဒါမှမဟုတ် Spring) ရဲ့ approach ကို သုံးရတာကြိုက်ရင် အဲဒီ framework ကိုပဲသုံးလိုက်သင့်ပါတယ်။
Route file တွေမှာ ဘာlogic မှမထည့်ပါနဲ့။
Vanilla PHP ကို Blade templates တွေမှာ နည်းနိုင်သမျှ နည်းသုံးပါ။
Testing အတွက် in-memory DB ကိုသုံးပါ။
Framework version update လုပ်တာ နဲ့ တစ်ခြား issues တွေမတက်အောင် framework ရဲ့ standard features တွေကိုပြင်မရေးပါနဲ့။
နောက်ထွက် PHP syntax တွေကိုတက်နိုင်သမျှ အသုံးပြုပါ ဒါပေမယ့် ဖတ်ရလွယ်အောင်ရေးဖို့လဲ မမေ့ပါနဲ့။
တကယ်သေချာမသိရင် View Composers နဲ့ တစ်ခြားဆင်တူတဲ့ tools တွေကိုမသုံးပါနဲ့။ များသောအားဖြင့် ပြဿနာကို ဖြေရှင်းဖို့ ပိုကောင်းတဲ့ နည်းတွေရှိပါတယ်။