这是域映射器模型的通用结构吗?

Is this the common structure for the domain mapper model?

希望我是在正确的堆栈交换论坛上问这个问题。如果没有,请告诉我,我会去别的地方问。我也在 Code Review 上问过,但是社区似乎不太活跃。

因为我自学了 PHP 和所有一般的编程,我最近才发现 'Data Mappers' 允许将数据传递到 classes 而无需说 class知道数据的来源。我已经阅读了使用映射器的一些积极因素以及为什么它们 'easier' 稍后会执行升级,但是我真的很难找到在目录结构中使用映射器及其布局的推荐方式。

假设我们有一个简单的应用程序,其目的是回显用户的名字和姓氏。

我一直using/creating映射的方式(以及文件结构如下):

index.php

include 'classes/usermapper.php';
include 'classes/user.php';

$user = new User;
$userMapper = new userMapper;

try {
  $user->setData([
    $userMapper->fetchData([
      'username'=>'peter1'
    ])
  ]);
} catch (Exception $e) {
  die('Error occurred');
}

if ($user->hasData()) {
  echo $user->fullName();
}

classes/user.php

class User {
  private $_data;

  public function __construct() { }

  public function setData($userObject = null) {
    if (!$userObject) { throw new InvalidArgumentException('No Data Set'); }
    $this->_data = $dataObject;
  }

  public function hasData() {
    return (!$this->_data) ? false : true;
  }

  public function fullName() {
    return ucwords($this->_data->firstname.' '.$this->_data->lastname);
  }
}

classes/usermapper.php

class userMapper {
  private $_db;

  public function __construct() { $this->_db = DB::getInstance(); }

  public function fetchData($where = null) {
    if (!is_array($where)) { 
      throw new InvalidArgumentException('Invalid Params Supplied'); 
    }

    $toFill = null;
    foreach($where as $argument=>$value) {
      $toFill .= $argument.' = '.$value AND ;
    }

    $query = sprintf("SELECT * FROM `users` WHERE %s ", substr(rtrim($toFill), 0, -3));


    $result = $this->_db->query($query); //assume this is just a call to a database which returns the results of the query

    return $result;
  }
}

了解用户 table 包含用户名、名字和姓氏,并且还缺少许多清理检查,为什么映射器使用方便?

这种获取数据的方式很啰嗦,假设用户不是一切,订单、支付、票务、公司等都有对应的映射器,不创建就显得浪费了一个映射器并在每个 class 中到处实现它。 这使文件夹结构看起来更好,也意味着代码不会经常重复。

示例映射器在每种情况下看起来都一样,除了 table 从中提取数据。

所以我的问题是。 'domain model mappers' 下的数据映射器应该是这样的吗?如果不是,我的代码该如何改进?其次,无论 class 的大小如何,是否在所有需要从数据库中提取数据的情况下都需要此模型,或者应该仅在 user.php[=41 的情况下使用此模型=] class 在这种情况下非常大?

预先感谢您的所有帮助。

Data Mapper 将域对象与持久存储(数据库)完全分离,并提供特定于domain-level 操作的方法。使用它将数据从域传输到数据库,反之亦然。在一个方法中,通常会执行数据库查询,然后将结果映射(水合)到域对象或域对象列表。

示例:

基地class:Mapper.php

abstract class Mapper
{
    protected $db;

    public function __construct(PDO $db)
    {
        $this->db = $db;
    }
}

文件:BookMapper.php

class BookMapper extends Mapper
{
    public function findAll(): array
    {
        $sql = "SELECT id, title, price, book_category_id FROM books;";
        $statement = $this->db->query($sql);

        $items = [];
        while ($row = $statement->fetch()) {
            $items[] = new BookEntity($row);
        }

        return $items;
    }

    public function findByBookCategoryId(int $bookCategoryId): array
    {
        $sql = "SELECT id, title, price, book_category_id 
                FROM books
                WHERE book_category_id = :book_category_id;";

        $statement = $this->db->prepare($sql);
        $statement->execute(["book_category_id" => $bookCategoryId]);

        $items = [];
        while ($row = $statement->fetch()) {
            $items[] = new BookEntity($row);
        }

        return $items;
    }

    /**
     * Get one Book by its ID
     *
     * @param int $bookId The ID of the book
     * @return BookEntity The book
     * @throws RuntimeException
     */
    public function getById(int $bookId): BookEntity
    {
        $sql = "SELECT id, title, price, book_category_id FROM books 
                WHERE id = :id;";

        $statement = $this->db->prepare($sql);

        if (!$result = $statement->execute(["id" => $bookId])) {
            throw new DomainException(sprintf('Book-ID not found: %s', $bookId));
        }

        return new BookEntity($statement->fetch());
    }

    public function insert(BookEntity $book): int
    {
        $sql = "INSERT INTO books SET title=:title, price=:price, book_category_id=:book_category_id";
        $statement = $this->db->prepare($sql);

        $result = $statement->execute([
            'title' => $book->getTitle(),
            'price' => $book->getPrice(),
            'book_category_id' => $book->getBookCategoryId(),
        ]);

        if (!$result) {
            throw new RuntimeException('Could not save record');
        }

        return (int)$this->db->lastInsertId();
    }
}

文件:BookEntity.php

class BookEntity
{
    /** @var int|null */
    protected $id;

    /** @var string|null */
    protected $title;

    /** @var float|null */
    protected $price;

    /** @var int|null */
    protected $bookCategoryId;

    /**
     * Accept an array of data matching properties of this class
     * and create the class
     *
     * @param array|null $data The data to use to create
     */
    public function __construct(array $data = null)
    {
        // Hydration (manually)
        if (isset($data['id'])) {
            $this->setId($data['id']);
        }
        if (isset($data['title'])) {
            $this->setTitle($data['title']);
        }
        if (isset($data['price'])) {
            $this->setPrice($data['price']);
        }
        if (isset($data['book_category_id'])) {
            $this->setBookCategoryId($data['book_category_id']);
        }
    }

    /**
     * Get Id.
     *
     * @return int|null
     */
    public function getId(): ?int
    {
        return $this->id;
    }

    /**
     * Set Id.
     *
     * @param int|null $id
     * @return void
     */
    public function setId(?int $id): void
    {
        $this->id = $id;
    }

    /**
     * Get Title.
     *
     * @return null|string
     */
    public function getTitle(): ?string
    {
        return $this->title;
    }

    /**
     * Set Title.
     *
     * @param null|string $title
     * @return void
     */
    public function setTitle(?string $title): void
    {
        $this->title = $title;
    }

    /**
     * Get Price.
     *
     * @return float|null
     */
    public function getPrice(): ?float
    {
        return $this->price;
    }

    /**
     * Set Price.
     *
     * @param float|null $price
     * @return void
     */
    public function setPrice(?float $price): void
    {
        $this->price = $price;
    }

    /**
     * Get BookCategoryId.
     *
     * @return int|null
     */
    public function getBookCategoryId(): ?int
    {
        return $this->bookCategoryId;
    }

    /**
     * Set BookCategoryId.
     *
     * @param int|null $bookCategoryId
     * @return void
     */
    public function setBookCategoryId(?int $bookCategoryId): void
    {
        $this->bookCategoryId = $bookCategoryId;
    }
}

文件:BookCategoryEntity.php

class BookCategoryEntity
{
    const FANTASY = 1;
    const ADVENTURE = 2;
    const COMEDY = 3;

    // here you can add the setter and getter methods
}

table结构:schema.sql

CREATE TABLE `books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `price` decimal(19,2) DEFAULT NULL,
  `book_category_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `book_category_id` (`book_category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `book_categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

/*Data for the table `book_categories` */

insert  into `book_categories`(`id`,`title`) values (1,'Fantasy');
insert  into `book_categories`(`id`,`title`) values (2,'Adventure');
insert  into `book_categories`(`id`,`title`) values (3,'Comedy');

用法

// Create the database connection
$host = '127.0.0.1';
$dbname = 'test';
$username = 'root';
$password = '';
$charset = 'utf8';
$collate = 'utf8_unicode_ci';
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_PERSISTENT => false,
    PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES $charset COLLATE $collate"
];

$db = new PDO($dsn, $username, $password, $options);

// Create the data mapper instance
$bookMapper = new BookMapper($db);

// Create a new book entity
$book = new BookEntity();
$book->setTitle('Harry Potter');
$book->setPrice(29.99);
$book->setBookCategoryId(BookCategoryEntity::FANTASY);

// Insert the book entity
$bookId = $bookMapper->insert($book);

// Get the saved book
$newBook = $bookMapper->getById($bookId);
var_dump($newBook);

// Find all fantasy books
$fantasyBooks = $bookMapper->findByBookCategoryId(BookCategoryEntity::FANTASY);
var_dump($fantasyBooks);