存储库和数据映射器耦合

Got*_*ist 0 php model-view-controller datamapper decoupling repository-pattern

我有一个控制器,可以获取要传递给视图的数据.向此注入(通过疙瘩容器)服务,该服务使用许多域模型+业务逻辑来创建数据.

服务本身有一个注入其中的"存储库"类,它有创建数据映射器和返回域模型实例的方法.

我知道我可能没有深入了解存储库概念,因为Martin Fowler将其设置为"在映射层上构建另一层抽象"和"存储库在域和数据映射层之间进行调解,就像在-memory域对象集合." 所以我可能会错误地使用这个术语.

服务:

class InputService
    {
        private $repos;

        public function __construct($repo) {
            $this->repos = $repo;
        }

        public function getInitialData()
        {
            $product = $this->repo->getProduct();
            $country = $this->repo->getCountry();
            $spinalPoint = $this->repo->getPoint();

            /*business logic with model instances to produce data array*/

            return //array of data
        }
    }
Run Code Online (Sandbox Code Playgroud)

库:

class InputRepository
    {
        private $db;

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

        public function getCountry()
        {
            $mapper = new CountryMapper($this->db);
            $country = $mapper->fetch();
            return $country; //returns country object
        }
        // lots of other methods for returning different model objects
    }
Run Code Online (Sandbox Code Playgroud)

映射器:

class CountryMapper
    {
        private $db;

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

        public function fetch()
        {
            $data = //code to grab data from db;

            $obj = new Country($data);
            return $obj;
        }
    }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,映射器与存储库类紧密耦合,但是我无法看到它的方法.

我想知道是否有办法实现这个存储库,提供与数据映射器类更松散的耦合?

在宏观方案中,这个应用程序相当小,因此不得不在两者之间更新代码并不是灾难性的,但是现在你永远不会增长!

小智 8

  • db操作应该通过适配器(MySqliAdapter,PdoAdapter等)执行.因此,db连接被注入适配器,而不是映射到映射器.当然不是在存储库中,因为存储库的抽象目的是没有意义的.
  • 映射器接收适配器作为依赖项,也可以接收其他映射器.
  • 映射器作为依赖项传递给存储库.
  • 存储库名称在语义上与域层名称相关,而不是与服务层的名称相关.例如:"InputService":好的."InputRepository":错了."CountryRepository":正确.
  • 服务可以接收更多存储库.或者,如果您不想应用额外的存储库层,则使用映射器.
  • 在代码中,唯一紧密耦合的结构是Country对象(实体或域对象) - 为每个获取的表行动态创建.即使这样也可以通过使用域对象工厂来避免,但我个人认为没有必要.

PS:很抱歉没有提供更详细的代码.

服务

class InputService {

    private $countryRepository;
    private $productRepository;

    public function __construct(CountryRepositoryInterface $countryRepository, ProductRepositoryInterface $productRepository) {
        $this->countryRepository = $countryRepository;
        $this->productRepository = $productRepository;
    }

    public function getInitialData() {
        $products = $this->productRepository->findAll();
        $country = $this->countryRepository->findByName('England');

        //...

        return // resulted data
    }

}
Run Code Online (Sandbox Code Playgroud)

知识库

class CountryRepository implements CountryRepositoryInterface {

    private $countryMapper;

    public function __construct(CountryMapperInterface $countryMapper) {
        $this->countryMapper = $countryMapper;
    }

    public function findByPrefix($prefix) {
        return $this->countryMapper->find(['prefix' => $prefix]);
    }

    public function findByName($name) {
        return $this->countryMapper->find(['name' => $name]);
    }

    public function findAll() {
        return $this->countryMapper->find();
    }

    public function store(CountryInterface $country) {
        return $this->countryMapper->save($country);
    }

    public function remove(CountryInterface $country) {
        return $this->countryMapper->delete($country);
    }

}
Run Code Online (Sandbox Code Playgroud)

数据映射器

class CountryMapper implements CountryMapperInterface {

    private $adapter;
    private $countryCollection;

    public function __construct(AdapterInterface $adapter, CountryCollectionInterface $countryCollection) {
        $this->adapter = $adapter;
        $this->countryCollection = $countryCollection;
    }

    public function find(array $filter = [], $one = FALSE) {
        // If $one is TRUE then add limit to sql statement, or so...
        $rows = $this->adapter->find($sql, $bindings);

        // If $one is TRUE return a domain object, else a domain objects list.
        if ($one) {
            return $this->createCountry($row[0]);
        }

        return $this->createCountryCollection($rows);
    }

    public function save(CountryInterface $country) {
        if (NULL === $country->id) {
            // Build the INSERT statement and the bindings array...
            $this->adapter->insert($sql, $bindings);

            $lastInsertId = $this->adapter->getLastInsertId();

            return $this->find(['id' => $lastInsertId], true);
        }

        // Build the UPDATE statement and the bindings array...
        $this->adapter->update($sql, $bindings);

        return $this->find(['id' => $country->id], true);
    }

    public function delete(CountryInterface $country) {
        $sql = 'DELETE FROM countries WHERE id=:id';
        $bindings = [':id' => $country->id];

        $rowCount = $this->adapter->delete($sql, $bindings);

        return $rowCount > 0;
    }

    // Create a Country (domain object) from row.
    public function createCountry(array $row = []) {
        $country = new Country();

        /*
         * Iterate through the row items.
         * Assign a property to Country object for each item's name/value.
         */

        return $country;
    }

    // Create a Country[] list from rows list.
    public function createCountryCollection(array $rows) {
        /*
         * Iterate through rows.
         * Create a Country object for each row, with column names/values as properties.
         * Push Country object object to collection.
         * Return collection's content.
         */

        return $this->countryCollection->all();
    }

}
Run Code Online (Sandbox Code Playgroud)

Db适配器

class PdoAdapter implements AdapterInterface {

    private $connection;

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

    public function find(string $sql, array $bindings = [], int $fetchMode = PDO::FETCH_ASSOC, $fetchArgument = NULL, array $fetchConstructorArguments = []) {
        $statement = $this->connection->prepare($sql);
        $statement->execute($bindings);
        return $statement->fetchAll($fetchMode, $fetchArgument, $fetchConstructorArguments);
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)

域对象集合

class CountryCollection implements CountryCollectionInterface {

    private $countries = [];

    public function push(CountryInterface $country) {
        $this->countries[] = $country;
        return $this;
    }

    public function all() {
        return $this->countries;
    }

    public function getIterator() {
        return new ArrayIterator($this->countries);
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)

域对象

class Country implements CountryInterface {
    // Business logic: properties and methods...
}
Run Code Online (Sandbox Code Playgroud)