Восемь способов писать чистый и лаконичный код в Laravel

Источник: «8 Ways Laravel Helps You Write Clean and Terse Code»
Определение чистого кода субъективно. Оно часто становится источником горячих споров между разработчиками в Интернете. В этой статье мы рассмотрим возможности Laravel, позволяющие писать меньше кода.

1. Фасады

С момента своего появления фасады Laravel оказались крайне противоречивыми. Тем не менее они обеспечивают интуитивно понятный и простой интерфейс, который нравится многим.

Фасады предоставляют "статический" интерфейс к классам, доступным в сервис-контейнере приложения. Laravel поставляется с большим количеством фасадов, предоставляющих доступ практически ко всем возможностям Laravel.

— Документация Laravel

Вот краткая иллюстрация того, как они работают. Когда вы пишете Cache::get('foo'), родительский класс Illuminate\Support\Facades\Facade запускает магический метод __callStatic(), перенаправляющий ваш вызов на тот экземпляр объекта, который в данный момент зарегистрирован в контейнере. Если вы рассмотрите Illuminate\Support\Facades\Cache, то увидите, что он привязан к строке cache.

namespace Illuminate\Support\Facades;

class Cache extends Facade
{
protected static function getFacadeAccessor()
{
return 'cache';
}
}

Затем эта строка используется для получения соответствующего экземпляра кэша Repository из сервис-контейнера. Считайте, что это причудливая таблица поиска.

$lookup = [
'cache' => Illuminate\Cache\Repository::class,
];

Естественно, это несколько сложнее, чем в приведённом примере, но общая идея вам понятна.

Для того чтобы это было понятно, я рекомендую узнать больше о сервис-контейнере Laravel. Но не волнуйтесь, он не так сложен, как кажется. Рассматривайте его как большую умную коробку, способную находить и создавать объекты.

Возможно, вам поможет этот эпизод Laracasts: Toy Chests and Contracts.

Если бы не было фасадного подхода, мы бы использовали традиционное внедрение зависимостей, например, так:

<?php

namespace App\Http\Controllers;

use Illuminate\Cache\Repository;

class FooController extends Controller
{
public function __construct(
protected Repository $cache
) {
}

public function foo()
{
$bar = $this->cache->get('bar');
}
}

Это вполне нормальный и распространённый способ внедрения зависимостей, однако новичкам или тем, кто надеется на более лаконичный синтаксис, могут понравиться фасады Laravel. Как и все остальное, они являются необязательной функцией.

<?php

namespace App\Http\Controllers;

class FooController extends Controller
{
public function foo()
{
$bar = Cache::get('bar');
}
}

Прежде чем перейти к следующему разделу, следует помнить, что использование Laravel хэлпера cache() практически идентично вызову фасада Cache. Скажем так, это просто вопрос предпочтений.

Подробнее о фасадах можно узнать из официальной документации.

2. Неявное связывание

Вместо того чтобы ручного извлечения ресурсов маршрута из базы данных, почему бы не делегировать это Laravel?

Неявное связывание начинается в файле routes/web.php. Вы должны изменить свой способ объявления маршрутов следующим образом:

-Route::get('/blog/{id}', [PostController::class, 'show']);
+Route::get('/blog/{post}', [PostController::class, 'show']);

Если в объявлении маршрута указан параметр {post} (по имени соответствующей модели), то ресурс Post можно получить автоматически, без необходимости вручную писать запрос на его получение из базы данных:

 <?php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
- public function show(int $id)
+ public function show(Post $post)
{
- $post = Post::findOrFail($id);
-
return view('posts.show', compact('post'));
}
}

Под капотом Laravel, конечно, по прежнему запрашивает базу данных, но теперь эта часть скрыта от глаз, что делает контроллер более чистым на вид.

Более подробную информацию можно найти в официальной документации.

3. Автоматическое внедрение

Laravel может волшебным образом внедрять зависимости в ваш код. Вы наверняка имели дело с объектом Request в Laravel, не так ли? И я готов поспорить, что вам никогда не приходилось создавать его экземпляр самостоятельно. Чаще всего вы получаете его через контроллер, например, так:

<?php

namespace App\Http\Controllers;

class PostController extends Controller
{
public function store(Request $request)
{
//
}
}

Просто запросите экземпляр Request, и Laravel предоставит его вам! Отлично!

Но как это работает? Опять, это не волшебство. Используя возможности PHP Reflection, Laravel автоматически внедряет экземпляр того, что вы указали в методе store() (не только Illuminate\Http\Request).

Конечно, если вы предпочитаете использовать внедрение конструктора, то этот метод также работает:

<?php

namespace App\Http\Controllers;

class PostController extends Controller
{
public function __construct(
public Request $request
) {
}

public function store()
{
$this->request->validate();

//
}
}

Такая автоматическое введение зависимостей работает не только для контроллеров, но и внутри консольных команд, заданий и т.д.

4. Перенаправление маршрутов

Маршрутизатор Laravel содержит несколько сюрпризов, способных порадовать тех, кто постоянно ищет способы разгрузить свой код.

Например, представим, что вы хотите перенаправить старый URL на новый. Стали бы вы создавать целый контроллер только для однострочного перенаправления? Конечно, можно поступить следующим образом:

Route::get('/old', function () {
return redirect('/new');
});

Или можно использовать замыкания, сделав его более лаконичным:

Route::get('/old', fn () => redirect('/new'));

Однако можно пойти ещё дальше и использовать метод redirect(), например, так:

Route::redirect('/old', '/new');

Все три приведённых выше фрагмента кода выполняют одно и то же действие. Давайте выберем самый короткий. Круто, да?

5. Маршруты представления

В том же духе Laravel может подстроиться под вас и в том случае, если вам нужно зарегистрировать только маршрут, отображающий простое представление. Вместо того чтобы создавать контроллер или маршрут на основе замыкания, обратитесь к методу view().

Route::view('/about', 'pages.about');

Теперь путь /about будет обслуживать представление, расположенное в resources/views/pages/about.

6. Коллекции

Коллекции в Laravel — это, по сути, обёртка вокруг массивов, предоставляющая множество невероятно полезных методов для работы с ними. Вы, несомненно, убедитесь, что они обеспечивают гораздо более согласованную работу по сравнению с собственными (и неудобными) функциями PHP.

Необходимо пройтись циклом по массиву? Оберните его в коллекцию и используйте для этого более современный объектно-ориентированный синтаксис:

collect([1, 2, 3])->each(function ($item) {
//
});

Следующий вопрос заставит вас задуматься. Хотите отправить пользователю инвойс по электронной почте? Используйте сообщения высшего порядка, чтобы сохранить чистоту кода:

$user->invoices()->get()->each->sendByEmail();

Коллекции не только лаконичны, но и гораздо более интуитивны. Вы больше никогда не будете задаваться вопросом, какая стрелка идёт первой или второй!

7. Conditionable/условный трейт

Conditionable — это простой PHP-трейт, поставляемый в Laravel. Он включает в себя всего два метода: when() и unless().

namespace Illuminate\Support\Traits;

use Closure;
use Illuminate\Support\HigherOrderWhenProxy;

trait Conditionable
{
public function when($value = null, callable $callback = null, callable $default = null)
{

}

public function unless($value = null, callable $callback = null, callable $default = null)
{

}
}

Его можно встретить во многих классах фреймворка, таких, как Builder, Factory, Logger, PendingRequest и Carbon. Он предлагает способ условного применения логики с помощью удобного API, а не традиционных выражений if.

Один из распространённых примеров — работа с конструктором запросов Eloquent. Например, вместо того, чтобы написать:

$query = Model::query();

if ($something) {
$query->where('something', true);
} else {
$query->where('something_else', true);
}

$models = $query->get();

Можно выбрать более гибкий подход, используя метод when().

$models = Model::query()->when(
$something,
fn ($query) => $query->where('something', true),
fn ($query) => $query->where('something_else', true),
)->get();

Мне это очень нравится. А вам?

8. Бесконечные собственные пакеты

Конечно, одно из самых сильных достоинств Laravel, о котором мы ещё не говорили, — это обширная коллекция как собственных, так и сторонних пакетов.

Эти пакеты решают общие задачи, избавляя разработчиков от кошмарной необходимости выполнять всю работу с нуля. Представьте себе биллинг, аутентификацию, отладку, флаги возможностей и многое другое… достаточно лишь выполнить команду composer require.

Приведём несколько примеров популярных официальных пакетов Laravel:

И несколько примеров популярных пакетов сторонних производителей:

Используя эти пакеты, вы можете переложить ответственность на других людей, которые, возможно, имеют больший опыт и регулярно работают над пакетами. Это не только экономит время и силы, но и обеспечивает чистоту и лаконичность кодовой базы.

В конце концов, зачем изобретать велосипед, если есть специалисты, которые уже создали и доработали необходимые компоненты?

Заключение

Независимо от стиля написания кода, единственное, что действительно важно, — это результат и удовольствие. Поэкспериментируйте с различными способами написания кода, о которых я рассказал в этой статье, и посмотрите, что из этого получится!

Дополнительные материалы

Предыдущая Статья

Начните тестировать Laravel код меньше чем за 5 минут

Следующая Статья

Отзывчивая типографика с clamp