PHP ООП: Как структурировать Mapper, который требует функциональности от другого 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, который получает (и вставляет / обновляет / удаляет) Category-объекты из базы данных:

CategoryMapper

<?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() Метод должен возвращать объект Product с объектом Category внутри него.

Итак, теперь мой вопрос: как должен быть структурирован мой 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 требуется много других Mappers, я мог бы здесь не заметить, какие Mappers на самом деле используются 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 и дублируйте код build-a-category в 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;

}
}
?>

Так как это обычно делается?
Есть ли другие варианты, о которых я не думал?

0

Решение

Краткий ответ: первый метод правильный. Вы должны передать все зависимости как параметры в конструкторе.

Длинный ответ: первый метод правильный, но рекомендуется использовать некоторый контейнер для инъекций зависимостей. Он позаботится об инициализации объектов и передаче их в качестве параметров другим объектам. Это также обеспечит инициализацию только одного объекта каждого класса. Если вы хотите что-то легкое, вы можете пойти с Прыщ или, если вы предпочитаете что-то более настраиваемое с множеством функций, Компонент DependencyInjection от Symfony.

1

Другие решения

Я думаю, что вы должны добавить метод singleton в классе CategoryMapper, как показано ниже

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();
}
0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector