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

时间:2018-03-01 18:48:51

标签: php oop datamapper

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

由于我自学了PHP和所有编程,我最近才发现有关数据映射器的信息。它允许将数据传递到类中,而不会让所述类知道数据的来源。我已经阅读了使用映射器的一些好处以及为什么它们使它变得更容易'稍后进行升级,但我真的很难找到在目录结构中使用映射器及其布局的推荐方法。

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

我一直在使用/创建映射器的方式(以及文件结构如下):

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

类/ 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);
  }
}

类/ 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;
  }
}

了解到users表包含用户名,名字和姓氏,以及缺少许多清理检查,为什么mappers方便使用?

这是获取数据的一种非常漫长的方式,并假设用户不是所有东西,而是订单,付款,票证,公司等都有相应的映射器,似乎不浪费不创建一个映射器并在每个类的每个地方实现它。 这允许文件夹结构看起来更好,并且还意味着代码不经常重复。

示例映射器在每种情况下看起来都是相同的,从表中拉出数据。

因此我的问题是。这就是“域名模型映射器”下的数据映射器。看起来应该是,如果不是,我的代码怎么可能改进?其次,无论类的大小如何,在需要从数据库中提取数据的所有情况下都需要此模型,或者此模型是否仅在此情况下 user.php 类非常大的情况下使用?

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

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

示例:

基类:const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));

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

文件:BookCategoryEntity.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;
    }
}

表结构:schema.sql

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

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

用法

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');