Laravel по-русски
Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
А давайте рассмотрим и обсудим как вариант хорошей практики репозиторий.
Вот например в данном пакете вся логика запросов лежит в нем https://github.com/bestmomo/laravel5-3- … positories
А его уже как зависимость вставлять в контроллер.
В случае ТС если ему скажем нужно заполнить меню которое на многих страницах одинаковые категории из БД, то лучше это сделать в ViewComposer. А в контроллере редактирования продукта выбирать только продукт и то что с ним связано и нужно для редактирования, скажем его атрибуты и картинки. И все это можно сделать одним вызовом метода репозитория типа $repo->getProductFullInfoForEdit(). Ну а ORM запрос к БД уже внутри класса репозитория.
PS. Я сам конечно только учусь
Proger_XP Есть ли мнение по теме и по заданным вопросам? Репо и сервисы нужно к месту используются? Как можно упростить? Стоит ли объединить добавление, апдейт юзера, активацию его и смену мейла в UserRepo или лучше разделить на несколько Repo как в исходном посте моем?
Чем например плох пример такой https://github.com/bestmomo/laravel5-ex … sitory.php с репо включающим две модели?
"ты пишешь с помощью кучи join вместо использования связей, тогда используй репозитории."
Может и от сервиса не стоит отказываться? Или он скорее не нужен все же когда используется в одной форме.. Но если с помощью него повторный код убивается то видимо и сервис нужен..
"Если ты пишешь с помощью кучи join вместо использования связей, тогда используй репозитории."
Да я так пишу. Более того реальный проект для которого будем эти тестовые разработки потом использовать уже написан с весьма сложными и многосоставными sql запросами..
В данном случае в модель EmailChange, ты же с данными этой модели работаешь.
Понимаю, но не считаю это красивым. Считаю что нужен класс (репо) в котором работа с юзеров и с ближайшим к нему связанным функционалом той же активацией и сменой мейла..
Делать все в eloquent моделях кажется неверно. А если у меня 2-3 равнозначные таблицы для работы с каким то функционалом. Тк в функционале как правило join'ы или иные тесные использования этих таблиц, то как то не очень удобно будет выбирать в какой из 2-3 моделек это писать. По мне лучше в таком случае написать один класс, из которого уже вызывать Eloquent методы, для всех трех таблиц. А местами и DB:raw для этих же таблиц...
Ты видимо говоришь, о чем простых и типичных случаях. Я же затачиваюсь и на сложности и не красивости.
Другие ответы подожду.. Но пока кроме тебя все молчат...
Email htchtc052@gmail.com кидай пример
Экземпляр объекта $item $user итд, на мой взгляд не должен иметь методов Eloqent через него не должны искаться другие юзеры итд... А это возможно. Или я неверно понял??
Зачем тебе на коллекции или объекте запускать еще какие-то методы?
Вот именно не зачем. Но если наша модель унаследована от Eloquent то я это могу сделать с экземпляром $item в примере нашем... Вызывать какие угодно Eloqeunt методы из него. А это кажется очень лишнее.
Как от такого избавиться?
"То их перемещаешь в модель."
В какую? В модель User или EmailChange ?
"Я же тебе говорю про:
$item = $this->item->getByIdWithUsers(2);"
А после данной строчки будет работать в этом случае $item->getByIdWithUsers(3); и $item->getTopItems();
Хотелось бы, что бы все таки нет. Понимаешь меня?
"Простой пример методов в модели:"
А можешь показать, примерно весь класс модели в таком случае для понимания?
Оставшиеся наиболее актуальные вопросы:
1) Как быть если я хочу использовать модель или репо, но не наследованные от Eloquent значит создаю, что то типа class App\MyModels\User в нем нужные методы типа User->getById($id){return $user} User->getTopUsers() {return $users} User->SetNewEmail($email). Далее бинд этого к контейнеру и инжект в контроллеры. Верно ли? Есть ли какая четкая алтернатива. Но что бы конкретный экземпляр $user не отдавал никаким уж методом нескольких юзеров, или другого юзера ![]()
2) Работа с табличками activation и change_email записать токен, обновить токен проверить токен, это в модели/репо юзера, или в отдельных моделях и репо как у меня сейчас лучше?
3) Сервисы убью, валидацию вынесу тут все ясно.
"Если ты в репозитории используешь запросы в Eloquent - это то же самое, что ты в классе модели используешь те же запросы"
Экземляр репо не может относится к конкретному объекту из таблицы, а экземпляр модели только этим и является. Сравни $item = Item::find(2); тут мы имеем item c id =2 и выполнять скажем $item->getSomeItems(); просто глупо. Получается вещь с ид 2, и обращаемся к ней почему, за несколькими другими вещями.. Что кажется совершенно не логично. В случае с repo не так. тк он не относится к конкретной вещи, а работает с наборами вещей, тк не наследует от Eloquent.
Может тупой пример, но понятный или нет?
" Смена емэйла в методе контроллера, при этом запросы в модели" в какой модели? UserModel или ChangeEmailModel ???
"Нужны ли контракты/фасады все же для каждого такого класса.
Фасады нужны для удобства, мне больше нравится использовать контейнер. Контракты - полностью зависит от твоей архитектуры."
Да я тоже за просто привязку классу к контейнеру и за его инжект в контроллере. Видимо для этого нужен просто app bind и все.
С этим ладно. Интереснее вопросы про Репо, про то что не хочу описывать бизнес логику там где наследуется от Eloquent и про то куда девать бизнес логику активации и смены мейла, в UserRepo/UserModel или куда еще?
"Они автоматически привязываются, в доках это описано. Привязывать в сервис провайдерах нужно класс к интерфейсу."
Покажи пример автоматической привязки и так что бы потом это можно было инжектить в контроллере. Разве для этого app bind не нужен? Пусть в AppService ?
"классе модели используешь те же запросы"
Но класс модели то наследует от use Illuminate\Database\Eloquent\Model; вот в чем дело... а репо нет..
То, что отсылку письма можно вынести в нотификацию я понимаю. Оставил это на потом. КРоме того в случае смены мейла, емейл нужно выслать на новый именно мейл, а не тот который в user->email потому я не очень с этим справился в нотификации и оставил просто Mail:: и думаю оно пока ладно.
Но вот вопросы выше особенно куда девать содержимое class ActivateEmailRepository и class ChangeEmailRepository ответь пожалуйста.
А сервисы
class ChangeEmailService implements ChangeEmailContract
{
protected $mailer;
protected $changeEmailRepo;
public function __construct(ChangeEmailRepositoryContract $changeEmailRepo)
{
$this->changeEmailRepo = $changeEmailRepo;
}
public function sendChangeEmailMail($user, $email)
{
$token = $this->changeEmailRepo->createEmailChange($user, $email);
\Mail::to($email)->send(
new \App\Mail\ChangeEmail(array(
'email' => $email,
'token' => $token,
))
);
}
public function setEmail($token, $email)
{
$changeEmail = $this->changeEmailRepo->getChangeEmailByTokenAndEmail($token, $email);
if ($changeEmail === null) {
throw new ChangeEmailNotFoundException();
}
$user = User::find($changeEmail->user_id);
if (!$user) {
throw new ChangeEmailNotFoundException();
}
$user->email = $email;
$user->save();
$this->changeEmailRepo->deleteChangeEmail($token);
return $user;
}
}class ActivateEmailService implements ActivateEmailContract
{
protected $mailer;
protected $activationEmailRepo;
public function __construct(ActivateEmailRepositoryContract $activationEmailRepo)
{
$this->activationEmailRepo = $activationEmailRepo;
}
public function sendActivationMail($user)
{
if (!$user || $user->verified) {
throw new ActivateUserNotFoundException();
}
$token = $this->activationEmailRepo->createActivation($user);
\Mail::to($user->email)->send(
new \App\Mail\ActivateEmail(array(
'email' => $user->email,
'token' => $token,
))
);
}
public function activateUser($token, $email)
{
$activation = $this->activationEmailRepo->getActivationByToken($token);
if ($activation === null) {
throw new ActivateUserNotFoundException();
}
$user = User::find($activation->user_id);
if (!$user) {
throw new ActivateUserNotFoundException();
}
$user->verified = true;
$user->save();
$this->activationEmailRepo->deleteActivation($token);
return $user;
}
}Что просто вынести в контроллеры??
И все же совсем конкретно:
namespace App\Repositories\Auth;
use \App\Contracts\Auth\ChangeEmailRepositoryContract;
use \App\Models\EmailChange;
use Carbon\Carbon;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
class ChangeEmailRepository implements ChangeEmailRepositoryContract
{
public function createEmailChange($user, $email)
{
$email_change = $this->getEmailChange($user);
if (!$email_change) {
return $this->createEmailChangeRecord($user, $email);
}
return $this->updateEmailChangeRecord($user, $email);
}
public function getEmailChange($user)
{
return EmailChange::where('user_id', $user->id)->first();
}
public function getChangeEmailByTokenAndEmail($token, $email)
{
return EmailChange::where(array('token' => $token, 'email' => $email))->first();
}
public function deleteChangeEmail($token)
{
EmailChange::where('token', $token)->delete();
}
private function updateEmailChangeRecord($user, $email)
{
$token = $this->getToken();
EmailChange::where('user_id', $user->id)->update([
'token' => $token,
'email' => $email,
'created_at' => new Carbon()
]);
return $token;
}
private function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
}
private function createEmailChangeRecord($user, $email)
{
$token = $this->getToken();
EmailChange::insert([
'user_id' => $user->id,
'token' => $token,
'email' => $email,
'created_at' => new Carbon()
]);
return $token;
}
}Вот это все где хранить? В модели/репо юзера?? И дергать из контроллера?
Аналогично по активации
<?php
namespace App\Repositories\Auth;
use \App\Contracts\Auth\ActivateEmailRepositoryContract;
use \App\Models\Activation;
use Carbon\Carbon;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
class ActivateEmailRepository implements \App\Contracts\Auth\ActivateEmailRepositoryContract
{
public function createActivation($user)
{
$activation = $this->getActivation($user);
if (!$activation) {
return $this->createToken($user);
}
return $this->regenerateToken($user);
}
private function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
}
private function regenerateToken($user)
{
$token = $this->getToken();
Activation::where('user_id', $user->id)->update([
'token' => $token,
'created_at' => new Carbon()
]);
return $token;
}
private function createToken($user)
{
$token = $this->getToken();
Activation::insert([
'user_id' => $user->id,
'token' => $token,
'created_at' => new Carbon()
]);
return $token;
}
public function getActivation($user)
{
return Activation::where('user_id', $user->id)->first();
}
public function getActivationByToken($token)
{
return Activation::where('token', $token)->first();
}
public function deleteActivation($token)
{
Activation::where('token', $token)->delete();
}
}Теперь другой вопрос. Вот эти модели или уж репо. Для Юзера, для Юзерский файлов в общем для крупных кусков бизнес логике. Как правильней привязать к контейнеру?
В AppService или каждый в своем провайдере? Нужны ли контракты/фасады все же для каждого такого класса.
И еще ты не ответил стоит ли методы активации и смены мейла пихать в модель(репо) юзера, или это отдельные модели(репо) должны быть…
Вот это ответь и отстану… И возможно через пару дней покажу переписанный код…
«С файлами работать можно в контроллере, если это 1-2 строки. Если что-то серьезное, тогда двигаешь логику в отдельный класс.»
Уверен, что сложнее. Про отдельные готовые пакеты пока не уверен. Для файлов скорее нет. Для подписок и оплаты возможно.
Опять же отдельный класс… Но надеюсь не наследованный от Eloquent…
- $this->user->getTopUsers() // После инжекта модели.
Инжект модели в контроллер в данном случае как я понимаю. И конечно это должно быть НЕ тоже самое что $user = Auth::user(); те это текущий юзер и его элокьент модель. Ну если сделать так:
$users = $user->getTopUsers();
$other_user = $user::find($other_user_id);
То кажется это ужас жуткий. И лучше уж репо. Но если есть другое решение предлагай… Но я не хочу наследовать свой модель от Eloquent