-
-
Save fesor/33f041e3f362beff8d0ef977afd1ff66 to your computer and use it in GitHub Desktop.
<?php | |
class RegisterUserHandler | |
{ | |
private $encoder; | |
private $users; | |
private $notificator; | |
public function __construct(PasswordEncoder $encoder, UserRepository $users, Notificator $notificator) | |
{ | |
$this->encoder = $encoder; | |
$this->users = $users; | |
$this->notificator = $notificator; | |
} | |
public function handle(RegisterUserRequest $request) | |
{ | |
$user = (new UserBuilder() | |
->withEmail($request->get('email')) | |
->withPassword($request->get('password', $this->encoder) | |
->withName($request->get('name')) | |
->withBirthDay($request->get('birthday')) | |
->build(); | |
$this->users->add($user); | |
$this->notificator->notify(new UserRegisteredNotification($user)); | |
} | |
} |
<?php | |
namespace App\Domain\User; | |
class User | |
{ | |
private $id; | |
private $email; | |
private $unconfirmedEmail; | |
private $emailConfirmationToken; | |
private $password; | |
private $passwordsHistory; | |
private $name; | |
private $birthDay; | |
public function __construct(UserBuilder $builder) | |
{ | |
$this->email = $builder->email(); | |
$this->emailConfirmationToken = uuid4(); | |
$this->password = $builder->password(); | |
$this->passwordsHistory = []; | |
$this->name = $builder->name(); | |
$this->birthDay = $builder->birthDay(); | |
} | |
public function changeEmail(string $email, Notificator $notificator) | |
{ | |
$this->unconfirmedEmail = $email; | |
$this->resendConfirmationEmail($notificator); | |
} | |
public function confirmEmail(string $confirmationToken) | |
{ | |
if ($this->emailConfirmationToken !== $confirmationToken) { | |
throw new InvalidConfirmationTokenException(); | |
} | |
$this->emailConfirmationToken = null; | |
} | |
public function resendConfirmationEmail(Notificator $notificator) | |
{ | |
$this->emailConfirmationToken = uuid4(); | |
$notificator->notify(new ConfirmNewEmailAddressNotification($this->unconfirmedEmail, $this->email)); | |
} | |
public function changePassword(string $password, PasswordEncoder $encoder) | |
{ | |
$this->passwordsHistory[] = $this->password; | |
$this->failIfPasswordAlreadyHasBeenUsed(); | |
$this->password = $encoer->encode($password); | |
} | |
private function failIfPasswordAlreadyHasBeenUsed(string $password, PasswordEncoder $encoder) | |
{ | |
foreach ($this->passwordsHistory as $previousPassword) { | |
if ($this->encoder->isPasswordValid($previousPassword, $password) { | |
throw new PasswordIsAlreadyHasBeenUsedException(); | |
} | |
} | |
} | |
} |
<?php | |
class UserBuilder | |
{ | |
private $email; | |
private $password; | |
private $name; | |
private $birthDay; | |
public function withEmail($email) | |
{ | |
$this->email = $email; | |
return $this; | |
} | |
public function withPassword($password, PasswordEncoder $encoder) | |
{ | |
$this->password = $encoder->encode($password); | |
return $this; | |
} | |
public function withName($name) | |
{ | |
$this->name = $name; | |
return $this; | |
} | |
public function withBirthDay($birthDay) | |
{ | |
$this->birthDay = $birthDay; | |
return $this; | |
} | |
/** | |
* @return mixed | |
*/ | |
public function email() | |
{ | |
return $this->email; | |
} | |
/** | |
* @return mixed | |
*/ | |
public function password() | |
{ | |
return $this->password; | |
} | |
/** | |
* @return mixed | |
*/ | |
public function name() | |
{ | |
return $this->name; | |
} | |
/** | |
* @return mixed | |
*/ | |
public function birthDay() | |
{ | |
return $this->birthDay; | |
} | |
public function build() : User | |
{ | |
if (empty($this->email) | |
|| empty($this->password) | |
|| empty($this->name) { | |
throw \InvalidArgumentException('Fields email, password and name are required'); | |
} | |
return new User($this); | |
} | |
} |
думаю, для того, чтобы сущность User никогда не была в невалидном состоянии (например, с установленным емейлом, но не установленным паролем). А билдер в данном случае гарантирует нам создание только валидных сущностей
но для этого можно использовать конструктор, то-есть мы не можем создать объект без обязательных свойств
$user = (new User($request->get('name'),$request->get('email'),$request->get('password', $this->encoder))
->setBirthDay($request->get('birthday'))
;
@dimaxz, когда у тебя 10 аргументов в конструкторе - это не дело. Вопервых это не читабельно, во вторых - это не очень гибко (когда у тебя может быть несколько наборов обязательных полей в зависимости от какой-то логики), в третьих - добавь 11-ый аргумент в середину и все сломаешь.
А с билдерами - у тебя все читабельно, правила валидации можно перенести в конструктор, мне просто так не очень нравится, с точки зрения никапсуляции тоже все гуд.
@fesor убедительно, но остается такой момент если делаем вставку не валидного значения с последующим сохранением в бд например через маппер, как быть в данном случае делать еще одну валидацию?
$user->setEmail("incorrenct_mail");//или даже $user->setEmail(NULL);
try{
$userMapper->save($user);
}catch(Exception $e){
echo $e->getMessage();
}
@dimaxz так, вопервых допущение простое - у тебя никогда не должно быть такого кода, который "меняет состояние сущности и делает ее невалидно". Инварианты объекта всегда должны сохраняться.
->withEmail($request->get('email'))
И на первый взгляд это не сеттер ))
правильно я понимаю UserBuilder отвечает за валидацию данных при создании объекта? почему нельзя сразу создать entity