Посоветуйте как организовать структуру
Посоветуйте как организовать структуру
Смотрел видео с конференций по Yii. Выступающие говорили, что модель основанная на ActiveRecord это не совсем та модель, которая относится к MVC. Сказал как то не очень понятно, но надеюсь смысл понятен. Также предлагалось всю логику выводить в отдельный класс, который и будет модель в MVC. Мне это показалось правильным и удобным. Я теперь хочу опробовать это написан модуль регистрации/авторизации. Начнем с регистрации...
Вот структура модуля:
user
--- Module.php
--- controllers
--- --- SignupController.php
--- common
--- --- SignUp.php // та самая модель MVC
--- models
--- --- User.php // модель Yii \yii\db\ActiveRecord
--- --- SignUpForm.php // модель Yii \yii\base\Model
--- views
--- --- signUp.php // форма регистрации
Т.е. мы имеем модель SignUpForm для вывода формы. А модель User служит для добавления/регистрации нового пользователя. А вот классе в commom\SignUp как раз и будет вся логика регистрации. Из этого класса будет вызываться User::save(). Вопрос: где валидировать нового пользователя перед добавлением его в базу (в каком из этих двух классов)? И может наличие SignUpForm излишне и проще использовать сразу User?
Подскажите пожалуйста.
Вот структура модуля:
user
--- Module.php
--- controllers
--- --- SignupController.php
--- common
--- --- SignUp.php // та самая модель MVC
--- models
--- --- User.php // модель Yii \yii\db\ActiveRecord
--- --- SignUpForm.php // модель Yii \yii\base\Model
--- views
--- --- signUp.php // форма регистрации
Т.е. мы имеем модель SignUpForm для вывода формы. А модель User служит для добавления/регистрации нового пользователя. А вот классе в commom\SignUp как раз и будет вся логика регистрации. Из этого класса будет вызываться User::save(). Вопрос: где валидировать нового пользователя перед добавлением его в базу (в каком из этих двух классов)? И может наличие SignUpForm излишне и проще использовать сразу User?
Подскажите пожалуйста.
Re: Посоветуйте как организовать структуру
По простому без мапперов и репозиториев:
1. Контроллер передает данные в форму (SignUpForm)
2. Форма валидирует данные
3. Если данные валидны контроллер: или передает объект формы в сервис (SignUp) или из формы в сервис
4. Сервис заполняет AR (User) данными из формы.
1. Контроллер передает данные в форму (SignUpForm)
2. Форма валидирует данные
3. Если данные валидны контроллер: или передает объект формы в сервис (SignUp) или из формы в сервис
4. Сервис заполняет AR (User) данными из формы.
Re: Посоветуйте как организовать структуру
модель MVC - это не конкретная модель/класс, а просто слой приложения, работающий с данными. В том числе это и модель AR и модель формы.
Исхордя из этого, не понятно что вы сделали тут - SignUp.php // та самая модель MVC
Исхордя из этого, не понятно что вы сделали тут - SignUp.php // та самая модель MVC
Re: Посоветуйте как организовать структуру
Хорошо. Наверное перемудрил. Логика будет в файле SignUpForm.php.
Это метод контроллера:
Это модель:
Вот на сколько это правильно? Я хотел разгрузить ActiveRecord User. Как при таком подходе правильно организовать валидацию? Ведь что бы выполнилось $user->save() нужны правила валидации в User.php. Также нужны правила валидации в SignUpForm.php, что бы ActiveForm сообщал об ошибках ввода.
Это метод контроллера:
Код: Выделить всё
public function actionIndex() {
$model = new SignUpForm();
if(\Yii::$app->request->post('SignUpForm')) {
$model->attributes = \Yii::$app->request->post('SignUpForm');
$model->signUp();
}
return $this->render('signUp', [
'model' => $model
]);
}
Код: Выделить всё
class SignUpForm extends \yii\base\Model {
public $username;
public $password;
public function rules() {
return [
---------------
];
}
public function signUp() {
if($this->validate()) {
$user = new User();
$user->attributes = $this->attributes;
$user->save();
} else {
echo 'n';
}
}
}
Код: Выделить всё
/
class User extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%user}}';
}
public function rules()
{
return [];
}
}
Re: Посоветуйте как организовать структуру
Код: Выделить всё
public function actionIndex()
{
$model = new SignUpForm();
if ($model->load()) {
try {
$service = Yii::createObject(SignUpService::class);
$service->signUpFromSignUpForm($model);
} catch (/* тут перехват logic, domain и runtime исключений*/) {
/* обработка исключения */
}
}
return $this->render('signUp', [
'model' => $model
]);
}
class SignUpService
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function signUpFromSignUpForm(SignUpForm $model)
{
if (!$model->validate()) {
throw new RuntimeException('Model is invalid');
}
return $this->create($model->username, $model->password);
}
public function create($username, $password)
{
$user = clone $this->user;
$user->username = $username;
$user->password = $password;
if (!$user->save(false)) {
throw new RuntimeException('Can not save model');
}
return $user;
}
}
Последний раз редактировалось SiZE 2017.09.06, 15:44, всего редактировалось 1 раз.
Re: Посоветуйте как организовать структуру
А метод SignUpService::create() для чего? Где он должен вызываться? И для чего объект клонируется?
И все таки я не пойму... В каком классе должны быть правила валидации, что бы не было дублирования? Или без этого не обойтись?
И все таки я не пойму... В каком классе должны быть правила валидации, что бы не было дублирования? Или без этого не обойтись?
Re: Посоветуйте как организовать структуру
Сорян, подправил пример.
Это для примера, можешь без DI обойтись и клонирования. Но таким образом у тебя всегда будет первоначальный объект внедренный через DI, если ты повторно вызовешь create ты будешь уверен в состоянии этого объекта.
Я лично не вижу смысла отказываться от штатной валидации. Но без проверки в сервисе тоже не обойтись. В контроллере перехватываем исключения из сервиса и в зависимости от исключения: преобразуем его например в HttpException или же $model->addError('attribute', 'error'); или пишем лог и игнорируем. Думать надо вобщем.
Re: Посоветуйте как организовать структуру
Код: Выделить всё
try {
$service = Yii::createObject(SignUpService::class);
$service->signUpFromSignUpForm($model);
} catch (RuntimeException $e) {
// Пример где-то тут был от Elisdn, типа такого
throw new HttpException(403, Yii::t('signupservice', $e->getMessage())); // переводим сообщения в человекопонятные :)
}
Re: Посоветуйте как организовать структуру
Код: Выделить всё
class SignupController extends Controller
{
private $service;
public function __construct($id, $module, SignupService $service, $config = [])
{
parent::__construct($id, $module, $config);
$this->service = $service;
}
public function actionRequest()
{
$form = new SignupForm();
if ($form->load(Yii::$app->request->post()) && $form->validate()) {
try {
$this->service->signup($form);
Yii::$app->session->setFlash('success', 'Check your email for further instructions.');
return $this->goHome();
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', Yii::t('exception', $e->getMessage()));
}
}
return $this->render('request', [
'model' => $form,
]);
}
public function actionConfirm($token)
{
try {
$this->service->confirm($token);
Yii::$app->session->setFlash('success', 'Your email is confirmed.');
return $this->redirect(['auth/login']);
} catch (\DomainException $e) {
Yii::$app->errorHandler->logException($e);
Yii::$app->session->setFlash('error', Yii::t('exception', $e->getMessage()));
}
return $this->goHome();
}
}
Re: Посоветуйте как организовать структуру
А вообще, что бы вывести форму какую модель Yii лучше использовать \yii\db\ActiveRecord или \yii\base\Model? Если можно, то аргументировано
Re: Посоветуйте как организовать структуру
ActiveRecord - это модель, сохраняющаяся в базу, Model - это просто модель, т.е. класс хранящий данные. Форма - это данные. Ответ: Model.
Re: Посоветуйте как организовать структуру
А как передать параметры в конструктор контроллера? А зачем это нужно?ElisDN писал(а): ↑2017.09.06, 16:26Код: Выделить всё
class SignupController extends Controller { private $service; public function __construct($id, $module, SignupService $service, $config = []) { parent::__construct($id, $module, $config); $this->service = $service; } }
Моя IDE сама создала такой конструктор:
Код: Выделить всё
public function __construct($id, Module $module, array $config = []) {
parent::__construct($id, $module, $config);
}
Class app\modules\user\controllers\Module does not exist
Re: Посоветуйте как организовать структуру
Их сам фреймворк передаёт. Затем, чтобы не вызывать вручную $service = new SignupService() в каждом экшене.
Либо уберите слово Module, либо импортируйте класс как use yii\base\Module.
Re: Посоветуйте как организовать структуру
А конкретные описания ошибок "catch (\DomainException $e) " в сервисе находятся?
Re: Посоветуйте как организовать структуру
Код: Выделить всё
class User extends ActiveRecord
{
...
public function confirmSignup(): void
{
if (!$this->isWait()) {
throw new \DomainException('User is already active.');
}
$this->status = self::STATUS_ACTIVE;
$this->email_confirm_token = null;
$this->recordEvent(new UserSignUpConfirmed($this));
}
}