Laravel - ən yaxşı 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;
}

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

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

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

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

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

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

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

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

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.

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

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ət Qeyri-standart alət
Səlahiyyətlər Policies Entrust, Sentinel və digər paketlər və ya şəxsi həll
JS, CSS və s. ilə işlər Laravel Mix Grunt, Gulp, kənar paketlər
Proqramlaşdırma mühiti Homestead Docker
Tətbiqin yerləşdirilməsi Laravel Forge Deployer və digərləri
Testləmə Phpunit, Mockery Phpspec
e2e testləmə Laravel Dusk Codeception
VB ilə iş Eloquent SQL, Query Builder, Doctrine
Şablonlar Blade Twig
Məlumatlarla iş Laravel Collections Massivlər
Form validasiyaları Request sinifləri Kənar paketlər, kontroller daxili validasiya
Daxil olma Daxili funksional Kənar paketlər, şəxsi həll
API ilə daxil olma Laravel Passport JWT işlədən kənar paketlər, OAuth
API yaratma Daxili funksional Dingo API və digər paketlər
VB strukturu ilə iş Miqrasiyalar VB ilə birbaşa işləmək
Lokalizasiya Daxili funksional Kənar paketlər
Real zamanda məlumatlarla iş Laravel Echo, Pusher Kənar paketlər və web-socketlərlə birbaşa işləmək
Test dataların generasiyası Seeder sinifləri, model fabrikləri, Faker Manual daxil etmə və kənar paketlər
Tapşırıq planlaması Laravelin daxili planlaması Skriptlər və kənar paketlər
VB MySQL, PostgreSQL, SQLite, SQL Server MongoDb

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:

Qayda Qəbul olunur Qəbul olunmur
Kontroller təklik ArticleController ArticlesController
Marşrutlar cəmlik articles/1 article/1
Marşrut adları snake_case users.show_active users.show-active, show-active-users
Model təklik User Users
hasOne və belongsTo əlaqələri təklik articleComment articleComments, article_comment
Digər əlaqələr cəmlik articleComments articleComment, article_comments
Cədvəl cəmlik article_comments article_comment, articleComments
Pivot cədvəl təklik (model adları əlifba sırası ilə) article_user user_article, articles_users
Cədvəl sütunu snake_case (model adı olmadan) meta_title MetaTitle; article_meta_title
Model dəyişənləri snake_case $model->created_at $model->createdAt
Kənar açarlar model adı təklikdə və _id article_id  ArticleId, id_article, articles_id
Əsas açar - id custom_id
Miqrasiya - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
Metod camelCase getAll get_all
Resurs kontrollerlərində metod cədvəl store saveArticle
Test metodları camelCase testGuestCannotSeeArticle test_guest_cannot_see_article
Dəyişənlər camelCase $articlesWithAuthor $articles_with_author

 

0

Şərh yazmaq üçün hesabınıza daxil olun.