Как стать автором
Обновить
1109.81
OTUS
Цифровые навыки от ведущих экспертов

Попрощайтесь с проверками на null и исключениями: использование монады Maybe в Symfony

Время на прочтение3 мин
Количество просмотров7.8K
Автор оригинала: Aleksei Kankov

Введение

Функциональное программирование появилось не вчера. Но оно так и не приобрело какой-либо дикой популярности, и, вероятно, не просто так. Иногда оно может быть довольно сложным с точки зрения понимания и использования. Но у него есть много преимуществ. Одним из них является возможность избежать проверок на null и исключений.

В этой статье мы рассмотрим монаду Maybe и то, как ее можно использовать в Symfony.

Что из себя представляет монада Maybe?

Начать следует с определения самой концепции монады. Монада (monad) — это структура, предназначенная для представления вычислений в виде императивной последовательности шагов. Она является обобщением концепции функции, которая принимает аргумент и возвращает результат. Монады в функциональном программировании не является чем-то новым. Они существуют с 1960-х годов.

Монада Maybe — это монада, которая инкапсулирует необязательное значение. Значение типа Maybe a содержит либо значение типа а (представленное как Just a), либо вообще ничего (представленное как Nothing). Используя монаду Maybe, мы можем избежать null и исключений.

Как использовать монаду Maybe в Symfony?

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

// src/Utils/Maybe.php
<?php

namespace App\Utils;

/**
 * @template T
 */
class Maybe
{
    /**
     * @var T|null
     */
    private $value;

    /**
     * @param T|null $value
     */
    private function __construct($value)
    {
        $this->value = $value;
    }

    /**
     * @param T|null $value
     * @return Maybe<T>
     */
    public static function just($value): Maybe
    {
        return new self($value);
    }

    /**
     * @return Maybe<T>
     */
    public static function nothing(): Maybe
    {
        return new self(null);
    }

    /**
     * @template U
     * @param callable(T):U $fn
     * @return Maybe<U>
     */
    public function map(callable $fn): Maybe
    {
        if ($this->value === null) {
            return self::nothing();
        }
        return self::just($fn($this->value));
    }

    /**
     * @param T $defaultValue
     * @return T
     */
    public function getOrElse($defaultValue)
    {
        return $this->value ?? $defaultValue;
    }
}

Класс Maybe содержит два статических метода: just и nothing. just метод создает объект Maybe со значением.

Метод nothing создает объект Maybe без значения. Метод map принимает в качестве аргумента функцию и применяет ее к значению внутри объекта Maybe. Если значение внутри объекта Maybe null, метод map возвращает nothing. Метод getOrElse возвращает значение из объекта Maybe или значение по умолчанию, если значение внутри объекта Maybe – null.

Давайте посмотрим, как его можно использовать в Symfony-приложении.

// src/Controller/DefaultController.php
<?php

namespace App\Controller;

use App\Entity\User;
use App\Service\UserSrvice;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class DefaultController extends AbstractController
{
    #[Route('', name: 'default')]
    public function getUserData(Request $request, UserSrvice $userSrvice): JsonResponse
    {
        $email = $request->get('email');

        $maybeUser = $userSrvice->getUserByEmail($email);

        $userData = $maybeUser
            ->map(fn(User $user) => [
                'name'  => $user->getName(),
                'email' => $user->getEmail(),
            ])
            ->getOrElse([
                'name'  => 'Unknown',
                'email' => 'Unavailable',
            ])
        ;


        return $this->json($userData);
    }
}
// src/Service/UserSrvice.php
<?php

declare(strict_types=1);

namespace App\Service;

use App\Repository\UserRepository;
use App\Utils\Maybe;

class UserSrvice
{

    public function __construct(private readonly UserRepository $userRepository)
    {
    }

    public function getUserByEmail(string $email): Maybe
    {
        return Maybe::just($this->userRepository->getUserByEmail($email));
    }
}

В классе DefaultController мы получаем электронное письмо из запроса. Затем мы определяем пользователя по этому электронному письму, используя класс UserSrvice.

Класс UserSrvice возвращает объект Maybe. Для получения пользовательских данных мы используем метод map. Если пользователь не найден, метод map возвращает nothing.

Затем мы используем метод getOrElse для получения пользовательских данных или значения по умолчанию, если пользователь не найден.

Заключение

В этой статье мы рассмотрели монаду Maybe и то, как ее можно использовать в Symfony. Мы создали класс Maybe, реализующий монаду Maybe. Мы использовали класс Maybe в классе DefaultController, чтобы избежать проверки на null и необходимости использовать исключения. Используя этот подход, мы можем избежать проверки на null и исключений почти во всем нашем Symfony-приложении, что сделает код более читабельным.

Полный код вы можете найти на GitHub


На днях пройдет открытый урок «Twig и Symfony forms: создаем полноценное веб-приложение без погружения во frontend». На нем разработаем быструю и простую административную панель штатными средствами фреймворка.

Занятие пройдет в преддверии старта курса "Symfony Framework". Записаться на открытый урок можно по ссылке.

Теги:
Хабы:
Всего голосов 12: ↑8 и ↓4+5
Комментарии19

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS