Sne*_*ike 3 php model-view-controller datamapper domain-object slim-3
I\xe2\x80\x99m 在后端使用 Slim 3 PHP 创建身份验证/登录系统,在前端使用 Angular,并且 I\xe2\x80\x99m 尝试理解 \xe2\x80\x98domain 对象\xe2 \x80\x99 和 \xe2\x80\x98data mapper\xe2\x80\x99 是 MVC 结构中模型层的一部分。我\xe2\x80\x99已经阅读了很多关于诸如此类的问题的有用答案,从中我了解到模型应该由\xe2\x80\x98domain对象\xe2\x80\x99、\xe2\x80\x98data组成映射器\xe2\x80\x99 和\xe2\x80\x98services\xe2\x80\x99。
\n\n然而,我\xe2\x80\x99m 不太确定在用户能够注册并登录网站的情况下应该如何构建它。
\n\n根据我的理解,我可以拥有一个具有用户名和密码等属性的用户“域对象”。它还可以具有注册或登录等方法来表示业务逻辑。
\n\n然后我是否会有一个服务类来创建用户对象的新实例,在其中我将表单数据传递到该对象中?那么现在我的用户对象实例将设置用户名和密码值?
\n\n现在我不确定如何将此对象属性数据插入到数据库中。我是否可以使用用户对象注册方法通过传入用户名和密码作为参数来将数据插入数据库?
\n\n显然,服务应该是域对象和数据映射器交互的地方,但我不确定如果注册方法位于用户域对象中,这将如何工作。
\n\n我希望有人可以向我展示一些代码示例,说明服务类中应包含哪些内容,以及域对象和数据映射器之间的交互如何在用户注册和登录的上下文中工作。
\n\n注意我不想使用任何框架,我想尝试手动实现正确的 MVC 结构,因为我觉得我会了解更多。
\n\n到目前为止,我有这样的结构来注册用户:
\n\n我有一个带有 registerUser 方法的 AuthenticationController 来允许用户创建帐户:
\n\n class AuthenticationController\n{\n protected $authenticationService;\n\n public function __construct(AuthenticationService $authenticationService)\n {\n $this->authenticationService = $authenticationService;\n }\n\n public function registerUser($request, $response)\n {\n $this->authenticationService->registerUser($request, $response);\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后我有带有 registerUser 方法的 AuthenticationService 类:
\n\nclass AuthenticationService\n{\n protected $database;\n\n public function __construct(PDO $database)\n {\n $this->database = $database;\n }\n\n public function registerUser ($request, $response)\n {\n $strings = $request\xe2\x86\x92getParsedBody(); // will be sanitised / validated later\n $username = $strings[\'username\'];\n $password = $strings[\'password\'];\n $email = "temp random email";\n\n $stmt = $this->database->prepare("INSERT INTO users (email, username, password) values (:email, :username, :password)");\n $stmt->bindParam(\':email\', $email);\n $stmt->bindParam(\':username\', $username);\n $stmt->bindParam(\':password\', $password);\n $stmt->execute();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n稍后我打算将 SQL 放入 AuthenticationRepository 并将 PDO 逻辑放入 \xe2\x80\x99s 自己的类中。此 AuthenticationService 方法还将确保使用 PHP\xe2\x80\x99s 内置函数清理用户详细信息。
\n\nI\xe2\x80\x99m 不确定建议的 PDO 数据库类或 AuthenticationRepository 是否算作数据映射器。
\n这是一个注册示例。我根本没有测试过。有关更多详细信息,请参阅本答案末尾的资源列表。也许从最后一个开始,我刚刚意识到,这是您问题的答案。
使用的文件系统结构:
a) 扩展“MyApp/UI”:
b) 扩展“MyApp/Domain”:
控制器:
<?php
namespace MyApp\UI\Web\Controller\Users;
use Psr\Http\Message\ServerRequestInterface;
use MyApp\Domain\Model\Users\Exception\InvalidData;
use MyApp\Domain\Service\Users\Exception\FailedRegistration;
use MyApp\Domain\Service\Users\Registration as RegistrationService;
class Registration {
private $registration;
public function __construct(RegistrationService $registration) {
$this->registration = $registration;
}
public function register(ServerRequestInterface $request) {
$username = $request->getParsedBody()['username'];
$password = $request->getParsedBody()['password'];
$email = $request->getParsedBody()['email'];
try {
$user = $this->registration->register($username, $password, $email);
} catch (InvalidData $exc) {
// Write the exception message to a flash messenger, for example,
// in order to be read and displayed by the specific view component.
var_dump($exc->getMessage());
} catch (FailedRegistration $exc) {
// Write the exception message to the flash messenger.
var_dump($exc->getMessage());
}
// In the view component, if no exception messages are found in the flash messenger, display a success message.
var_dump('Successfully registered.');
}
}
Run Code Online (Sandbox Code Playgroud)
服务:
<?php
namespace MyApp\Domain\Service\Users;
use MyApp\Domain\Model\Users\User;
use MyApp\Domain\Model\Users\Email;
use MyApp\Domain\Model\Users\Password;
use MyApp\Domain\Service\Users\Exception\UserExists;
use MyApp\Domain\Model\Users\UserCollection as UserCollectionInterface;
class Registration {
/**
* User collection, e.g. user repository.
*
* @var UserCollectionInterface
*/
private $userCollection;
public function __construct(UserCollectionInterface $userCollection) {
$this->userCollection = $userCollection;
}
/**
* Register user.
*
* @param string $username Username.
* @param string $password Password.
* @param string $email Email.
* @return User User.
*/
public function register(string $username, string $password, string $email) {
$user = $this->createUser($username, $password, $email);
return $this->storeUser($user);
}
/**
* Create user.
*
* @param string $username Username.
* @param string $password Password.
* @param string $email Email.
* @return User User.
*/
private function createUser(string $username, string $password, string $email) {
// Create the object values (containing specific validation).
$email = new Email($email);
$password = new Password($password);
// Create the entity (e.g. the domain object).
$user = new User();
$user->setUsername($username);
$user->setEmail($email);
$user->setPassword($password);
return $user;
}
/**
* Store user.
*
* @param User $user User.
* @return User User.
*/
private function storeUser(User $user) {
// Check if user already exists.
if ($this->userCollection->exists($user)) {
throw new UserExists();
}
return $this->userCollection->store($user);
}
}
Run Code Online (Sandbox Code Playgroud)
尝试注册已有用户时抛出异常:
<?php
namespace MyApp\Domain\Service\Users\Exception;
use MyApp\Domain\Service\Users\Exception\FailedRegistration;
class UserExists extends FailedRegistration {
public function __construct(\Exception $previous = null) {
$message = 'User already exists.';
$code = 123;
parent::__construct($message, $code, $previous);
}
}
<?php
namespace MyApp\Domain\Service\Users\Exception;
abstract class FailedRegistration extends \Exception {
public function __construct(string $message, int $code = 0, \Exception $previous = null) {
$message = 'Registration failed: ' . $message;
parent::__construct($message, $code, $previous);
}
}
Run Code Online (Sandbox Code Playgroud)
域对象(实体):
<?php
namespace MyApp\Domain\Model\Users;
use MyApp\Domain\Model\Users\Email;
use MyApp\Domain\Model\Users\Password;
/**
* User entity (e.g. domain object).
*/
class User {
private $id;
private $username;
private $email;
private $password;
public function getId() {
return $this->id;
}
public function setId(int id) {
$this->id = $id;
return $this;
}
public function getUsername() {
return $this->username;
}
public function setUsername(string $username) {
$this->username = $username;
return $this;
}
public function getEmail() {
return $this->email;
}
public function setEmail(Email $email) {
$this->email = $email;
return $this;
}
public function getPassword() {
return $this->password;
}
public function setPassword(Password $password) {
$this->password = $password;
return $this;
}
}
Run Code Online (Sandbox Code Playgroud)
实体使用的值对象:
<?php
namespace MyApp\Domain\Model\Users;
use MyApp\Domain\Model\Users\Exception\InvalidEmail;
/**
* Email object value.
*/
class Email {
private $email;
public function __construct(string $email) {
if (!$this->isValid($email)) {
throw new InvalidEmail();
}
$this->email = $email;
}
private function isValid(string $email) {
return (isEmpty($email) || !isWellFormed($email)) ? false : true;
}
private function isEmpty(string $email) {
return empty($email) ? true : false;
}
private function isWellFormed(string $email) {
return !filter_var($email, FILTER_VALIDATE_EMAIL) ? false : true;
}
public function __toString() {
return $this->email;
}
}
<?php
namespace MyApp\Domain\Model\Users;
use MyApp\Domain\Model\Users\Exception\InvalidPassword;
/**
* Password object value.
*/
class Password {
private const MIN_LENGTH = 8;
private $password;
public function __construct(string $password) {
if (!$this->isValid($password)) {
throw new InvalidPassword();
}
$this->password = $password;
}
private function isValid(string $password) {
return (isEmpty($password) || isTooShort($password)) ? false : true;
}
private function isEmpty(string $password) {
return empty($password) ? true : false;
}
private function isTooShort(string $password) {
return strlen($password) < self::MIN_LENGTH ? true : false;
}
public function __toString() {
return $this->password;
}
}
Run Code Online (Sandbox Code Playgroud)
值对象抛出的异常:
<?php
namespace MyApp\Domain\Model\Users\Exception;
use MyApp\Domain\Model\Users\Exception\InvalidData;
class InvalidEmail extends InvalidData {
public function __construct(\Exception $previous = null) {
$message = 'The email address is not valid.';
$code = 123402;
parent::__construct($message, $code, $previous);
}
}
<?php
namespace MyApp\Domain\Model\Users\Exception;
use MyApp\Domain\Model\Users\Exception\InvalidData;
class InvalidPassword extends InvalidData {
public function __construct(\Exception $previous = null) {
$message = 'The password is not valid.';
$code = 123401;
parent::__construct($message, $code, $previous);
}
}
<?php
namespace MyApp\Domain\Model\Users\Exception;
abstract class InvalidData extends \LogicException {
public function __construct(string $message, int $code = 0, \Exception $previous = null) {
$message = 'Invalid data: ' . $message;
parent::__construct($message, $code, $previous);
}
}
Run Code Online (Sandbox Code Playgroud)
存储库接口:
<?php
namespace MyApp\Domain\Model\Users;
use MyApp\Domain\Model\Users\User;
/**
* User collection, e.g. user repository.
*/
interface UserCollection {
/**
* Find a user by id.
*
* @param int $id User id.
* @return User|null User.
*/
public function findById(int $id);
/**
* Find all users.
*
* @return User[] User list.
*/
public function findAll();
/**
* Check if the given user exists.
*
* @param User $user User
* @return bool True if user exists, false otherwise.
*/
public function exists(User $user);
/**
* Store a user.
*
* @param User $user User
* @return User User.
*/
public function store(User $user);
}
Run Code Online (Sandbox Code Playgroud)
存储库:
<?php
namespace MyApp\Domain\Infrastructure\Repository\Users;
use MyApp\Domain\Model\Users\User;
use MyApp\Domain\Infrastructure\Mapper\Users\UserMapper;
use MyApp\Domain\Model\Users\UserCollection as UserCollectionInterface;
/**
* User collection, e.g. user repository.
*/
class UserCollection implements UserCollectionInterface {
private $userMapper;
public function __construct(UserMapper $userMapper) {
$this->userMapper = $userMapper;
}
/**
* Find a user by id.
*
* @param int $id User id.
* @return User|null User.
*/
public function findById(int $id) {
return $this->userMapper->fetchUserById($id);
}
/**
* Find all users.
*
* @return User[] User list.
*/
public function findAll() {
return $this->userMapper->fetchAllUsers();
}
/**
* Check if the given user exists.
*
* @param User $user User
* @return bool True if user exists, false otherwise.
*/
public function exists(User $user) {
return $this->userMapper->userExists($user);
}
/**
* Store a user.
*
* @param User $user User
* @return User User.
*/
public function store(User $user) {
return $this->userMapper->saveUser($user);
}
}
Run Code Online (Sandbox Code Playgroud)
数据映射器接口:
<?php
namespace MyApp\Domain\Infrastructure\Mapper\Users;
use MyApp\Domain\Model\Users\User;
/**
* User mapper.
*/
interface UserMapper {
/**
* Fetch a user by id.
*
* @param int $id User id.
* @return User|null User.
*/
public function fetchUserById(int $id);
/**
* Fetch all users.
*
* @return User[] User list.
*/
public function fetchAllUsers();
/**
* Check if the given user exists.
*
* @param User $user User.
* @return bool True if the user exists, false otherwise.
*/
public function userExists(User $user);
/**
* Save a user.
*
* @param User $user User.
* @return User User.
*/
public function saveUser(User $user);
}
Run Code Online (Sandbox Code Playgroud)
数据映射器:
<?php
namespace MyApp\Domain\Infrastructure\Mapper\Users;
use PDO;
use MyApp\Domain\Model\Users\User;
use MyApp\Domain\Model\Users\Email;
use MyApp\Domain\Model\Users\Password;
use MyApp\Domain\Infrastructure\Mapper\Users\UserMapper;
/**
* PDO user mapper.
*/
class PdoUserMapper implements UserMapper {
/**
* Database connection.
*
* @var PDO
*/
private $connection;
public function __construct(PDO $connection) {
$this->connection = $connection;
}
/**
* Fetch a user by id.
*
* Note: PDOStatement::fetch returns FALSE if no record is found.
*
* @param int $id User id.
* @return User|null User.
*/
public function fetchUserById(int $id) {
$sql = 'SELECT * FROM users WHERE id = :id LIMIT 1';
$statement = $this->connection->prepare($sql);
$statement->execute([
'id' => $id,
]);
$record = $statement->fetch(PDO::FETCH_ASSOC);
return ($record === false) ? null : $this->convertRecordToUser($record);
}
/**
* Fetch all users.
*
* @return User[] User list.
*/
public function fetchAllUsers() {
$sql = 'SELECT * FROM users';
$statement = $this->connection->prepare($sql);
$statement->execute();
$recordset = $statement->fetchAll(PDO::FETCH_ASSOC);
return $this->convertRecordsetToUserList($recordset);
}
/**
* Check if the given user exists.
*
* Note: PDOStatement::fetch returns FALSE if no record is found.
*
* @param User $user User.
* @return bool True if the user exists, false otherwise.
*/
public function userExists(User $user) {
$sql = 'SELECT COUNT(*) as cnt FROM users WHERE username = :username';
$statement = $this->connection->prepare($sql);
$statement->execute([
':username' => $user->getUsername(),
]);
$record = $statement->fetch(PDO::FETCH_ASSOC);
return ($record['cnt'] > 0) ? true : false;
}
/**
* Save a user.
*
* @param User $user User.
* @return User User.
*/
public function saveUser(User $user) {
$id = $user->getId();
if (!isset($id)) {
return $this->insertUser($user);
}
return $this->updateUser($user);
}
/**
* Insert a user.
*
* @param User $user User.
* @return User User.
*/
private function insertUser(User $user) {
$sql = 'INSERT INTO users (
username,
password,
email
) VALUES (
:username,
:password,
:email
)';
$statement = $this->connection->prepare($sql);
$statement->execute([
':username' => $user->getUsername(),
':password' => (string) $user->getPassword(),
':email' => (string) $user->getEmail(),
]);
$user->setId($this->connection->lastInsertId());
return $user;
}
/**
* Update a user.
*
* @param User $user User.
* @return User User.
*/
private function updateUser(User $user) {
$sql = 'UPDATE users
SET
username = :username,
password = :password,
email = :email
WHERE id = :id';
$statement = $this->connection->prepare($sql);
$statement->execute([
':id' => $user->getId(),
':username' => $user->getUsername(),
':password' => (string) $user->getPassword(),
':email' => (string) $user->getEmail(),
]);
return $user;
}
/**
* Convert a record to a user.
*
* @param array $record Record data.
* @return User User.
*/
private function convertRecordToUser(array $record) {
$user = $this->createUser(
$record['id'],
$record['username'],
$record['password'],
$record['email']
);
return $user;
}
/**
* Convert a recordset to a list of users.
*
* @param array $recordset Recordset data.
* @return User[] User list.
*/
private function convertRecordsetToUserList(array $recordset) {
$users = [];
foreach ($recordset as $record) {
$users[] = $this->convertRecordToUser($record);
}
return $users;
}
/**
* Create user.
*
* @param int $id User id.
* @param string $username Username.
* @param string $password Password.
* @param string $email Email.
* @return User User.
*/
private function createUser(int $id, string $username, string $password, string $email) {
$user = new User();
$user
->setId($id)
->setUsername($username)
->setPassword(new Password($password))
->setEmail(new Email($email))
;
return $user;
}
}
Run Code Online (Sandbox Code Playgroud)
资源:
| 归档时间: |
|
| 查看次数: |
1656 次 |
| 最近记录: |