• Час читання ~1 хв
  • 14.03.2023

Відповідь з вашої програми Laravel - це те, що я б назвав життєво важливим, особливо коли ви створюєте API. Давайте подивимося, як ми можемо посилити наші відповіді.

Багато з нас зазвичай починають з використання допоміжних функцій у наших програмах, оскільки документи та багато навчальних посібників будуть використовувати їх. З них легко почати і зробити саме те, що ви очікуєте від них. Давайте подивимося, як вони виглядають:

return response()->json(
    data: [],
    status: 200,
);

Це трохи перебільшений приклад, ви зазвичай надсилаєте дані і пропускаєте код статусу. Однак для мене звички важко вмирають!

Цей код створить для вас новий JsonResponse і передасть дані та код статусу, готові до повернення. Це працює, і немає нічого поганого в тому, щоб використовувати такий підхід. Якщо ви вже використовуєте це, спосіб активізувати свою гру API тут - додати код статусу, щоб бути більш декларативним у тому, що ви повертаєте.

Просуваючись вперед, ми можемо пропустити використання допоміжних функцій і почати використовувати базовий клас, який створюють допоміжні функції:Мені подобається такий підхід,

return new JsonResponse(
    data: [],
    status: 200,
);

оскільки він менше покладається на помічників і є більш декларативним. Дивлячись на код, ви точно знаєте, що повертається, тому що він знаходиться прямо перед вами, а не абстрагується за помічником. Ви можете нівелювати це, використовуючи постійний або інший спосіб заявити сам код статусу - зробивши це ще більш доступним для читання та розуміння для розробників, які можуть не знати напам'ять усіх кодів статусу. Давайте подивимося, як це може виглядати:

return new JsonResponse(
    data: [],
    status: JsonResponse::HTTP_OK,
);

Клас JsonResponse розширює клас Symfony Response через кілька шарів абстракції, щоб ви могли назвати це безпосередньо - однак ваш статичний аналізатор може скаржитися на це. Я створив пакет під назвою juststeveking/http-status-codePHP Enum, який поверне щось подібне, і його єдина робота - повернути коди статусу. Я віддаю перевагу цьому більш легкому підходу утиліти до подібних речей, оскільки ви точно знаєте, що відбувається і що може зробити цей клас або пакет. Проблема іноді полягає в тому, що клас, який ви використовуєте, робить так багато, що вам доведеться завантажити цю величезну річ в пам'ять, щоб мати можливість повернути ціле значення. Це не має особливого сенсу, тому я рекомендую використовувати спеціальний пакет або клас, щоб керувати цим самостійно. Давайте подивимося, як це виглядає, коли ви це робите:Це значний крок вперед з точки зору ясності того,

return new JsonResponse(
    data: [],
    status: Http::OK->value,
);

наскільки декларативним є наш код. Він легко читається і розуміє, що саме відбувається. Однак ми час від часу створюємо один і той же блок коду, тож як ми можемо вирішити це?

Відповідь досить проста - Класи відповідей. У Laravel є контракт, який ми можемо використовувати під назвою Responsable, який говорить нам, що наш клас повинен мати toResponse метод на ньому. Ми можемо повернути це безпосередньо від наших контролерів, оскільки Laravel без проблем розбере та зрозуміє ці класи. Давайте розглянемо короткий базовий приклад того, як виглядають ці заняття:

class MyJsonResponse implements Responsable
{
    public function __construct(
        public readonly array $data,
        public readonly Http $status = Http::OK,
   ) {}
    public function toResponse($request): Response
    {
        return new JsonResponse(
            data: $this->data,
            status: $this->status->value,
        );
    }
}

Це щось просте у використанні. Однак це не додає ніякої цінності нашому додатку. Це просто абстракція навколо того, що вже є. Давайте розглянемо щось, що може додати більшої цінності нашому додатку.

class CollectionResponse implements Responsable
{
    public function __construct(
        public readonly JsonResourceCollection $data,
        public readonly Http $status = Http::OK,
    ) {}
    public function toResponse($request): Response
    {
        return new JsonResponse(
            data: $this->data,
            status: $this->status->value,
        );
    }
}

Тепер у нас є клас відповідей, який оброблятиме будь-які колекції ресурсів, через які ми проходимо, що робить його дуже багаторазовим для нашої програми. Давайте розглянемо, як ми можемо повернути це в нашому контролері:

return new CollectionResponse(
    data: UserResource::collection(
        resource: User::query()->get(),
    ),
);

він чистіший, має менше дублювання коду і легко замінити статус за замовчуванням, якщо нам це потрібно. Це дає нам користь, яку дали нам методи помічників та клас відповіді Json, але дозволяє нам більше контексту та передбачуваності.

Однак зараз ми зіткнулися з проблемою дублювання коду в інших областях. В рамках самих наших класів відповідей. Багато з них виглядають схожими, різниця лише в тому, що властивості конструктора будуть різними типами. Ми хочемо зберегти контекст використання користувацьких класів відповідей, але ми хочемо уникнути створення чогось із величезним аргументом типу союзу для властивості - коли ми також можемо додати змішане і зробити з цим.

У цій ситуації ви можете або тягнутися до абстрактного класу, щоб розширити, або до риси, щоб додати поведінку до класів, які цього потребують. Особисто я фанат композиції над спадщиною, тому використання риси має для мене більше сенсу.

trait SendsResponse
{
    public function toResponse($request): Response
    {
        return new JsonResponse(
            data: $this->data,
            status: $this->status->value,
        );
    }
}

Найбільша проблема такого підходу полягає в тому, що статичний аналіз буде скаржитися на цей код, тому що риса повинна мати або знати про властивості класу. Однак це щось із простого виправлення.

/**
 * @property-read mixed $data
 * @property-read Http $status
 */

Ми можемо додати цей блок документів до риси, щоб він знав про властивості, до яких має доступ.

Тепер наші класи відповідей будуть набагато простішими у використанні та побудові, з меншим повторенням у нашому коді.

class MessageResponse implements Responsable
{
    use SendsResponse;

    public function __construct(
        public readonly array $data,
        public readonly Http $status = Http::OK,
    ) {}
}

Тепер ми можемо створити всі потенційні відповіді, які нам потрібно легко надіслати, зберігаючи безпеку типу та зменшуючи дублювання коду.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297