Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
Начал изучать для себя вопрос хороших практик на Laravel, но все же сейчас остались некоторые вопросы на какие не могу найти ответы. Мог бы помочь, пожалуйста, разобратся с этим? Знакомство началаось через эти две теми: Тема#1 , Тема#2 . А потом и через статьи о SRP, именовании, ну и конечно же сам репозиторий хороших практик. Очень сильно помогло разобраться и еще лучше разложить для себе все по полочкам
Как я понял, твоя позиция такова, что нет смысла использовать репозитории, как обертку над елокмент(в репозитории есть смысл только если используешь сырые запросы или квери билдер). Сначала не понимал(так как «все» говорят что репозиторий нужен), но после прочтения этой статьи понял твое видение и почему это «бесполезно» и лишний слой абстракции.
Сам я сейчас для себя решил также работать без репозиториев и выносить запросы в модель Елокмент а саму модель подключаю через IoC — контейнеры.
1) Но вот также, какие именно запросы стоит туда выносить? Как я понимаю, все которые повторяються и те, которые посложнее? А если такие как PHP$this->post->where('date', '>', 'now')->orderBy('title')
? Здесь уже как, считается что присутствует логика и их нужно выносить? Или нет?
2) Может ли репозиторий(в моем случае модель Елокмент) вызывать сервисы? Или он должен быть только коллекцией запросов? Например, нужно обработать какие-то данные передаваемые с реквеста что-бы использовать их в выборке. Каков тогда правильный алгоритм последовательности действий? С контролера вызываем метод репозитория для выборки данных и в него же передаем реквест-данные, где внутри метода репозитория вызываем сервис, их обрабатываем и уже передаем в саму выборку? Например:
//Controller:
$posts = $this->post->getPostsWithFilters($request);
//Model/Repository:
public function getPostsWithFilters($request)
{
$data = $this->postService->getPreparedData($request);
return $this->all()->where($data) ...//возвращаем запрос с обработанными данными
}
Или вызываем в контролере сервис, который сделает уже обработку данных и + вызовет метод репозитория и вернет результат выборки в контролер, и тогда мы получается с репозиторием напрямую через контролер и не общались. Например:
//Controller:
$posts = $this->postService->getPostsWithFilters($request);
//postService:
public function getPostsWithFilters($request)
{
$request = modify($request); //обрабатываем как-то данные
return $this->post->getPostsWithFilters($request);
}
//Model/Repository:
class Post extends Model
{
public function getPostsWithFilters($request)
{
return $this->all()->where($data) ...//возвращаем запрос с обработанными данными
}
}
Или же вызываем в контролере сервис, который только лишь обрабатывает данные и + вызываем в контролере метод репозитория куда и передаем эти подготовленные данные. Например:
//Controller:
$data = $this->postService->getPreperedData($request);
$posts = $this->post->getPostsWithFilters($data);
//или так
$posts = $this->post->getPostsWithFilters($this->postService->getPreperedData($request))
А сервис и модель соответственно будуть вылядеть так:
//Model/Repository:
class Post extends Model
{
public function getPostsWithFilters($request)
{
return $this->all()->where($request) ...//возвращаем запрос с обработанными данными
}
}
//postService:
public function getPreparedData($request)
{
$prepared_data = modify($request); //обрабатываем как-то данные
return $prepared_data;
}
Как правильно по архитекуте и по лучших практиках делать?
3) Также если нужно совсем немного модифицировать некоторые данные(или же какой-то совсем один параметр) в реквесте, можно ли это делать напрямую в конотролере? Без сервисов? Например:
//Controller
public function store($request)
{
$data = $request->all();
$data['cost'] = $this->tovar->find($data['tovar_id'])->first()->cost;
$this->booking->create($data);
}
И можно ли такое использовать в мутаторах?
Например, чтобы совсем не писать эту часть кода ни в сервисе ни в контролере:
//Model Booking
class Booking extends Model
{
public function setCostAttribute($value)
{
$this->attributes['cost'] = Tovar::find($this->tovar_id)->first()->cost;
}
}
Можно ли делать так? То есть могут ли две модели елокмент общаться бежду собой? И как правильно тогда их внедрять? Если я использую по всму приложению контейнеры то не будет ли глупо там использовать фасад?(так как вроде внедрить через конструктор как контейнер не получиться). И насколько вообще хороша практика использования мутаторов и аксесоров?
Есть еще несколько вопросов по SPR и RESTFull Api. Был бы очень сильно благодарен за твои ответы и помощь
Изменено PeterKravets (29.11.2017 22:14:25)
Не в сети
- 1) Но вот также, какие именно запросы стоит туда выносить? Как я понимаю, все которые повторяються и те, которые посложнее? А если такие как $this->post->where(’date’, ’>’, ’now’)->orderBy(’title’) ? Здесь уже как, считается что присутствует логика и их нужно выносить? Или нет?
- Может ли репозиторий(в моем случае модель Елокмент) вызывать сервисы? Например, нужно обработать какие-то данные передаваемые с реквеста что-бы использовать их в выборке. Каков тогда правильный алгоритм последовательности действий?
Зависит от ситуации, но если логика сложная, то лучше вызвать сервис или трансформер из контроллера.
$filters = $this->postService->getPreparedData($request->all());
$posts = $this->post->getPostsWithFilters($filters);
Если запрос простой, то вполне можно и в модели работать с данными. Но сервис в модели я бы использовать не стал.
- 3) Также если нужно совсем немного модифицировать некоторые данные(или же какой-то совсем один параметр) в реквесте, можно ли это делать напрямую в конотролере? Без сервисов?
Лучше в сервис. А вообще, в таких ситуациях используются связи. В своем примере ты дублируешь стоимость товара, когда можно привязать товары к заказу (многие ко мнгоим).
- Можно ли делать так? То есть могут ли две модели елокмент общаться бежду собой?
Общаться они могут, но делать этого не нужно. Почти всегда можно использовать связи. Либо работать с обеими моделями через контроллер/сервис.
- И насколько вообще хороша практика использования мутаторов и аксесоров?
Эти методы нужно использовать по назначению. Для того, чтобы создать запрос в БД, их точно не нужно использовать.
Не в сети
Спасибо за ответы.
Зависит от ситуации, но если логика сложная, то лучше вызвать сервис или трансформер из контроллера
Т.е. правильно ли я понял, если логика простая то мы можем отфильтровать эти данные прямо в моделе, не вызывая при этом сервиса для этого?
Позволь задать, пожалуйста, еще пару вопросов.
1) Что также лучше передавать с контролера в сервис, весь обьект $request или только его параметры $request->all() ? И почему именно так?
2) Как лучше разбивать сервисы? У меня пока получается идет разбивка сервисов по моделях. Для тех моделей где он нужен - создается сервис. И у них у всех есть один общий, родительский где вынесены методы общие, которые могут быть полезны для всех. Правильный ли это подход для не слишком большого проекта? Или нужно разбивать всегда по логике(даже если в сервисе будет 1-2 метода) в независимости большой проект или нет и по моделях не разбивать?
Не в сети
1) Что также лучше передавать с контролера в сервис, весь обьект $request или только его параметры $request->all() ? И почему именно так?
Лучше all(), потому что это массив нужных модели данных. Зачем модели весь Request объект?
Как лучше разбивать сервисы?
По функциональности (по "фичам"). Например, у тебя два контроллера работают с календарем. Ты создаешь сервис для работы с календарем и работаешь с ним в контроллерах.
даже если в сервисе будет 1-2 метода
Это не проблема.
Не в сети
[QUOTE]даже если в сервисе будет 1-2 методаЭто не проблема[/QUOTE]
Будет ли хорошо в таком случае в контролер внедрять множество сервисов? Например, мне нужен будет и fileService с 1-2 методами, и calendarService с 1 методом и, например, postService. При этом также в контролер как контейнеры инжектятся и мои модели.
public function __construct(Specialization $specialization, Language $language, Location $location, LawyerService $lawyerService, FileService $fileService, CalendarService $calendarService)
{
$this->specialization = $specialization;
$this->language = $language;
$this->location = $location;
$this->lawyerService = $lawyerService;
$this->fileService = $fileService;
$this->calendarService = $calendarService;
}
2) И стоит ли использовать сервисы как обертку над методами create(), update(), где обрабатываются как-то данные и в сервисе вызывается же $this->post->create/update. Или create/update должен вызывать контролер, а сервис возвращает только некие подготовленные данные?
т.е.
//Controller:
public function store(Request $request)
{
$this->lawyerService->create($request);
}
//LawyerService:
public function create(Request $request)
{
//здесь как-то обрабатываются данные $request
$this->post->create($request); //и в сервисе идет вызов create() с модели
}
Или сервисы как-то только формируют данные, возвращают их в контролер и контролер вызывает на моделе create/update
3) Также сам вызов для рассылки email-ов через —>notify() происходит в контролере?
4) Ты упоминаешь о трансформерах. Что это такое? Ранее не сталкивася с этим определением.
Не в сети
Будет ли хорошо в таком случае в контролер внедрять множество сервисов?
Нормально, но это звоночек, что контроллер делает слишком много.
И стоит ли использовать сервисы как обертку над методами create(), update(), где обрабатываются как-то данные и в сервисе вызывается же $this->post->create/update
Я бы не стал делать как в примере, но если логика сложнее, то стоит.
Также сам вызов для рассылки email-ов через —>notify() происходит в контролере?
Если это одна читаемая строка, то можно. Если ты передаешь данные, например, тогда лучше вынести в сервис.
Отталкивайся от читаемости кода и возможности переиспользовать его. Я бы вынес в любом случае.
Ты упоминаешь о трансформерах. Что это такое? Ранее не сталкивася с этим определением.
Так часто называют классы, которые переводят данные из одного формата в другой.
Не в сети
Так часто называют классы, которые переводят данные из одного формата в другой.
А можешь привести тут или написать в своем репозитории пример преобразователя конкретный и именно при создании модели и/или при создании дочерней модели через отношения?
Не в сети
А можешь привести тут или написать в своем репозитории пример преобразователя конкретный и именно при создании модели и/или при создании дочерней модели через отношения?
Я ими не пользуюсь, т.к. гораздо удобнее элементы формы (например) грамотно назвать, чтобы не нужно было преоразование данных делать.
Но если хочется понять что такое трансформер, можно посмотреть на примеры пакета Fractal
Не в сети
Я ими не пользуюсь, т.к. гораздо удобнее элементы формы (например) грамотно назвать, чтобы не нужно было преоразование данных делать.
Тогда понятно.. Нет меня интересует не столько названия полей итд. Сколько создание объекта с еще какими то дополнительными действиями перед ним. Скажем создали объект через API на удаленном сервисе/платежном шлюзе, а затем в свою БД сохранили. И как бы все это что бы $user->some_child_objects()->createMethod($request->all()); ??
Это же типичная такая задача... Нужно что бы внутри createMethod был доступен $user родительский и данные с которыми создаем ну например из формы пришедшие... Или тут только сервис?
Изменено htclog81 (01.12.2017 12:12:13)
Не в сети
- Это же типичная такая задача… Нужно что бы внутри createMethod был доступен $user родительский и данные с которыми создаем ну например из формы пришедшие… Или тут только сервис?
В соседней теме ответил, что лучше минимизировать такие преобразования. При грамотной архитектуре их минимум. Для остального можно делать что-то вроде:
->create(array_merge($request->all(), ['something' => $this->class->getSomething()]))
Не в сети