Как уведомить о существовании записи из сервиса?

Обсуждаем, как правильно строить приложения
Ответить
Аватара пользователя
SiZE
Сообщения: 2817
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Как уведомить о существовании записи из сервиса?

Сообщение SiZE »

Хотел бы обсудить практику работы с исключениями. Когда стоит их применять и когда нет.

Например, в методе сервиса необходимо для пользователя добавить связанную сущность. Если такая уже есть, надо уведомить об этом. Я это представляю как-то так:

Код: Выделить всё

class SomethingService
{
    /**
     * Exist
     */
    const EX_CODE_EXIST = 100;
    
    /**
     * Create
     * @throw 
     */
    public function create (UserIdentityInterface $user)
    {
        if ($user->something !== null) {
            throw new \yii\base\Exception('Something already exist', self::EX_CODE_EXIST)
        }
               
        $model = new Something();
        $model->userId = $user->id;
        if (!$model->save(false)) {
            throw new \yii\base\Exception('Can not create something model.');
        }
               
        return $model;
    }
} 
и потом в контроллере например обработать

Код: Выделить всё

try {
    $service = new SomethingService();
    $service->create(Yii::$app->user->identity);
} catch (\yii\base\Exception $e) {
    if ($e->getCode() === SomethingService::EX_CODE_EXIST) {
        // logic one
    } else {
        // logic two
    }
}
Или лучше на каждый чих создавать свое исключение и обрабатывать его? Может какие-то стандартные исключения больше подходят для таких вещей?
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Как уведомить о существовании записи из сервиса?

Сообщение ElisDN »

Для логики можно использовать доменные исключения и прочие наследники LogicException, а для системных ошибок - семейство исключений рантайма:

Код: Выделить всё

throw new \DomainException('Something already exist.');
...
throw new \RuntimeException('Can not create something model.'); 
Красиво можно выводить только ошибки доменные:

Код: Выделить всё

try {
    $this->service->create(Yii::$app->user->identity);
} catch (\DomainException $e) {
    Yii::$app->session->addFlash('error', $e->getMessage());
} 
Остальные системные пусть вываливаются как обычно и пишутся в логи.

Если реализовываем валидацию внутри сервисов (например, для CommandBus), то делаем своё исключение с полем для ошибок:

Код: Выделить всё

throw new ValidationException($errors); 
и отдельно обрабатываем его:

Код: Выделить всё

try {
    $this->commandBus->execute(new SmthCreateCommand(Yii::$app->user->id));
    return $this->redirect(['index']);
} catch (ValidationException $e) {
    $form->addErrors($e->getErrors());
} catch (\DomainException $e) {
    Yii::$app->session->addFlash('error', $e->getMessage());
} 
Последний раз редактировалось ElisDN 2016.10.29, 17:44, всего редактировалось 2 раза.
Аватара пользователя
SiZE
Сообщения: 2817
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: Как уведомить о существовании записи из сервиса?

Сообщение SiZE »

ElisDN писал(а):Для логики можно использовать доменные исключения и прочие наследники LogicException, а для системных ошибок - семейство исключений рантайма
Попался на такой штуке:

Код: Выделить всё

try {
    $service = new SubscriptionCreateService();
    $service->setUser($registrationModel->user);
    $service->createStandart();
} catch (\LogicException $e) {
    if ($e->getCode()) {
        $result['error'] = $e->getCode();
        $result['message'] = $e->getMessage();
    } else {
        // do nothing
    }
}
Внутри $service->createStandart(); я использовал только \DomainException и \RuntimeException.

Я не учел, что если, например, неправильно написать имя вызываемого метода, будет брошен \yii\base\UnknownMethodException, который является наследником \LogicException. В итоге вместо ошибки я выводил, что все ок.

На всякий случай написал, вдруг кому-то пригодится в будущем.
Ответить