SOLID
SOLID — принципы программирования, следуя которым можно добиться легко-масштабируемого и легко-поддерживаемого кода.
Controller Service Repository
Controller Service Repository — архитектурный паттерн, необходимый для разделения ответственности и помогающий соблюдать принципы SOLID в фреймворке Laravel
Контроллеры
Контроллеры — классы, отвечающие за обработку запросов. Таким образом ответственность контроллера — это формирование ответа на пользовательские запросы.
Метод контроллера должен…
В современном искусстве программирования техника SOLID получила широкое распространение благодаря старому доброму принципу «разделяй и властвуй».
В этой статье я хочу осветить некоторые солидные моменты, которые неочевидны новичкам.
Для начала я покажу общую архитектуру приложения, так как без нее будет непонятно использование классов и наследований.
Так как я буду использовать ресур-контроллер, то маршруты описываются примитивным массивом вида
Работая с API иногда имеет смысл делать лог запросов, которые приходят на сервер, и ответов, которые сервер отправляет. Я уже написал статью о том, как делать лог, когда работаешь с API клиентом. Ее можно посмотреть здесь. Сейчас давайте рассмотрим как делать лог, когда программируешь API сервис. Например, если система третьей стороны присылает нам уведомления, что заказ отправлен, неплохо было бы сделать лог запросов которые эта система присылает и ответов, которые высылает наша система. Такой лог поможет разрешить спорные ситуации или найти ошибки в работе системы. Итак, начнем.
В routes/api.php файле Laravel создадим следующий маршрут:
Route::middleware('request.logging')->post('/hello', function() {
return…
Введение
Очереди (queues) это одна из сильных сторон фреймворка. Хотя они реализованы так, чтобы любой ремесленник смог их осилить, всё же некоторый порог для входа есть и моя цель помочь вам его преодолеть.
Закончив читать эту статью вы научитесь:
Использовать очереди для того чтобы быстро выполнять http-запросы.
Запускать обработку очереди с минимальными (нулевыми) дополнительными требованиями к серверу.
Обеспечить постоянную работу очереди.
Здесь будет рассмотрен только самый минималистический вариант организации очередей: через драйвер database. Будем считать, что это local или staging окружение, на котором не ожидается больших нагрузок. Хотя, если честно, большинство "рабочих" окружений также нельзя назвать highload :)…
В документации по Laravel тут и его английском варианте весьма неявно указано, как производить загрузку внешних файлов.
Цитирую оригинал
The put method may be used to store raw file contents on a disk. You may also pass a PHP resource to the PHPput
method, which will use Flysystem's underlying stream support. Using streams is greatly recommended when dealing with large files:…
В прошлой статье мы разобрались как в Laravel работать с UUID. Но он не решает всех проблем распределенных систем. Один из новых подходов к генерации уникальных идентификаторов это ULID — Universally Unique Lexicographically Sortable Identifier (универсальный уникальный лексографически сортируемый идентификатор).
Сравнение UUID и ULID
Во многих случаях использование UUID неоптимально:
- Это не самый эффективный способ кодирования 128-битной случайности
- UUID v1/v2 непрактичен во многих средах, так как требует доступа к уникальному стабильному MAC-адресу
- UUID v3/v5 требует уникального начального числа и генерирует случайно распределенные идентификаторы, которые могут вызвать фрагментацию во многих структурах данных
- UUID v4 не предоставляет никакой другой информации…
Одна из самых распространенных проблем с производительностью, которую я видел в Laravel - это использование методов Eloquent и отношений из шаблонов Blade, создание ненужных дополнительных циклов и запросов. В этой статье я покажу различные сценарии и способы их эффективного использования.
Сценарий 1. Загрузка отношения belongsTo(): не забудьте про «жадную загрузку»
Типичный случай — вы перебираете записи через @foreach, и, в каком-то столбце, вам нужно показать родительскую запись с определенным полем.
@foreach ($sessions as $session)
<tr>
<td>{{ $session->created_at }}</td>
<td>{{ $session->user->name }}</td>
</tr>
@endforeach
И, конечно, Session принадлежит User, в app/Session.php :
public function user()
{
return $this->belongsTo(User::class)…
Веб-программирования для меня - это хобби. Поэтому я могу переключиться на что-то другое и не заниматься web-разработкой в течении длительного времени. Когда я возвращаюсь к этому, то некоторые моменты приходится вспоминать заново. Эта статья - это инструкция для меня, а также, надеюсь помощь кому-то из вас.
У меня несколько сайтов и в очередной раз я решил обновить их. В качестве основы выбрал Lumen. Это PHP-микрофреймворк написанный на основе компонентов Laravel его автором. Он достаточно простой и шустрый, как раз то, что мне нужно.
Установка Open Server
Локальную версию сайта я запускаю на Open Server, использую версию 5.2.9…
Для нашего любимого фреймворка существует с десяток различных админок и ещё больше генераторов, выбор может показаться настолько запутанным, что некоторые пишут исключительно свои решения. Я не работал со всеми и не могу утверждать, что какие то лучше/хуже, но могу рассказать об ORCHID и как с ней работать ~ за 10 минут.
Всегда начинайте с данных
Буду надеяться, что вы уже установили фреймворк и платформу, создали базу данных и даже запустили веб-сервер.
Очевидным продолжением будет написание моделей и миграций для вашей базы данных.
Создадим новую модель "Проекты":
php artisan make:model Project -m
Используя флаг -m
, будет создан…
Всем привет, кто читает мою статью.
Продолжаем выполнение тестового задания. Выполним пункт 3-5.
3) Добавить возможность удалять статьи в мягком режиме
. При этом сделать возможность обновить систему со старой версии (из первого задания) на новую без полного сброса БД.
Для возможности удаление статей в мягком режиме из таблицы «articles» создадим миграцию :
- php artisan make:migration ChangeArticleSoftTable --table=articles
В созданной миграции в методе up() опишем softDeletes.
…
CSR — одно из самых неудачных архитектурных решений со времен Б4.
Предлагается для каждой модели написать свой репозиторий и сервис к нему. Но, простите, а что делать, если, например, одной модели оказывается недостаточно? К примеру пользователю понадобился адрес, его куда прикажете запихивать? В UserRepository? Или уже в AddressRepository?
Если в последний, то как передать пользователя? Через id? Тогда будет N+1.
Через ids? Тогда что делать, если сам UserRepository используется в каком-нибудь CustomerService который, не к ночи будет сказано, подтягивает еще какой-нибудь OrderRepositiory?
Т.е. дальше одного шага проблема не решается принципиально, придется городить какие-то обходы выборок, сбор ids, передача всего этого в репы... и всё ради чего???
try {
$user = $service->getUser($request->getUserId());
}
Изюмительно. Я прямо сижу.. и старческие слезы умиления наворачиваются на глазах. Простите, а что Вы собираетесь делать, когда в запрос нужно будет передать новый параметр, кроме id? Ну, например, СНИЛС или что-то еще?
Давайте я Вам помогу. Небольшой реверс-энжинеринг.
Так как getUserId — это какой то малопонятный огород, вангую, что это обычная обертка над Request за каким-то бесом засунутая в виде метода в ShowUserRequest
protected int $userId;
public function __construct()
{
$this->userId = (int) $this->id;
}
public function getUserId(): int
{
return $this->userId
}
Зачем? Да просто, ради писанины. Ведь теперь, чтобы добавить новый параметр и передать его в сервис нужно еще методов в ShowUserRequest закинуть.
Почему нельзя просто сделать (int) $request->get('id', 0) ? Вопрос чисто риторический.
Ну оке.. с ид понятно, добавление всего одного поля в такую систему приводит к каскаду правок в
1. реквесте
2. контроллере
3. сервисе(он же получает не реквест, а параметры отдельно)
4. репозитории(его же нужно как то обработать)
Несколько многовато для такого простого действия.
catch (UserNotFoundException $exception) {
return view('user.not_found', ['user_id' => $request->getUserId()]);
}
тут еще интереснее. Вместо совершенно логичного и общего для любых моделей ModelNotFound требуется на каждый реп описать свой эксепшен. За ради что?
Т.е. в репе User::find($userId) заменить на User::findOrFail($userId) — уже не православно, а наогородить
if ($user === null) {
throw new UserNotFoundException("User {$userId} not found.');
}
— это феншуй. Ню... токооое.
«Негативные последствия такой реализации в том, что при необходимости получения разных ответов в разных методах контроллеров, одно и то же исключение может быть преобразовано по разному.»
Это в каком мире 404 страница должна быть преобразована по разному? Нет модели — пульнул одну для всех 404 и забыл. Я так понимаю, что разные ответы нужны чтобы программисты не скучали.
«В теле метода сервиса не выполняются запросы к базе данных. Только бизнесовая логика»
и это приводит к тому, что запросы выполняются катастрофически долго, а логика превращается в спагетти-стайл с вызовом сервисов внутри других сервисов обернутое циклами внутри других циклов со вложенностью 5 и более и нытьём что ой у нас все как то сложно получилось давайте перейдем на DDD там всё еще сложнее.
«Репозитории — классы, отвечающие за сохранение и извлечение некоторого набора данных»
Нет конечно. Ничего подобного репозитории НЕ ДЕЛАЮТ. Этим занимается DAO! Запомни, а лучше запиши и никогда в дальнейшем не путай!!!
Репозиторий — это высокоуровневая абстракция доступа к данным. Никаких изменений он делать не может и не умеет.
«В идеале репозиторий не должен возвращать или принимать объекты класса Model»
В сферическом вакууме, надо понимать. Там, где сферические кони.
Паттерн был придуман задолго до того, как появилась эта ваша богомерзкая DDD и предназначался, в первую очередь, для быстрого доступа к уже полученным данным. Возможно ты в курсе, что есть языки, которые не выгружаются из памяти всякий раз на каждом запросе, так вот в них(например в яве) репы очень комфортно себя чувствуют, так как являются эдаким кэшем справочника в памяти, ну и, разумеется, хранят они самые что ни на есть Models, а не эти ваши «энтити», простихоспиди.
«Лучше создать собственные entity или dto.»
Ага.. давай еще один огород сверху прикостылим. А то там абстракций чет маловато, ОРМ уже не хватает. Теперь для добавления одного поля мало того, что нужно будет сделать миграцию и добавить логику в сервис, так еще написать пару-десятков методов гетеров и сетеров в entity, dto, valueObject commandBus и всё заверте... но это не точно.
«Но с опытом вы поймете...»
что простое лучше чем сложное, что код, который никогда не ломается — это код, которого нет. Его проще сопровождать, проще добавлять новые фичи, проще разбираться с багами. YAGNI + KISS.