50 نکته برای کد نویسی تمیز در لاراول (نکته‌های 50-25)

50 نکته برای کد نویسی تمیز در لاراول (نکته‌های 50-25)

در بخش اول از مقاله‌ی 50 نکته برای کد نویسی تمیز در لاراول، با 25 مورد از نکته های جالب برای نوشتن کد تمیز و خوانا، آشنا شدیم. در این بخش 25 نکته‌ی دوم را با شما در میان خواهیم گذاشت. فراموش نکنید این موارد بدون ترتیب یا اولویت بندی بوده و هر کدام در جای خود، می تواند مهم باشد. 

26. برای ()Where های پیچیده، scope های query برای ایجاد کنید. 

در لاراول به جای نوشتن جمله های پیچیده ی ()scope ،Where های query را با نام های واضح ایجاد کنید. برای مثال، این کار باعث می شود کنترلرها کمتر در مورد ساختار پایگاه داده بدانند و کد شما تمیزتر شود.

// Bad
Order::whereHas('status', function ($status) {
    return $status->where('canceled', true);
})->get();
// Good
Order::whereCanceled()->get();


class Order extends Model

{
    public function scopeWhereCanceled(Builder $query)
    {
        return $query>whereHas('status', function ($status) {
                return $status->where('canceled', true);
            });
    }

}

27. از متد های مدل لاراول، برای دریافت داده ها استفاده نکنید. 

اگر می خواهید برخی از داده ها را از یک مدل بازیابی کنید، یک Accessor ایجاد کنید. متد هایی را برای چیزهایی که به نوعی مدل را تغییر می دهند، حفظ کنید. یا برای مثال تاریخ ها را در قالب استاندارد ذخیره کنید. برای تغییر فرمت تاریخ از accessor ها و mutator ها استفاده کنید. 

// Bad
$user->gravatarUrl();


class User extends Authenticable

{
    // ...

    public function gravatarUrl()
    {
        return "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
    }

}
// Good
Suser->gravatar_url;


class User extends Authenticable

{
    // ...

    public function getGravatarUrlAttribute()
    {
        return "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email)));
    }

}

28. از فایل های پیکربندی شخصی سازی شده استفاده کنید.

می توانید مواردی مانند "نتایج در هر صفحه" را در فایل های پیکربندی ذخیره کنید. اما آن ها را به فایل پیکربندی app اضافه نکنید. بهتر است فایل پیکربندی سفارشی برای آن بسازید. به عنوان مثال، در یک پروژه ی تجارت الکترونیک، می توانید از config/shop.php استفاده کنید. 

// config/shop.php
return [
    'vat rates' => [
        0.21,
        0.15,
        0.10,
        0.0,
    ],
    'fee_vat_rate' => 0.21,

    'image_sizes' => [
        'base' => 500, // detail
        't1'   => 250, // index
        't2'   => 50,  // search
    ],
];

29. پرهیز از flag ها

وقتی صحبت از interface های عمومی می‌شود، در مورد نیاز به flag برای تغییر عملکردهای مختلف، مراقب باشید. از خودتان بپرسید که آیا ایجاد یک متد جدید نیاز به flag را به طور کامل حذف می کند یا نه. 

30. استفاده از Eloquent لاراول

Eloquent به شما امکان می دهد کدهای قابل خواندن و نگه داری بنویسید. همچنین، Eloquent دارای ابزارهای داخلی عالی مانند soft delete ها، event ها، scope ها و غیره است. 

31. استفاده از Repository در زمان مناسب 

اگر از Query Builder یا کوئری های raw SQL استفاده می‌کنید و مدل‌های خیلی بزرگ و کنترلر های طولانی دارید، بهتر است تمام منطق مربوط به DB را در مدل‌های Eloquent یا در کلاس‌های Repository قرار دهید. 

32. از فضای نام (namespace) کنترلر استفاده نکنید. 

به جای نوشتن action های کنترلر مانند PostController@index، از syntax آرایه ی قابل فراخوانی [PostController::class, 'index'] استفاده کنید. با این کار، IDE این ابزار را دارد که با کلیک روی PostController، بتوانید به آن کلاس بروید. 

// Bad
Route::get('/posts', 'PostController@index');
// Good
Route::get('/posts', [PostController::class, 'index']);

33. کنترلرها را تک کاره (single-action) در نظر بگیرید. 

اگر action پیچیده ای دارید، آن را به یک کنترلر جداگانه منتقل کنید. برای CreateOrderController ،OrderController::create را ایجاد می کنید. راه حل دیگر این است که این منطق را به یک کلاس action منتقل کنید. بهترین کار را متناسب با شرایط خودتان انجام دهید. 

// We use class syntax from the tip above.
Route::post('/orders/', CreateOrderController::class);


class CreateOrderController

{
    public function _invoke(Request $request)
    {
        // ...
    }

}

34. با IDE خود دوست باشید. 

افزونه ها را نصب کنید، حاشیه نویسی کنید، از typehint ها استفاده کنید. IDE به شما کمک می کند تا کد خود را به درستی پیاده سازی کنید و به شما امکان می دهد انرژی بیشتری را صرف نوشتن کدهایی کنید که قابل خواندن هستند. 

$products = Product::with('options')->cursor();


foreach ($products as $product) {
    /** @var Product $product */
    if ($product->options->isEmpty()) {
        // ...
    }

}

///////////////////////////

foreach (Order::whereDoesntHave('invoice')->whereIn('id', $orders->pluck('id'))->get() as $order) {
    /** @var Order $order */
    $order->createInvoice();

    // ...
}

///////////////////////////

$productImage
    ->help('Max 2 MB')
    ->store(function (NovaRequest $request, ProductModel $product) {
        /** @var UploadedFile $image */
        $image = $request->image;

        // ...
    });

35. از عملگرهای کوتاه استفاده کنید. 

PHP اپراتورهای بسیار خوبی دارد که در صورت امکان می توانند جایگزین کد های بد شوند. آن ها را به خاطر بسپارید. 

// Bad

// truthy test
if (! $foo) {
    $foo = 'bar';
}

// null test
if (is_null($foo)) {
    $foo = 'bar';
}

// isset test
if (! isset($foo)) {
    $foo = 'bar';
}
// Good

// truthy test
$foo = $foo ?: 'bar';

// null test
$foo = $foo ?? 'bar';
// PHP 7.4
$foo ??= 'bar';

// isset test
$foo = $foo ?? 'bar';
// PHP 7.4
$foo ??= 'bar';

36. در مورد فضای خالی بین عملگر ها تصمیم بگیرید که آیا وجود داشته باشند یا حذف شوند. 

در تکه کد بالا می بینید که از فاصله بین بلوک های کد استفاده شده است. در جاهای مناسب این کار را انجام دهید زیرا باعث تمیز شدن کد می شود. 

37. Helper ها به جای Facade ها 

در لاراول به جای Facade، از Helper استفاده کنید. آن ها می توانند همه چیز را تمیز کنند. این کار تا حد زیادی یک ترجیح شخصی است، اما فراخوانی یک تابع global به جای نیاز به وارد کردن یک کلاس و فراخوانی static یک متد، بهتر است. 

// Bad
Cache::get('foo');
// Good
cache()->get('foo');
// Better
cache('foo');

38. دستور های Blade شخصی سازی شده را برای منطق تجاری ایجاد کنید.

می‌توانید با ایجاد دستور های شخصی سازی شده، الگوهای Blade خود را واضح تر کنید. به عنوان مثال، به جای بررسی این که آیا کاربر  نقش (rule) admin را دارد، می توانید از admin@ استفاده کنید. 

// Bad
@if(auth()->user()->hasRole('admin'))
    // ...
    @else
    // ...
    @endif
// Good
@admin
  // ...
@else
  // ...
@endadmin

39. در صورت امکان از کوئری زدن در Blade خودداری کنید.

گاهی اوقات ممکن است بخواهید کوئری های DB را در blade لاراول اجرا کنید. موارد استفاده ی خوب برای این کار وجود دارد، مانند فایل های layout. اما اگر یک کنترلر، view را بر می گرداند، به جای آن، داده ها را از طریق view ارسال کنید. 

// Bad
@foreach(Product::where('enabled', false)->get() as $product)
    // ...
@endforeach
// Good

// Controller
return view('foo', [
    'disabledProducts' => Product::where('enabled', false)->get(),
]);

// View
@foreach($disabledProducts as $product)
    // ...
@endforeach

40. از مقایسه دقیق و سخت گیرانه (strict) استفاده کنید. 

استفاده از === یا !== به جای == یا != برای مقایسه های دقیق پیشنهاد می شود. همچنین بهتر است برای بررسی سخت گیرانه، مشخص کنید که متغیر ها دقیقا از چه نوعی باید باشند. این کار از انتقال متغیرهای نوع داده ی اشتباه، به توابع جلوگیری می کند. 

// Bad
$foo == 'bar';
// Good
$foo === 'bar';
// Better
declare(strict_types=1);

41. از docblock ها (بلاک های کامنت) فقط زمانی استفاده کنید که چیز‌های مبهم را روشن کنند.

بسیاری از افراد با این کار مخالف خواهند بود، زیرا آن ها این کار را انجام می دهند. اما برای استفاده از docblock ها در صورتی که اطلاعات اضافی ارائه نمی دهند، هیچ فایده ای وجود ندارد. اگر typehint به اندازه کافی باشد، دیگر نیازی به یک docblock اضافه نیست زیرا فقط باعث کثیف کاری در کد می شود. 

// Bad

// No types at all
function add_5($foo)

{
    return $foo + 5;
}

// The @param annotation adds precisely 0% value and 100% noise.
/**
 * Add 5 to a number.
 *
 * @param int $foo
 * @return int
 */
function add_5(int $foo): int
{
    return $foo + 5;
}
// Good

// Everything is clear without a docblock
function add_5(int $foo)

{
    return $foo + 5;
}

// The typehint said as much as it could & the annotation said even more.
/**
 * Turn words into a sentence.
 *
 * @param string[] $words
 * @return string
 */
function sentenceFromWords(array $words): string
{
    return implode(' ', $words) . '.';
}

// Personal favourite. Only use the annotations that bring value. Don't use description or @return just because it's so common.
/** @param string[] $words */
function sentenceFromWords(array $words): string
{
    return implode(' ', $words) . '.';
}

42. برای قواعد اعتبارسنجی، یک کد واحد معتبر داشته باشید.

اگر attribute های برخی منابع را در چند مکان validate می‌کنید، به طور قطع می‌خواهید این قوانین اعتبارسنجی را متمرکز کنید، به طوری که آن ها را در یک مکان تغییر ندهید، اما مکان های دیگر را فراموش کنید. اغلب قوانین اعتبارسنجی را در یک متد روی مدل نگه داری می کنند. این کار به ما امکان می دهد تا هر کجا که نیاز داشته باشم، از جمله در کنترلرها یا فرم های درخواست، از آن ها مجدد استفاده کنیم. 

class Reply extends Model

{
    public static function getValidationRules(): array
    {
        return [
            'thread_id' => ['required', 'integer'],
            'user_id'   => ['required', 'integer'],
            'body'      => ['required', 'string', new SpamRule()],
        ];
    }

}

43. از collection ها زمانی استفاده کنید که بتوانند کد شما را تمیز کنند.

همه ی آرایه ها را فقط به این دلیل که لاراول این امکان را ارائه می دهد به collection تبدیل نکنید، اما زمانی که می توانید از دستور collection برای پاکسازی کد استفاده کنید، آرایه ها را به collection تبدیل کنید. 

$collection = collect([
    ['name' => 'Regena', 'age' => null],
    ['name' => 'Linda', 'age' => 14],
    ['name' => 'Diego', 'age' => 23],
    ['name' => 'Linda', 'age' => 84],
]);

$collection->firstWhere('age', '>=', 18);

44. زمانی که به نفع شماست، کد functional بنویسید.

کد functional (عملکردی) هم می‌تواند همه چیز را تمیز کند و هم درک آن ها را غیرممکن کند. Loop های رایج را به فراخوانی های functional تبدیل کنید، اما به منظور اجتناب از نوشتن یک حلقه، کد پیچیده ننویسید. یک مورد استفاده، برای هر دو وجود دارد. 

// Bad
return array_unique(array_reduce($keywords, function ($result, $keyword) {
    return array_merge($result, array_reduce($this->variantGenerators, function ($result2, $generator) use ($keyword) {
        return array_merge($result2, array_map(function ($variant) {
            return strtolower($variant);
        }, $generator::getVariants($keyword)));
    }, []));
}, []));
// Good
return $this->items()->reduce(function (Money $sum, OrderItem $item) {
    return $sum->addMoney($item->subtotal());
}, money(0, $this->currency));

45. کامنت ها معمولا نشان دهنده ی طراحی ضعیف کد هستند. 

قبل از نوشتن کامنت، از خودتان بپرسید که آیا می توانید نام برخی چیزها را تغییر دهید یا متغیرهایی را برای بهبود خوانایی ایجاد کنید. اگر این امکان پذیر نیست، کامنت را به گونه ای بنویسید که هم همکارانتان و هم شما 6 ماه بعد متوجه شوید. 

46. Context اهمیت دارد.

در بالا گفتیم که انتقال منطق تجاری به کلاس های action/service خوب است. اما context مهم است. برای یک طراحی خوب در کد می توانید از ریپازیتوری محبوب Laravel best practices استفاده کنید. برای مثال استفاده از چند خط کد برای بررسی یا انجام کاری، در جایی که می توان آن را بهبود داده کوتاه تر کرد، به نظر بیش از حد پیچیده است.

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

47. Blade include های یک بار مصرف ایجاد کنید. 

مشابه trait های single-use (یک بار مصرف) می توانید این کار را انجام دهید. این تاکتیک زمانی عالی است که یک قالب بسیار طولانی دارید و می خواهید آن را قابل مدیریت تر کنید. هیچ مشکلی با include@ در header و footer طرح بندی ها یا مواردی مانند فرم های پیچیده در نمایش صفحه، وجود ندارد.

48. از اختصار ها استفاده نکنید.

استفاده از نام متغیر/متد طولانی، اشتباه نیست. باید بدانیم که نام ها بیان کننده کاری که متد یا متغیر انجام می دهد، هستند. بهتر است یک متد طولانی‌تر را فراخوانی کنید تا یک متد کوتاه و docblock را بررسی کنید تا بفهمید چه کار می‌کند. در مورد متغیرها هم همین طور. از اختصارهای سه حرفی استفاده نکنید. 

// Bad
$ord = Order::create($data);

// ...

$ord->notify();
// Good
$order = Order::create($data);

// ...

$order->sendCreatedNotification();

49. به کار بردن درست فلسفه های micro و macro

استفاده از برخی فلسفه های " macro" برای ساختار کد، مانند معماری شش ضلعی یا DDD، شما را نجات نخواهد داد. یک پایگاه کد تمیز، نتیجه تصمیم های خوب در سطح micro است. 

50. فقط از چیز هایی استفاده کنید که کمک کننده هستند و موارد دیگر را نادیده بگیرید. 

هدف ما نوشتن کد خواناتر است. هدف این نیست که کاری را که کسی در اینترنت گفته است، انجام دهیم. این نکات فقط تاکتیک هایی هستند که به تمیز شدن کد کمک می کنند. هدف نهایی خودتان را در ذهن داشته باشید و از خود بپرسید "آیا این بهتر است؟ یا نه؟". تصمیم با شماست. 

علاوه بر مواردی که در این مقاله و بخش اول آن ارائه شد، نکته های دیگری نیز وجود دارد اما ما سعی کردیم مهم ترین نکته ها را برایتان شرح دهیم. فراموش نکنید تمرین و تکرار باعث می شود روی آن ها تسلط پیدا کنید. فکر می کنم برای شروع خوب باشد که به کد های گذشته ی خود مراجعه کرده و بر اساس این نکته ها، آن ها را بررسی و اصلاح کنید. امیدوارم مطالعه ی این مقاله برایتان مفید بوده باشد. لطفا نظر های خودتان را با ما در میان بگذارید. 

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس