Уже который день ломаю голову над тем, как и где лучше проводить валидацию данных. В документации к различным фреймворкам, я вижу что-то вроде $form->isValid() и ни слова о том, что происходит далее. На ум приходит несколько способов, но прежде нужно описать задачу, пускай это будет регистрация пользователя. Есть интерфейс
UserController::public function postRegisterUser(Request $request);
UserModel::public function register($name, $email, $password);
Напишите какая реализация лучше и почему:
1.
public function postRegisterUser(Request $request, UserModel $user) {
$user->find($request->get('id'))->register(
$request->get('name'),
$request->get('email'),
$request->get('password')
);
}
public function register($name, $email, $password) {
$this->validator()
->add(new String($name, 'name'))
->add(new Email($email, 'email'))
->add(new Password($password, 'password'))
->validate();
// register new user
}
2.
public function postRegisterUser(RegisterUserRequest $request, UserModel $user) {
$user->find($request->get('id'))->register(
$request->get('name'),
$request->get('email'),
$request->get('password')
);
}
public function register($name, $email, $password) {
// register new user
}
3.
public function postRegisterUser(RegisterUserRequest $request, UserModel $user) {
$user->find($request->get('id'))->register($request);
}
public function register(RegisterUserRequest $request) {
// register new user
}
На данный момент пользуюсь первым способом, но у него есть пару недостатков. Во-первых, очень часто данные пересекаются - при регистрации и изменении данных пользователя будут использоваться одни и те же поля, и тут либо дублировать проверку, либо выносить в отдельный метод. Хуже всего, когда приходится писать что-то вроде
public function register($name, $email, $password) {
$this->validateBaseData($name, $email, $password)
->add(new String($foo, 'foo'))
->validate();
// register new user
}
Не сказал бы, что это критично, но, как по мне, ухудшается читаемость. Во-вторых, есть более приятный для меня стиль описания фильтров.
Второй способ подразумевает, что я создаю отдельный класс для каждого POST запроса.
class RegisterUserRequest extends Request {
/**
* @return \Core\Validation\Param\String
*/
public function name() {
return new String;
}
/**
* @return \Core\Validation\Param\Email
*/
public function email() {
return new Email;
}
/**
* @return \Core\Validation\Param\Password
*/
public function password() {
return new Password;
}
}
Такой вариант мне нравится больше всего в плане читаемости, к тому же присутствует наследование. Но есть серьезный минус - валидация происходит при иньекции в метод контроллера, и модель не уверена, что данные пришли верные. Впринципе, любое изменение данных идет через веб-морду и, так или иначе, данные проходят через контроллер, но чувство волнения не покидает.
Третий вариант как бы решает эту проблему, но, насколько я знаю, реквесты - это удел контроллеров, к тому же, я бы не хотел в каких-то методах модели обращаться к запросу, а во всех "гет" методах использовать обычные переменные. Также странно выглядели бы модульные тесты, где мне пришлось бы сначала создавать нужный запрос и набивать его данными, а уже потом обращаться к нужному методу.
Разумеется, приветствуются и другие варианты.