yaleksandr89
@yaleksandr89
PHP developer

Как правильно реализовать реализовать классы Родитель->Потомок?

Здравствуйте.
Не так давно стал изучать ООП, поэтому прощу строго не судить :)
Нужно решить такую задачу. Есть класс:
class Subscribe

<?php declare(strict_types=1);
class SubscribeController
{
    public function notify(Request $request)
    {
        $email = $request->json('from.email');
        if (!isset($email)) {
            return self::userError('from.email is missing.');
        }
        if (!is_string($email)) {
            return self::userError('from.email must be a string.');
        }

        $name = $request->json('from.name');
        if (isset($name) && !is_string($name)) {
            return self::userError('from.name must be either null or a string.');
        }

        try {
            $mailer = Factory::createMailer(app()->basePath(), new EmailAddress($email, $name));
            $mailer->notify('тут название события', new EmailAddress('тут почта', 'тут имя'));
        } catch (Exception $e) {
            return self::error($e->getMessage());
        }

        return self::ok();
    }

    privat static function userError($message)
    {
        return self::error($message, 400);
    }

    privat static function error($message, $code = 500)
    {
        return response()->json($message, $code);
    }

    privat static function ok()
    {
        return response()->json("ok");
    }
}


Ранее событие было одно - подписка на новости, сейчас появились варианты (еженедельный дайджест, рассылка по категориями...). Соответственно нужно создать свой класс на тот или иное событие.

Так как в программирование занимаюсь от силы 6-7 месяцев и до этого шпарил исключительно на процедурке, первое что я сделал в такой ситуации - наговнокодил :) т.е. тупо скопировал содержимое этого класса, дал ему другое название и поменял тип события, фактически изменения (не считая переименования класса) были лишь в одной строке:
$mailer->notify('тут название события', new EmailAddress('тут почта', 'тут имя'));

Понял, что так дела не делаются, попытался вспомнить те статьи и видео, которые я смотрел по ООП (Где объяснялось: есть общий класс Животные, а есть классы Собака, Кошка и т.д.). Решил сделать нечто похожее. Сделать один общий класс SubscribeController и extends создать классы New, Didgest и т.д.

Переименовал приватные методы (обрабатывающие ошибки в protect), вынес в класс-родитель все что смог вынести и классы-потомки стали выглядеть примерно так:
Пример класса потомка

class Didgest extends SubscribeController
{
public function notify(Request $request)
    {
        $email = $request->json('from.email');
        if (!isset($email)) {
            return self::userError('from.email is missing.');
        }
        if (!is_string($email)) {
            return self::userError('from.email must be a string.');
        }

        $name = $request->json('from.name');
        if (isset($name) && !is_string($name)) {
            return self::userError('from.name must be either null or a string.');
        }

        try {
            $mailer = Factory::createMailer(app()->basePath(), new EmailAddress($email, $name));
            $mailer->notify('didgest', new EmailAddress('email', 'name'));
        } catch (Exception $e) {
            return self::error($e->getMessage());
        }

        return self::ok();
    }
}


Но даже тут видно, что копипасты крайне много осталось, фактически, в зависимости от типа события, мне нужно лишь менять название здесь:
$mailer->notify('--->NEW<---', new EmailAddress('email', 'name'));
   $mailer->notify('--->DIDGEST<---, new EmailAddress('email', 'name'));


Собственно подскажите, как можно убрать в класс-родитель(SubscribeController):
$email = $request->json('from.email');
        if (!isset($email)) {
            return self::userError('from.email is missing.');
        }
        if (!is_string($email)) {
            return self::userError('from.email must be a string.');
        }

        $name = $request->json('from.name');
        if (isset($name) && !is_string($name)) {
            return self::userError('from.name must be either null or a string.');
        }


Если его убрать в общий класс, то начинают ругаться переменные: $email, $name, что вполне логично, а как их сделать видимыми из класса-родителя в классах-потомках ума не приложу :( или может вы подскажите, какой-то более элегантный способ в такой ситуации.

Буду очень признателен за помощь!
  • Вопрос задан
  • 124 просмотра
Пригласить эксперта
Ответы на вопрос 2
maksim92
@maksim92
Нашел решение — пометь вопрос ответом!
Паттерн диспетчер событий.

https://github.com/ElisDN/yii2-demo-shop/blob/mast...

Там где нужно вызвать https://github.com/ElisDN/yii2-demo-shop/blob/mast...
Обработчик https://github.com/ElisDN/yii2-demo-shop/blob/mast...

Зависимости через DI
https://github.com/ElisDN/yii2-demo-shop/blob/mast...
Ответ написан
gzhegow
@gzhegow
Думал, стану умнее, когда адаптируюсь, но нет
Вот как я понимал события

<?php

$events = [];

$on = function (string $name, callable $func) use (&$events)
{
  $events[ $name ][] = $func;
};

$fire = function (string $name, $emitter, ...$arguments) use (&$events)
{
  if (! isset($events[ $name ])) return;
  
  foreach ($events[ $name ] as $func) {
    call_user_func($func, $name, $emitter, ...$arguments);
  }
};

// когда машина выйдет с завода - поехали!
$on('car-created', function ($event, $car, ...$comments) { var_dump($car, $comments); });

// ...some application code

// пришло время, тачка готова.
$fire('car-created', $car, 'faster', 'darling');


Что тут есть. Есть массив, хранилище, где будут лежать инструкции $func "что сделать", привязанные на ключ $name - "когда сделать".

Есть функция $on, которая тупо на некое имя записывает в хранилище что делать.
Есть функция $fire, которая "стреляет" - начинает выполнять всё, что висит на этом `$name`. В параметре эмиттер может быть что угодно, что душе хочется - обычно это виновник торжества, из-за чего событие стрельнуло.

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

Вот когда понимаешь что события, это вот так вот просто, сверху можешь на них навесить классы, чтобы понять что эвентс лучше хранить в классе "Диспетчер", а типы событий тоже могут быть классами вместо `$name`, тогда в них можно какие-то функции зашить внутрь.

Пока просто вот так отнесись к этому
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы