Home

Awesome

sdadsd

Hədəfimiz Laravel üçün SOLID, Dizayn şablonları və s. kimi bəlli təcrübələri təkrarlamaq deyil, əksinə, məhz real Laravel layihələrində nəzərə alınmayan təcrübələri bir araya toplamaqdır.

Mündəricat

Tək öhdəlik prinsipi (Single responsibility principle)

İncə kontrollerlər, dolğun modellər

Validasiya

Biznes məntiqi xidməti (Service) siniflərdə

Kodunu təkrarlama (DRY: Don't repeat yourself)

Sorğu yaradıcısının (query builder) və verilənlər bazasına birbaşa sorğuların əvəzinə Eloquent istifadə edin. Massivlərlə işləmək üçün kolleksiyalara üstünlük verin

Toplu doldurma istifadə edin (mass assignment)

View fayllarında sorğular yazmayın və xəsis yükləmədən istifadə edin (N + 1 problemi)

Kodlarınızı şərh edin, amma daha da yaxşısı oxunaqlı metod adlarına üstünlük verin

Blade Şablonlarında JS və CSS, PHP Kodunda isə HTML yazmayın

Kodda mətn yazmaq əvəzinə config, dil sənədləri və sabitlər istifadə edin

Laravel toplumunun qəbul etdiyi standart vasitələrdən və təcrübələrdən istifadə edin

Toplumun adlandırma konvensiyalarına riayət edin

Mümkün olduqca qısa və oxunaqlı sintaksis istifadə edin

"new Class" əvəzinə IoC və ya facade istifadə edin

.env sənədindəki məlumatlarla birbaşa işləməyin

Tarixləri standart formatda qeyd edin. Digər formata çevirmək üçün isə accessor və mutatorlardan istifadə edin

Digər tövsiyə və təcrübələr

Tək öhdəlik prinsipi (Single responsibility principle)

Hər bir sinif və metod ancaq bir işə cavabdeh olmalıdır.

Pis:

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;
    }
}

Yaxşı:

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;
}

🔝 Başa qayıt

İncə kontrollerlər, dolğun modellər

Eloquent ilə işləyərkən modellərə, Query Builder və ya standart SQL sorguları ilə işləyərkən isə Repository siniflərinə üstünlük verin.

Pis:

public function index()
{
    $clients = Client::verified()
        ->with(['orders' => function ($q) {
            $q->where('created_at', '>', Carbon::today()->subWeek());
        }])
        ->get();

    return view('index', ['clients' => $clients]);
}

Yaxşı:

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();
    }
}

🔝 Başa qayıt

Validasiya

İncə kontroller və SRP prinsiplərinə əsasən, validasiya işlərini Request siniflərinə köçürün.

Pis:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publish_at' => 'nullable|date',
    ]);

    ....
}

Yaxşı:

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',
        ];
    }
}

🔝 Başa qayıt

Biznes məntiqi xidməti (Service) siniflərdə

Kontroller yalnız birbaşa öz vəzifələrini yerinə yetirməlidir, ona görə də biznes məntiqini başqa siniflərə və service siniflərinə köçürün.

Pis:

public function store(Request $request)
{
    if ($request->hasFile('image')) {
        $request->file('image')->move(public_path('images') . 'temp');
    }
    
    ....
}

Yaxşı:

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');
        }
    }
}

🔝 Başa qayıt

Kodunu təkrarlama

Bu prinsip, bir dəfə yazdığınız kodu mümkün qədər lazım olan hər yerdə istifadə etməyə çağırır. Əgər siz SRP prinsipinə riayət edirsinizsə, təkrarlardan zatən qaçmış olursunuz, amma Laravel də view və bəzi Eloquent sorğularını təkrar istifadə etməyə imkan verir.

Pis:

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();
}

Yaxşı:

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();
}

🔝 Başa qayıt

Sorğu yaradıcısının (query builder) və verilənlər bazasına birbaşa sorğuların əvəzinə Eloquent istifadə edin. Massivlərlə işləmək üçün kolleksiyalara üstünlük verin

Eloquent maksimum oxunaqlı kod yazmağa imkan verir, onun funksionallığını dəyişmək isə olduqca sadədir. Eloquentdə həmçinin, bir-sıra digər rahat və güclü alətlər var.

Pis:

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

Yaxşı:

Article::has('user.profile')->verified()->latest()->get();

🔝 Başa qayıt

Toplu doldurma istifadə edin

Pis:

$article = new Article;
$article->title = $request->title;
$article->content = $request->content;
$article->verified = $request->verified;
// Məqaləni kateqoriya ilə əlaqələndir.
$article->category_id = $category->id;
$article->save();

Yaxşı:

$category->article()->create($request->validated());

🔝 Başa qayıt

View fayllarında sorğular yazmayın və xəsis yükləmədən istifadə edin (N + 1 problemi)

Pis (100 istifadəçi üçün verilənlər bazasına 101 sorğu gedəcək):

@foreach (User::all() as $user)
    {{ $user->profile->name }}
@endforeach

Yaxşı (100 istifadəçi üçün verilənlər bazasına cəmi 2 sorğu gedəcək):

$users = User::with('profile')->get();

...

@foreach ($users as $user)
    {{ $user->profile->name }}
@endforeach

🔝 Başa qayıt

Kodlarınızı şərh edin, amma daha da yaxşısı oxunaqlı metod adlarına üstünlük verin

Pis:

if (count((array) $builder->getQuery()->joins) > 0)

Nisbətən yaxşı:

// Join olub olmadığını müəyyənləşdirin.
if (count((array) $builder->getQuery()->joins) > 0)

Yaxşı:

if ($this->hasJoins())

🔝 Başa qayıt

Blade Şablonlarında JS və CSS, PHP Kodunda isə HTML yazmayın

Pis:

let article = `{{ json_encode($article) }}`;

Nisbətən yaxşı:

<input id="article" type="hidden" value='@json($article)'>

Və ya

<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}<button>

JavaScript kodu:

let article = $('#article').val();

Məlumatları backend-dən frontend-ə ötürmək üçün xüsusi bir paket istifadə etmək daha yaxşı olar.

🔝 Başa qayıt

Kodda mətn yazmaq əvəzinə config, dil sənədləri və sabitlər istifadə edin

Kodda birbaşa hər-hansı mətn olmamalıdır.

Pis:

public function isNormal()
{
    return $article->type === 'normal';
}

return back()->with('message', 'Sizin məqalə uğurla əlavə olundu!');

Yaxşı:

public function isNormal()
{
    return $article->type === Article::TYPE_NORMAL;
}

return back()->with('message', __('app.article_added'));

🔝 Başa qayıt

Laravel toplumunun qəbul etdiyi standart vasitələrdən və təcrübələrdən istifadə edin

Laraveldə tez-tez rast gəlinən məsələlərin həlli üçün standart alətlər mövcuddur. Üçüncü tərəf paketləri və alətlərdən istifadə etmək əvəzinə Laravelin öz alətlərdən istifadə etməyə üstünlük verin. Əks halda sizdən sonra layihəyə gələn Laravel developer, onun üçün yeni olan alətləri öyrənməyə vaxt itirməli, onlarla işləməli və bu alətlərin verə biləcəyi fəsadlarla mübarizə aparmalı olacaq. Bu səbəbdən toplumdan kömək almaq da çətin olacaq. Müştərinizi və ya işəgötürəninizi velosipedləriniz üçün əvəz ödəməyə məcbur etməyin.

MəsələStandart alətQeyri-standart alət
SəlahiyyətlərPoliciesEntrust, Sentinel və digər paketlər və ya şəxsi həll
JS, CSS və s. ilə işlərLaravel MixGrunt, Gulp, kənar paketlər
Proqramlaşdırma mühitiHomesteadDocker
Tətbiqin yerləşdirilməsiLaravel ForgeDeployer və digərləri
TestləməPhpunit, MockeryPhpspec
e2e testləməLaravel DuskCodeception
VB ilə işEloquentSQL, Query Builder, Doctrine
ŞablonlarBladeTwig
Məlumatlarla işLaravel CollectionsMassivlər
Form validasiyalarıRequest sinifləriKənar paketlər, kontroller daxili validasiya
Daxil olmaDaxili funksionalKənar paketlər, şəxsi həll
API ilə daxil olmaLaravel PassportJWT işlədən kənar paketlər, OAuth
API yaratmaDaxili funksionalDingo API və digər paketlər
VB strukturu ilə işMiqrasiyalarVB ilə birbaşa işləmək
LokalizasiyaDaxili funksionalKənar paketlər
Real zamanda məlumatlarla işLaravel Echo, PusherKənar paketlər və web-socketlərlə birbaşa işləmək
Test dataların generasiyasıSeeder sinifləri, model fabrikləri, FakerManual daxil etmə və kənar paketlər
Tapşırıq planlamasıLaravelin daxili funksionalıSkriptlər və kənar paketlər
VBMySQL, PostgreSQL, SQLite, SQL ServerMongoDb

🔝 Başa qayıt

Toplumun adlandırma konvensiyalarına riayət edin

Kod yazarkən PSR standartlarına əməl edin.

Həmçinin adlandırma ilə bağlı digər razılıqlara da əməl edin:

QaydaQəbul olunurQəbul olunmur
KontrollertəklikArticleControllerArticlesController
Marşrutlarcəmlikarticles/1article/1
Marşrut adlarısnake_caseusers.show_activeusers.show-active, show-active-users
ModeltəklikUserUsers
hasOne və belongsTo əlaqələritəklikarticleCommentarticleComments, article_comment
Digər əlaqələrcəmlikarticleCommentsarticleComment, article_comments
Cədvəlcəmlikarticle_commentsarticle_comment, articleComments
Pivot cədvəltəklik (model adları əlifba sırası ilə)article_useruser_article, articles_users
Cədvəl sütunusnake_case (model adı olmadan)meta_titleMetaTitle; article_meta_title
Model dəyişənlərisnake_case$model->created_at$model->createdAt
Kənar açarlarmodel adı təklikdə və _idarticle_idArticleId, id_article, articles_id
Əsas açar-idcustom_id
Miqrasiya-2017_01_01_000000_create_articles_table2017_01_01_000000_articles
MetodcamelCasegetAllget_all
Resurs kontrollerlərində metodcədvəlstoresaveArticle
Test metodlarıcamelCasetestGuestCannotSeeArticletest_guest_cannot_see_article
DəyişənlərcamelCase$articlesWithAuthor$articles_with_author
Kolleksiyalartəsvir edici cəmlikdə$activeUsers = User::active()->get()$active, $data
Obyekttəsvir edici təklikdə$activeUser = User::active()->first()$users, $obj
Config və dil fayllarında indekslərsnake_casearticles_enabledArticlesEnabled; articles-enabled
Viewkebab-caseshow-filtered.blade.phpshowFiltered.blade.php, show_filtered.blade.php
Config faylsnake_casegoogle_calendar.phpgoogleCalendar.php, google-calendar.php
Kontrakt (interface)sifət və ya isimAuthenticatableAuthenticationInterface, IAuthentication
TraitsifətNotifiableNotificationTrait

🔝 Başa qayıt

Mümkün olduqca qısa və oxunaqlı sintaksis istifadə edin

Pis:

$request->session()->get('cart');
$request->input('name');

Yaxşı:

session('cart');
$request->name;

Digər nümunələr:

Tez-tez istifadə olunan sintaksisDaha yığcam və oxunaqlı sintaksis
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->idoptional($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')

🔝 Başa qayıt

"new Class" əvəzinə IoC və ya facade istifadə edin

Sinflərin "new Class" Sintaksisi ilə tətbiq edilməsi, tətbiq hissələri arasında güclü bir bağ yaradır və testi çətinləşdirir. Bunun üçün IoC konteyner və ya facade istifadə edin.

Pis:

$user = new User;
$user->create($request->validated());

Yaxşı:

public function __construct(User $user)
{
    $this->user = $user;
}

....

$this->user->create($request->validated());

🔝 Başa qayıt

.env sənədindəki məlumatlarla birbaşa işləməyin

Məlumatları .env faylından konfiqurasiya faylına köçürün və bu məlumatları istifadə etmək üçün tətbiqdə 'config ()' istifadə edin.

Pis:

$apiKey = env('API_KEY');

Yaxşı:

// config/api.php
'key' => env('API_KEY'),

// Tətbiqin konfiqurasiya faylını istifadə edin
$apiKey = config('api.key');

🔝 Başa qayıt

Tarixləri standart formatda qeyd edin. Digər formata çevirmək üçün isə accessor və mutatorlardan istifadə edin

Pis:

{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}
{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }}

Yaxşı:

// Model
protected $dates = ['ordered_at', 'created_at', 'updated_at'];
// Oxucu (accessor)
public function getSomeDateAttribute($date)
{
    return $date->format('m-d');
}

// Şablon
{{ $object->ordered_at->toDateString() }}
{{ $object->ordered_at->some_date }}

🔝 Başa qayıt

Digər tövsiyə və təcrübələr

Marşrutlarda məntiq yazmayın.

Blade şablonlarında çalışın standart PHP istifadə etməyin.

🔝 Başa qayıt