Laravel урок №41: [ Наблюдатель. Observer. Обсервер ]

Поділитися
Вставка
  • Опубліковано 18 лис 2024

КОМЕНТАРІ • 102

  • @Dmitriy-webdev
    @Dmitriy-webdev 5 років тому +60

    Если у Вас не работают наблюдатели, рекомендую следующее:
    1. Досмотреть видео до конца, т.к. о том как зарегистрировать наблюдателей через сервис-провайдер рассказывается в конце (22:00).
    2. Зарегистрировать наблюдателей через сервис-провайдер в методе boot().
    3. Если создаете свой сервис-провайдер:
    3.1. Проверить зарегистрирован ли Ваш сервис-провайдер в котором Вы зарегистрировали наблюдателей: dd(app())
    3.2. Если Вы уверены что Ваш сервис провайдер создан и зарегистрирован в config/app.php, но dd(app()) не показывает Ваш сервис-провайдер в списке сервис-провайдеров - пропишите в консоли:
    - composer dump-autoload
    - php artisan config:cache
    Надеюсь это сообщение поможет кому-то сэкономить время. Ставьте лайк, чтобы сообщение показывалось выше.

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому +2

      Прикрепил

    • @fitter2boss72
      @fitter2boss72 4 роки тому

      @@DmitryAfanasyev Как лучше регистрировать обсервер, через сервис-провайдер (я так понимаю он будет загружеться всегда) или $this->model->oserve(BlogPostObserver::class) в конструкторе нашего BlogPostRepository? (если правильно понимаю тут он будет грузиться при обращегии к модели)

    • @DmitryAfanasyev
      @DmitryAfanasyev  4 роки тому +1

      Можно и так и эдак. Плюс регистрации в модели - сразу появляется понимание что есть наблюдатель и происходят доп операции при манипуляции с моделью. Плюс регистрации в сервис провайдере - всё в одном месте скумулирована и видна вся картина - сколько обсерверов и для каких моделей. Выбирай сам как тебе удобно. Когда и как они подгружаются, всегда или при обращении - не исследовал. Найдёшь ответ - просвети плз!

    • @fitter2boss72
      @fitter2boss72 4 роки тому

      @@DmitryAfanasyev Из практики, количество обсерверов в проектах обычно не велико?
      Представил проект на 50-70 моделей с таким же количеством обсерверов.
      Посмотрел, что если через сервис провайдер загружать, то ничего дополнительного на старте не подгружается. Интересно, что при выполнении сохранения после редактирования класс обсервера создается дважды.

    • @tatianakruglaya6760
      @tatianakruglaya6760 3 роки тому +1

      Lava 8 doc : To register an observer, you need to call the observe method on the model you wish to observe. You may register observers in the boot method of your application's App\Providers\EventServiceProvider .

  • @vitaercx
    @vitaercx 3 роки тому +2

    Спасибо. Одно из лучших для понимания видео, по моему мнению. Часто вы много теоритезируете и витьевато рассказывете. А тут лаконично и ясно.

  • @retvain
    @retvain 3 роки тому +4

    Очень круто, спасибо вам! Все больше убеждаюсь, что чем больше я узнаю, тем больше понимаю что я нифига не знаю еще))

  • @ChelovekPeople
    @ChelovekPeople 5 років тому +18

    Терминал Linux команды:
    php artisan make:observer BlogCategoryObserver --model=Models/BlogCategory
    php artisan make:observer BlogPostObserver --model=Models/BlogPost

    • @dmitryocheretko703
      @dmitryocheretko703 4 роки тому +1

      Это не линукс команды)) Это консольные команды ларавеля

    • @tatianakruglaya6760
      @tatianakruglaya6760 3 роки тому +1

      Lava 8
      php artisan make:observer BlogCategoryObserver --model=BlogCategory
      php artisan make:observer BlogPostObserver --model=BlogPost

  • @romanbush5164
    @romanbush5164 2 роки тому +1

    Оболдеть так в ларавел реализовано реактивной програмирование reative x, своя интепритация, но идея схожая

  • @alexandernepomniushchyi1008
    @alexandernepomniushchyi1008 4 роки тому +3

    С каждым уроком мир программирования для меня расширяется)). Спасибо за видео!

  • @alexanderdera1266
    @alexanderdera1266 5 років тому +13

    Досмотрел до тестирования. Протестировал. Как у автора не получилось. Дальнейшие действия:
    Обновлялся по апдейт гайду Тейлора первая мысль что-то не так сделал. Вернулся назад просмотрел еще раз, проверил. Результатов не дало. Вернулся назад сделал как в видео по переходу на 5.8. Результат тот же. Опять пересмотрел, проверил. Ошибок не нашел. Полез в документацию ларавел->обсервер. Подключил))) Все заработало))) Продолжил смотреть и..... 22:00. На все про все потрачено 2 дня(с перерывами на работу))). Я не знаю как себя после этого назвать))) Вывод: не стоит спешить))))))
    P.S. Автору за труд спасибо. Очень жду уроков про слушатели

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому

      На самом деле время не потрачено. Навык поиска ошибок очень важен. Его ты и потренировал сейчас.

    • @квадратя
      @квадратя 5 років тому +3

      фаааааааааак, сам щас час сидел искал где же я забыл чо написать. Тоже задумался почему мы нигде не указали что у нас есть обсерверы, но подумал что ларка сама как то там хитро линкует эти вещи. Спасибо))

    • @jurafree426
      @jurafree426 5 років тому +1

      Можно было и подробнее описать вот в этом месте...
      > Полез в документацию ларавел->обсервер. Подключил)))

    • @SLA-Z7
      @SLA-Z7 5 років тому

      @@jurafree426 ну это же очевидно... Это элементарные основы ООП. Прекращай смешить народ. Тут порой и так смешно бывает ))))))))))))))

    • @jurafree426
      @jurafree426 5 років тому +3

      @@SLA-Z7 ничерта не очевидно. Тут бывают начинающие совсем. И при воспроизведении урока может быть совсем не очевидно почему не получается так как у автора урока. Сразу лезешь в комментарии и видишь там ответ - не теряешь время.

  • @ruslangreen1434
    @ruslangreen1434 5 років тому +3

    Отличный курс. Почти 90% получается с первой попытки. Остальные 10% - невнимательность. Спасибо, Дима! Всего тебе самого хорошего!

  • @СергейН-в7в
    @СергейН-в7в Рік тому +1

    01:40 - Observer - теория
    05:00 - BlogPostObserver, BlogCategoryObserver
    10:40 - методы/события обзервера BlogPost для выполнения ДО основной операции
    12:10 - полезные методы модели для обсервера
    22:00 - регистрация обзервера
    23:25 - архитектура хранения обзерверов

  • @alexeynaumov5896
    @alexeynaumov5896 5 років тому +3

    Спасибо за уроки. При выполнении команды php artisan make:observer namespace модели нужно указаывать в одинарных кавычках, если в нем содержатся слэши: php artisan make:observer BlogCategoryObserver --model='Models\BlogCategory'.

    • @NoNameUkr
      @NoNameUkr 5 років тому

      красавчег))не пришлось биться головой что не так)

    • @квадратя
      @квадратя 5 років тому +1

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

    • @viadzmakzbiearusi2214
      @viadzmakzbiearusi2214 5 років тому +1

      ​@@квадратя
      В линуксе в обратную сторону слэш в терминале для пути

  • @DmitryAfanasyev
    @DmitryAfanasyev  5 років тому +6

    Топ донатеры:
    ► Константин [ ⚝ Меценат ]
    Сообщение: "Спасибо за отличные уроки по Laravel";
    .
    ► Дмитрий Лейко
    Сообщение: "Спасибо за уроки!!! Удачи!";
    .
    ► Andrew
    Сообщение: "На пиуко) P.S. Спасибо за крутой курс!";
    .
    ► Виталий Котов
    Сообщение: "Привет! Спасибо за видео, но к сожалению застрял в самом начале, не могу двигаться дальше, интернет перерыл, но по английски я не очень(что бы читать форумы), а по русски не нашел ничего, а что нашел, то не работает. Ответь пожалуйста на мой вопрос..";
    .
    ► Паша Гудман
    Сообщение: "Не так давно наткнулся на ваш канал с целью грамотного подхода к изучению Laravel и, в целом, процесса разработки. Спасибо за то, что вы делаете. Удачи в вашем деле.";
    .
    ► Ярослав;
    .
    ► Дмитрий
    Сообщение: "Спасибо за уроки, смотрю параллельно еще курс, такой нудный в отличие от твоего, так держать"
    ;
    .
    ► Руслан Васильчиков
    Сообщение: "Наконец-то я продвинулся дальше миграций в изучении Laravel. ** * как-то не зашел ;)"
    ;
    .
    ► Александр;
    .
    ► Бахтиёр
    ;
    Спасибо за поддержку, друзья!;

  • @denyschernyshov4557
    @denyschernyshov4557 5 років тому +2

    Спасибо большое за видео урок, Дмитрий! Если делать всё по видео - то будет работать. Объяснение материала очень хорошее и понятное.

  • @akbako
    @akbako 4 роки тому

    Много чего узнал благодаря вашим урокам

  • @JaanPajusalu
    @JaanPajusalu 5 років тому

    Спасибо за очередную порцию полезной информации

  • @dmitryocheretko703
    @dmitryocheretko703 4 роки тому

    отличный урок

  • @evgenroev5147
    @evgenroev5147 4 роки тому

    Спасибо классный урок

  • @DrZlad
    @DrZlad 5 років тому

    Спасибо Вам большое!

  • @vladmogilevetc2426
    @vladmogilevetc2426 4 роки тому +2

    Еще можно немного апнуть генерацию слага
    Если есть 2 статьи с одинаковым заголовком, то слаг, который в таблице unique, сгенерит одинаковый для обоих постов
    я накинул метод класса обсервера, который чекает не занятый ли слаг, а в апдейте в цикле докидую циферки если занят
    так для двух статей с заголвком Title, слаги будут title и title_1 соответственно
    protected static function isFreeSlug($slug, $except_id)
    {
    return !BlogPost::where('slug', '=', $slug)->where('id', '', $except_id)->exists();
    }
    protected function setSlug(BlogPost $blogPost)
    {
    if (empty($blogPost->slug)) {
    $slug = str_slug($blogPost->title);
    $i = 0;
    while(true) {
    if (BlogPostObserver::isFreeSlug($slug, $blogPost->id)) {
    break;
    } else {
    $slug .= "_" . strval(++$i);
    }
    }
    $blogPost->slug = $slug;
    }
    }

  • @bobpps
    @bobpps 5 років тому

    Как всегда плюс!

  • @dmitryocheretko703
    @dmitryocheretko703 4 роки тому

    отлично

  • @romanbush5164
    @romanbush5164 2 роки тому

    Обсервер для логики мутации полей ввода вывода.
    репозиторий книжный шкаф, найти и выдать данные. Что тогда в модели хранить, только связи?

  • @ЯрославАндроид-й7ч
    @ЯрославАндроид-й7ч 5 років тому

    Спасибо

  • @valentinknoll4106
    @valentinknoll4106 5 років тому

    Danke!!!

  • @КириллБузюк
    @КириллБузюк 3 роки тому

    Спасибо за видео. Такой вопрос, каким образом лучше сделать sync для many-to-many отношений? В Observer-е сохраняемой модели это не возможно, выходит или в контроллере или есть более правильный способ?

  • @pavlogalas9065
    @pavlogalas9065 5 років тому

    Привет Дмитрий, спасибо ище раз за курс. У меня вопрос :) Мы поставили в базе категорий и постов индекс SLUG - UNIQUE и когда я прописываю SLUG который уже существоем у меня ошибка. Где лучше всего поставить проверку на существование слага ? В том же Observer?

  • @nickfist9187
    @nickfist9187 3 роки тому +1

    Спасибо за уроки еще раз. Правильно ли я понял, что обсы - это аналог триггеров sql на стероидах?

    • @DmitryAfanasyev
      @DmitryAfanasyev  3 роки тому

      В какой-то степени. Обсервер можно обойти.

    • @nickfist9187
      @nickfist9187 3 роки тому

      @@DmitryAfanasyev Понял. А за отзывчивость отдельное спасибо!

  • @АндрейЧугунцев
    @АндрейЧугунцев 5 років тому

    Спасибо! Все хорошо, все понятно! Есть ли еще отличия обработки событий через обсервер от обработки через события - подписчики? Такое впечатление что обсервер отрабатывает самым последним.
    П.С. Комменты и доки надо писать в стиле Laravel (каждая следующая строка на три символа короче предыдущей!) :-)

  • @Pavelbrov
    @Pavelbrov 5 років тому

    Спасибо за качественный материал. Понятнее некуда. Есть вопросы:
    1. В конце видео сказано, что можно вынести в обсерверы в отдельный service provider. Как это сделать?
    2. Как изучать laravel не только по документации? Например, в видео используются методы isDirty, getOriginal, а в документации их нет, как узнавать об этом?

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому +2

      1) Создаешь новый файл (php artisan make:provider ObserversServiceProvider) и все обсерверы инициализируешь там.2) laravel.com/api/master/Illuminate/Database/Eloquent/Concerns/HasAttributes.html#method_isDirty

  • @handleftman
    @handleftman 5 років тому +3

    Спасибо за курс, очень удивлён, что при генерации модели не генерируется PHPDOC , в отличии от того же Yii2 с которого слезаю))

    • @ВячеславЧенгал
      @ВячеславЧенгал 5 років тому +1

      да, тоже с Yii2 слажу увидел такую неприятность, но фиксится очень отлично, проперти прописываются в модели и во вью если через иде хелпер пересканировать модели или одну вновь созданную модель ./artisan ide-helper:model

    • @DrZlad
      @DrZlad 5 років тому

      ​@@ВячеславЧенгал и как Вам Yii2? Чем хуже ларавеля?

    • @DrZlad
      @DrZlad 5 років тому

      и как Вам Yii2? Чем хуже ларавеля?

    • @ВячеславЧенгал
      @ВячеславЧенгал 5 років тому +1

      @@DrZlad на Yii2 около 6 лет проработал, в принципе оч легкий для понимания фреймворк, для мелких проектов и средних очень подходит. Из минусов это вкряченный в ядро бутстрап 3 и жыквери, были проблемы когда жыквери обновишь и сыпятся компоненты сторонние, от картика того же .... еще конечно шаг в лево шаг в право при использовании сторонних виджетов и пакетов вызывал гемор, хотелось плюнуть и написать свою реализацию ))) так иногда и делал. Yii2 мне нормально послужил так инструментарием , но сейчас проблема в том, что выходит Yii3, да и только по СНГ Yii2 популярен, поэтому пошел в лару, лара мне кажется шире по возможностям, полаконичнее все как то )

  • @director74
    @director74 5 років тому +2

    Почему-то у меня в BlogPostObserver сработало только при создании метода saving, а не updating

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому

      Погугли о значениях этих методов. И о нюансах их использования. Возможно что-то упустил. Например saving доджен возвращать тру - тогда сохранение пройдет, или фолс - тогда отмена сохранения.

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому

      Ошибся, в saving только ответ ===false будет отменой, все остальное одобрение.

  • @extreme_pw
    @extreme_pw 3 роки тому

    Дмитрий уроки просто пушка!) Хотел спросить, можно ли (вообще правильно ли) регистрировать Observer внутри модели а не в AppServiceProvider? Вроде как есть нарушение SOLID (дополнительная обязанность, но код регистрации маленький да и AppServiceProvider будет чище) Интересно услышать ваше мнение.

    • @DmitryAfanasyev
      @DmitryAfanasyev  3 роки тому +1

      Полагаю можно и так и эдак. Принципы солид сделаны для того чтобы приложение сделать более понятным и поддатливым к изменениям. Даже если и есть "нарушение" оно никак не вредит.

    • @extreme_pw
      @extreme_pw 3 роки тому

      @@DmitryAfanasyev Спасибо за ответ. Успокоили ))

  • @viadzmakzbiearusi2214
    @viadzmakzbiearusi2214 5 років тому

    Замечу про отличия updating от updated, это разные методы. У меня артизан автоматически updated проставил вместо updating, поэтому статья не обновлялась. Вручную прописать нужно на updating, чтобы можно было изменить данные до передачи в БД.
    Если я правильно понял, updated это уже работа метода после обновления БД.

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому

      А в видео что не так сказано?

  • @ВладимирАрестархов-х4з

    Дмитрий подскажите пожалуйста, обеспечивают ли Observers 100% срабатывание и
    выполнение кода при наступлении события?
    (Например необходимо вести таблицу-журнал действий пользователя, как вариант можно в методе Observer'a created создать запись в лога в бд, не произойдет ли случаев что лог не записался но основная сущность создалась?)

    • @DmitryAfanasyev
      @DmitryAfanasyev  4 роки тому

      Обсервер сработает всегда при условии работы с моделью записи. Не сработает если идти в обход (\DB::) или с помощью класса модели обновить группу записей.

    • @ВладимирАрестархов-х4з
      @ВладимирАрестархов-х4з 4 роки тому

      @@DmitryAfanasyev Спасибо! А насколько он "транзакционный"? Откатится ли результат сохранение модели если в ее обсервере произшла ошибка?

  • @vladzaiko5012
    @vladzaiko5012 5 років тому

    зачем в BlogPostObserver:setPublishedAt проверка на empty($blogPost->published_at) ? это поле не передается, оно всегда пустое

  • @АнтонРевякин
    @АнтонРевякин 4 роки тому +1

    isDirty() наверное без толку использовать - это событие вообще не вызывается, если модель не изменена
    Логирую в updating() текущий метод: если отправляю форму без изменений - не логирует, если изменяю в форме - появляется лог.
    Laravel Framework 6.13.1

  • @АнтонРевякин
    @АнтонРевякин 4 роки тому

    Как на счет вместо выборки и проверки на сущ-ние модели по id в контроллере перенести в Request'ы в метод
    protected function prepareForValidation()
    {
    $this->merge([
    'id' => $this->route('post')
    ]);
    }
    и уже в
    public function rules()
    {
    return [
    'id' => 'bail|exists:blog_posts,id',
    ...
    ];
    }
    Правда на 1 запрос больше получается

  • @dilavarzavkiev5419
    @dilavarzavkiev5419 3 роки тому

    Я чет не понял, методы обсервера должны иметь определенное название? Если нет, то тогда как модель понимает, какой метод у обсервера юзать при ивенте?

    • @DmitryAfanasyev
      @DmitryAfanasyev  3 роки тому +1

      Да, методы обсервера должны правильно именоваться. Иначе не сработают.

  • @Олександр-п3т3ж
    @Олександр-п3т3ж 3 роки тому

    Здраствуйте я хотел бы понять в чем смысл тогда в мутаторах, если есть observer?

    • @DmitryAfanasyev
      @DmitryAfanasyev  3 роки тому

      В мутаторах свойства изменяются сразу, а в обсервере непосредственно перед/после внесений изменений в бд. Очень разные механики. (Зачем педаль тормоза, если есть ручник - примерно тоже)

    • @Олександр-п3т3ж
      @Олександр-п3т3ж 3 роки тому

      @@DmitryAfanasyev Хорошо тогда, cпасибо за ответ - у меня были не правильные представление о мутаторах

  • @OlegM-nn4xx
    @OlegM-nn4xx 5 років тому

    Дмитрий, а как быть, если, например, я хочу вести логирование всех изменений в бд? Кто отредактировал, кто удалил, кто создал запись. Как я понимаю, обсервер прикручивается к определенной модели.
    В голову пришла такая мысль. Создать BaseModel, отнаследовать от нее все модели, на BaseModel повесить обсервер, и логировать все изменения. Будет ли это работать? и если я создам обсервер на конкретную модель, а не на baseModel, перезапишет ли логику обсервер на конкретную модель и не пропадет ли логика с mainObserver?
    Как вообще на практике такое реализуется на опыте?
    Видел подобную реализацию через snapshots и behavior в другом фреймворке, менее популярном. Интересно ваше мнение! Заранее спасибо!

    • @OlegM-nn4xx
      @OlegM-nn4xx 5 років тому

      Еще в документации видел такие методы как getDirty() дл получения всех измененных полей. Но как это грамотно устроить пока не складывается в голове. Подскажите пжл куда смотреть

  • @TheRedesc
    @TheRedesc 4 роки тому

    Slug на уникальность там не надо проверять?

    • @draackul
      @draackul 3 роки тому +1

      По идее этим класс Request занимается, нет? Мы же указывали там в правилах

  • @VASOTELVI
    @VASOTELVI 4 роки тому

    Спасибо! Может, всётаки не обсерверы, а ОБЗЁРВЕРЫ? :)))))

    • @DmitryAfanasyev
      @DmitryAfanasyev  4 роки тому +2

      Это смотря под каким углом смотреть 😁

  • @faizulla5838
    @faizulla5838 5 років тому +1

    Дмитрий, когда финиш?.... ларавел не обновиться снова пока мы дойдем?

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому +3

      Да прям на этом видео можно и финишировать :D

    • @faizulla5838
      @faizulla5838 5 років тому

      @@DmitryAfanasyev )))), я нить потерял.
      надо кое где заново вернуться

    • @faizulla5838
      @faizulla5838 5 років тому

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

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому +2

      теперь понятно. До этого почти добрались, но еще не близко.... Надо дать базу по джобам, коллекциям и тп.

    • @faizulla5838
      @faizulla5838 5 років тому

      @@DmitryAfanasyev Good!.... "законьчить привал !!!".... идем дальше

  • @stangevg
    @stangevg 3 роки тому

    Что-то я тормознул второй день. Если я удаляю слаг, то ругается, что я пытаюсь в уникальное поле записать нул... Column 'slug' cannot be null. Получается у меня не срабатывает
    if (empty($blogPost->slug)) {
    $blogPost->slug = Str::slug($blogPost->title);
    }
    Не подскажите, что это значит?

    • @DmitryAfanasyev
      @DmitryAfanasyev  3 роки тому

      Может и title пустой прилетает?

    • @stangevg
      @stangevg 3 роки тому

      @@DmitryAfanasyev проверял, в title все в норме, прилетает. Если slug изменяю, изменяется, а вот если делаю пустым, ругается.

    • @DmitryAfanasyev
      @DmitryAfanasyev  3 роки тому

      А в указанный выше блок кода вообще попадаем?

    • @stangevg
      @stangevg 3 роки тому

      @@DmitryAfanasyev да вроде, я ставлю dd($blogPost->slug, $blogPost->title); и получаю соответственно NULL и "Rerum aut numquam ex.", реальный заголовок.

    • @stangevg
      @stangevg 3 роки тому

      Я и внутри if проверил на всякий случай, и приходит slug "rerum-aut-numquam-ex" и title "Rerum aut numquam ex."

  • @nikolay-savin
    @nikolay-savin 5 років тому

    Я могу ошибаться, где-то пропустил может, или у меня что то индивидуально пошло не так. Но столкнулся с ошибкой, при которой контролер не видит обсервера напрочь. Судя по документации - обсервер нужно отдельно подключить в классе AppServiceProvider, а в видео этого нет. Подключил и все заработало. Ошибки - это 50% обучения. laravel.com/docs/5.8/eloquent#observers
    Поправка - в самом конце видео есть работа с AppServiceProvider. Хитрый Дмитрий в фоне подключил обсерверы и ничего про это при создании не сказал. Ну... ошибка и просмотр доков тоже хорошо.

    • @DmitryAfanasyev
      @DmitryAfanasyev  5 років тому +1

      В видео есть подключение обсервера. В точности как в документации.....

    • @НатанМамалига
      @НатанМамалига 5 років тому

      такая же проблема, спасибо помог)

  • @darknet106
    @darknet106 4 роки тому

    а в чем отличие от мутаторов?

  • @artemsergeev4833
    @artemsergeev4833 Рік тому

    Обсëр? Я что-то не расслышал. Или абсëр вэри?

  • @blackmulthumor
    @blackmulthumor 3 роки тому

    Спасибо