Çeviriler:
Nederlands (by Protoqol)
한국어 (by cherrypick)
Українська (by Tenevyk)
فارسی (by amirhossein baghaie)
Tiếng Việt (by Chung Nguyễn)
Español (by César Escudero)
Français (by Mikayil S.)
Polski (by Maciej Jeziorski)
Deutsche (by Sujal Patel)
Italiana (by Sujal Patel)
Bu metin Laravel için SOLID prensipleri, patternler vb. şeylerin uygulaması değildir. Burada, Laravel projelerinde geliştiriciler tarafından dikkate alınmayan iyi ve kötü pratiklerin karşılaştırmalarını bulacaksınız.
*Çevirmenin Notu #1: Wikipedia'da Türkçe başlığı olmaması nedeniyle SOLID prensipleri hakkında açıklayıcı bir Ekşi Sözlük entrysi
Single responsibility principle (Tek sorumluluk prensibi)
Büyük modeller, çirkin controllerlar
Business logic servis class'ında bulunmalıdır
Kendini tekrar etme (DRY: Don't repeat yourself)
Query Builder ve düz queryler kullanmak yerine Eloquent, array kullanmak yerine Collection kullanın
Blade templatelerinde asla query çalıştırmayın, eager loading kullanın (N + 1 problemi)
Koda yorum yazın ancak öncelikli olarak anlamlı method ve değişken isimleri seçin
Blade içinde JS ve CSS kullanmayın ve PHP classlarına HTML yazmayın
Config ve language dosyalarını kullanın, kod içinde ise metin kullanmak yerine constant kullanın
Laravel topluluğu tarafından kabul edilen standart araçları kullanın
Mümkün olduğunca daha kısa ve okunabilir syntax kullanın
new Class kullanımı yerine IoC container ya da facade kullanın
.env
dosyasından doğrudan veri çekmeyin
Tarihleri standart formatta kaydedin. Tarihleri formatlamak için accessor ve mutator kullanın
Bir class ya da method'un tek bir görevi ve amacı olmalıdır.
Kötü:
public function getFullNameAttribute()
{
if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
} else {
return $this->first_name[0] . '. ' . $this->last_name;
}
}
İyi:
public function getFullNameAttribute()
{
return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}
public function isVerifiedClient()
{
return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}
public function getFullNameLong()
{
return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}
public function getFullNameShort()
{
return $this->first_name[0] . '. ' . $this->last_name;
}
Bütün database ile ilişkili Query Builder ve raw SQL işlemlerini Eloquent modelinde ya da Repository class'ında tanımlayarak kullanın.
Kötü:
public function index()
{
$clients = Client::verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
return view('index', ['clients' => $clients]);
}
İyi:
public function index()
{
return view('index', ['clients' => $this->client->getWithNewOrders()]);
}
class Client extends Model
{
public function getWithNewOrders()
{
return $this->verified()
->with(['orders' => function ($q) {
$q->where('created_at', '>', Carbon::today()->subWeek());
}])
->get();
}
}
Validation işlemlerini controller içinde değil, Request classları içinde yapın.
Kötü:
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
....
}
İyi:
public function store(PostRequest $request)
{
....
}
class PostRequest extends Request
{
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
];
}
}
Bir controller sadece bir görevden sorumlu olmalıdır. Bu nedenle business logic, controllerlar yerine servis classında tanımlanmalıdır.
Kötü:
public function store(Request $request)
{
if ($request->hasFile('image')) {
$request->file('image')->move(public_path('images') . 'temp');
}
....
}
İyi:
public function store(Request $request)
{
$this->articleService->handleUploadedImage($request->file('image'));
....
}
class ArticleService
{
public function handleUploadedImage($image)
{
if (!is_null($image)) {
$image->move(public_path('images') . 'temp');
}
}
}
Kullanabildiğiniz sürece kodu tekrar kullanın. SRP (single responsibility principle - tek sorumluluk ilkesi) size kod tekrarını azaltmanıza da katkı sunar. Blade templatelerini, Eloqunt scopelarını tekrar kullanın.
Kötü:
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();
}
İyi:
public function scopeActive($q)
{
return $q->where('verified', 1)->whereNotNull('deleted_at');
}
public function getActive()
{
return $this->active()->get();
}
public function getArticles()
{
return $this->whereHas('user', function ($q) {
$q->active();
})->get();
}
Eloquent okunabilir ve sürdürülebilir kod yazmanıza izin verir. Ayrıca, Eloquent güzel dahili araçlara sahiptir. Örnek olarak; soft delete, event ve scope özellikleri verilebilir.
Kötü:
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
İyi:
Article::has('user.profile')->verified()->latest()->get();
Kötü:
$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();
İyi:
$category->article()->create($request->validated());
Kötü (100 kullanıcı için, 101 DB tane query çalıştırılacak):
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
İyi (100 kullanıcı için, 2 DB tane query çalıştırılacak):
$users = User::with('profile')->get();
...
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
Kötü:
if (count((array) $builder->getQuery()->joins) > 0)
Görece İyi:
// Determine if there are any joins.
if (count((array) $builder->getQuery()->joins) > 0)
İyi:
if ($this->hasJoins())
Kötü:
let article = `{{ json_encode($article) }}`;
Daha İyi:
<input id="article" type="hidden" value='@json($article)'>
Ya da
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>
Javascript dosyasında:
let article = $('#article').val();
Data transferi için en iyi yol amaca özel programlanmış PHP'den JS'ye veri aktaran paketleri kullanmaktır.
Kötü:
public function isNormal()
{
return $article->type === 'normal';
}
return back()->with('message', 'Makaleniz eklendi!');
İyi:
public function isNormal()
{
return $article->type === Article::TYPE_NORMAL;
}
return back()->with('message', __('app.article_added'));
Geniş topluluk desteği olmayan paketler yerine Laravel'e dahili gelen ya da topluluk tarafından kabul edilmiş araç ve paketleri kullanın. Bu şekilde koda dahil olan herkesin rahatlıkla projeye katkı sunmasını sağlayabilirsiniz. Ayrıca topluluk desteği düşük olan paketleri ya da araçları kullandığınızda yardım alabilme ihtimaliniz önemli derecede azalmaktadır. Bu paketleri tekrar hazırlamak yerine, tercih edilen paketleri kullanın ve bu maliyetlerden kaçı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.
Yapılacak | Standart araç | 3rd party araçlar |
---|---|---|
Authorization (Yetkilendirme) | Policies | Entrust, Sentinel vb. |
Compiling assets (CSS ve JS Derleme) | Laravel Mix | Grunt, Gulp, 3rd party paketler |
Geliştirme Ortamı | Homestead | Docker |
Deployment | Laravel Forge | Deployer and other solutions |
Unit testing | PHPUnit, Mockery | Phpspec |
Browser testing | Laravel Dusk | Codeception |
DB | Eloquent | SQL, Doctrine |
Template | Blade | Twig |
Veri işleme | Laravel collections | Arrays |
Form doğrulama | Request classları | 3rd party paketler, controllerda doğrulama |
Authentication (Doğrulama) | Dahili | 3rd party paketler ya da kendi çözümünüz |
API authentication (Doğrulama) | Laravel Passport | 3rd party JWT and OAuth packetleri |
API Oluşturma | Dahili | Dingo API vb. |
DB Yapısı | Migrations | Doğrudan DB yönetimi |
Lokalizasyon (Yerelleştirme) | Dahili | 3rd party paketler |
Gerçek zamanlı kullanıcı etkileşimi | Laravel Echo, Pusher | Doğrudan WebSocket kullanan 3rd party paketler |
Test verisi oluşturmak | Seeder classları, Model Factoryleri, Faker | Oluşturup manuel test etmek |
Görev Zamanlama | Laravel Task Scheduler | Scriptler ve 3rd party paketler |
DB | MySQL, PostgreSQL, SQLite, SQL Server | MongoDB |
PSR standards takip edin.
Ayrıca, topluluk tarafından kabul gören isimlendirmeler:
Ne | Nasıl | İyi | Kötü |
---|---|---|---|
Controller | tekil | ArticleController | |
Route | çoğul | articles/1 | |
Named route | snake_case ve dot notation (nokta kullanımı) | users.show_active | |
Model | tekil | User | |
hasOne or belongsTo relationship | tekil | articleComment | |
All other relationships | çoğul | articleComments | |
Table | çoğul | article_comments | |
Pivot table | tekil model isimleri alfabetik sırada | article_user | |
Table column | snake_case ve model adı olmadan | meta_title | |
Model property | snake_case | $model->created_at | |
Foreign key | tekil model adı ve _id suffix'i (soneki) | 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 | tanımlayıcı, çoğul | $activeUsers = User::active()->get() | |
Object | tanımlayıcı, tekil | $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) | sıfat ya da isim | Authenticatable | |
Trait | sıfat | Notifiable |
Kötü:
$request->session()->get('cart');
$request->input('name');
İyi:
session('cart');
$request->name;
Daha çok örnek:
Ortak syntax | Kısa ve daha okunabilir syntax |
---|---|
Session::get('cart') |
session('cart') |
$request->session()->get('cart') |
session('cart') |
Session::put('cart', $data) |
session(['cart' => $data]) |
$request->input('name'), Request::get('name') |
$request->name, request('name') |
return Redirect::back() |
return back() |
is_null($object->relation) ? null : $object->relation->id |
optional($object->relation)->id |
return view('index')->with('title', $title)->with('client', $client) |
return view('index', compact('title', 'client')) |
$request->has('value') ? $request->value : 'default'; |
$request->get('value', 'default') |
Carbon::now(), Carbon::today() |
now(), today() |
App::make('Class') |
app('Class') |
->where('column', '=', 1) |
->where('column', 1) |
->orderBy('created_at', 'desc') |
->latest() |
->orderBy('age', 'desc') |
->latest('age') |
->orderBy('created_at', 'asc') |
->oldest() |
->select('id', 'name')->get() |
->get(['id', 'name']) |
->first()->name |
->value('name') |
new 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.
Kötü:
$user = new User;
$user->create($request->validated());
İyi:
public function __construct(User $user)
{
$this->user = $user;
}
....
$this->user->create($request->validated());
Veriyi config dosyasında çağırın ve config()
helper fonksiyonunu kullanarak uygulama içinde erişin.
Kötü:
$apiKey = env('API_KEY');
İyi:
// config/api.php
'key' => env('API_KEY'),
// Use the data
$apiKey = config('api.key');
Kötü:
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}
İyi:
// Model
protected $dates = ['ordered_at', 'created_at', 'updated_at'];
public function getSomeDateAttribute($date)
{
return $date->format('m-d');
}
// View
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}
Route dosyalarına asla logic yazmayın.
Blade template dosyalarında vanilya PHP (düz PHP) kullanmayın.