Хранилище данных и картографическая связь

У меня есть контроллер, который получает данные для передачи в представление. В это вводится (через контейнер pimple) сервис, который использует ряд моделей домена + бизнес-логику для создания данных.

Сам сервис имеет встроенный класс «хранилище», в котором есть методы для создания преобразователей данных и возврата экземпляра модели домена.

Я знаю, что, возможно, я не понял бы концепцию хранилища как Мартин Фаулер помещает это, чтобы «построить другой слой абстракции по слою отображения» & «Репозиторий является посредником между доменом и слоями отображения данных, действуя как коллекция объектов домена в памяти». Так что я могу использовать этот термин по ошибке.

оказание услуг:

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
}
}

репозиторий:

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
}

картограф:

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

Как вы можете видеть, картографы тесно связаны с классом репозитория, однако я не вижу способа обойти это.

Мне было интересно, есть ли способ реализовать этот репозиторий, который обеспечивает более слабую связь с классами отображения данных?

По большому счету, это приложение довольно маленькое, и поэтому необходимость обновления кода в обоих случаях не будет катастрофической, но вы никогда не будете, когда ситуация будет расти!

0

Решение

  • Операции с БД должны выполняться через адаптеры (MySqliAdapter, PdoAdapter и т. Д.). Таким образом, соединения БД вводятся в адаптеры, а не в преобразователи. И, конечно, не в хранилищах, потому что тогда цель абстракции хранилищ будет бессмысленной.
  • Сопоставитель получает адаптер (ы) в качестве зависимостей и может получать другие сопоставители.
  • Картографы передаются в качестве зависимостей в репозитории.
  • Имя репозитория семантически связано с именами уровня домена, а не с именами уровня обслуживания. Например: «InputService»: хорошо. «InputRepository»: неверно. «CountryRepository»: правильно.
  • Сервис может получить больше репозиториев. Или картографы, если вы не хотите применять дополнительный слой хранилищ.
  • В коде единственной тесно связанной структурой является объект Country (объект или объект домена) — динамически создаваемый для каждой выбранной строки таблицы. Даже этого можно избежать, используя фабрику доменных объектов, но я лично не вижу в этом необходимости.

П.С .: Извините, что не предоставил более документированный код.

обслуживание

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
}

}

вместилище

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

}

Картограф данных

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

}

Дб адаптер

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

//...
}

Коллекция доменных объектов

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

//...
}

Доменный объект

class Country implements CountryInterface {
// Business logic: properties and methods...
}
5

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

Вы можете добавить имена классов ИЛИ экземпляров в конструктор:

class InputRepository
{
private $db;
protected $mappers = array();

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

public function getMapper($key) {
if (!isset($this->mappers[$key]) {
throw new Exception('Invalid mapper "'. $key .'"');
}

if (!$this->mappers[$key] instanceof MapperInterface) {
$this->mappers[$key] = new $this->mappers[$key]($this->db);
}

return $this->mappers[$key];
}

public function getCountry()
{
$mapper = $this->getMapper('country');
$country = $mapper->fetch();
return $country; //returns country object
}
// lots of other methods for returning different model objects
}

Вы, вероятно, захотите сделать проверку интерфейса немного более надежной, очевидно.

1

По вопросам рекламы [email protected]