PHP OOP:如何构建需要来自另一个 Mapper 的功能的 Mapper
PHP OOP: How to structure a Mapper that requires functionality from another Mapper
假设我有两个对象,类别和产品:
类别
<?php class Category {
private $name;
public function __construct(string $name) {
$this->name = $name;
}
}
?>
产品
<?php class Product {
private $name, $category;
public function __construct(string $name, Category $category) {
$this->name = $name;
$this->category = $category;
}
}
?>
我有一个 CategoryMapper,它从数据库中获取(和 inserts/updates/deletes)类别对象:
分类映射器
<?php
class CategoryMapper {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function ofId($id) : ?Category {
$query = 'SELECT name FROM category WHERE id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
return new Category($row['name']);
}
return null;
}
// Other methods
}
?>
我还需要一个 ProductMapper,但是 ProductMapper 的 ofId()
方法需要 return 一个包含 Category 对象的 Product 对象。
那么现在我的问题是:我的 ProductMapper 应该如何构建?
选项 1 - 在构造函数中将 ProductMapper 传递给 CategoryMapper
我想如果 CategoryMapper 又需要另一个 Mapper,而那个 Mapper 又需要另一个 Mapper,等等,这可能会变得相当大。
<?php
class ProductMapper {
private $pdo, $categoryMapper;
public function __construct(PDO $pdo, CategoryMapper $categoryMapper) {
$this->pdo = $pdo;
$this->categoryMapper = $categoryMapper;
}
public function ofId($id) {
$query = 'SELECT name, categoryId FROM product WHERE id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
$category = $this->categoryMapper->ofId($row['categoryId']);
return new Product($row['name'], $category);
}
return null;
}
}
?>
选项 2 - 让 ProductMapper 在需要的方法中创建一个 CategoryMapper
如果 ProductMapper 需要很多其他映射器,我可能会在这里忘记 ProductMapper 实际使用了哪些映射器,因为它们没有列在一个地方,而是分散在整个代码中。
<?php
class ProductMapper {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function ofId($id) {
$query = 'SELECT name, categoryId FROM product WHERE id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
$categoryMapper = new CategoryMapper($this->pdo);
$category = $categoryMapper->ofId($row['categoryId']);
return new Product($row['name'], $category);
}
return null;
}
}
?>
选项 3 - 不使用 CategoryMapper 并在 ProductMapper 中复制构建类别代码
这只会导致大量重复代码(尤其是当嵌入的对象变大或内部也嵌入了对象时)。
<?php
class ProductMapper {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function ofId($id) {
$query = 'SELECT p.name as productName, c.name as categoryName FROM product p join category c on p.categoryId = c.id WHERE p.id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
$category = new Category($row['categoryName']);
return new Product($row['productName'], $category);
}
return null;
}
}
?>
那么这通常是如何完成的呢?
还有其他我没有想到的选择吗?
我认为您应该在 CategoryMapper class 中添加一个单例方法,如下所示
public static $instance = null;
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
ProductMapper的调用如下
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
$this->categoryMapper = CategoryMapper::getInstance();
}
简答:第一种方法是正确的。您应该在构造函数中将所有依赖项作为参数传递。
长答案:第一种方法是正确的,但最好使用一些依赖注入容器。它将负责初始化对象并将它们作为参数传递给另一个对象。它还将确保每个 class 中只有一个对象被初始化。如果你想要轻量级的东西,你可以选择 Pimple or, if you prefer something more configurable with many features, Symfony's DependencyInjection component.
假设我有两个对象,类别和产品:
类别
<?php class Category {
private $name;
public function __construct(string $name) {
$this->name = $name;
}
}
?>
产品
<?php class Product {
private $name, $category;
public function __construct(string $name, Category $category) {
$this->name = $name;
$this->category = $category;
}
}
?>
我有一个 CategoryMapper,它从数据库中获取(和 inserts/updates/deletes)类别对象:
分类映射器
<?php
class CategoryMapper {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function ofId($id) : ?Category {
$query = 'SELECT name FROM category WHERE id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
return new Category($row['name']);
}
return null;
}
// Other methods
}
?>
我还需要一个 ProductMapper,但是 ProductMapper 的 ofId()
方法需要 return 一个包含 Category 对象的 Product 对象。
那么现在我的问题是:我的 ProductMapper 应该如何构建?
选项 1 - 在构造函数中将 ProductMapper 传递给 CategoryMapper
我想如果 CategoryMapper 又需要另一个 Mapper,而那个 Mapper 又需要另一个 Mapper,等等,这可能会变得相当大。
<?php
class ProductMapper {
private $pdo, $categoryMapper;
public function __construct(PDO $pdo, CategoryMapper $categoryMapper) {
$this->pdo = $pdo;
$this->categoryMapper = $categoryMapper;
}
public function ofId($id) {
$query = 'SELECT name, categoryId FROM product WHERE id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
$category = $this->categoryMapper->ofId($row['categoryId']);
return new Product($row['name'], $category);
}
return null;
}
}
?>
选项 2 - 让 ProductMapper 在需要的方法中创建一个 CategoryMapper
如果 ProductMapper 需要很多其他映射器,我可能会在这里忘记 ProductMapper 实际使用了哪些映射器,因为它们没有列在一个地方,而是分散在整个代码中。
<?php
class ProductMapper {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function ofId($id) {
$query = 'SELECT name, categoryId FROM product WHERE id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
$categoryMapper = new CategoryMapper($this->pdo);
$category = $categoryMapper->ofId($row['categoryId']);
return new Product($row['name'], $category);
}
return null;
}
}
?>
选项 3 - 不使用 CategoryMapper 并在 ProductMapper 中复制构建类别代码
这只会导致大量重复代码(尤其是当嵌入的对象变大或内部也嵌入了对象时)。
<?php
class ProductMapper {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function ofId($id) {
$query = 'SELECT p.name as productName, c.name as categoryName FROM product p join category c on p.categoryId = c.id WHERE p.id = ?';
$stmt = $pdo->prepare($query);
$stmt->execute([$id]);
if ($row = $stmt->fetch()) {
$category = new Category($row['categoryName']);
return new Product($row['productName'], $category);
}
return null;
}
}
?>
那么这通常是如何完成的呢? 还有其他我没有想到的选择吗?
我认为您应该在 CategoryMapper class 中添加一个单例方法,如下所示
public static $instance = null;
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
ProductMapper的调用如下
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
$this->categoryMapper = CategoryMapper::getInstance();
}
简答:第一种方法是正确的。您应该在构造函数中将所有依赖项作为参数传递。
长答案:第一种方法是正确的,但最好使用一些依赖注入容器。它将负责初始化对象并将它们作为参数传递给另一个对象。它还将确保每个 class 中只有一个对象被初始化。如果你想要轻量级的东西,你可以选择 Pimple or, if you prefer something more configurable with many features, Symfony's DependencyInjection component.