Создание сложных моделей с использованием гидраторов: агрегат / стратегии

Ранее я создавал сложные модели (объект, содержащий много других типов моделей), используя сервисный слой с различными преобразователями, передаваемыми в качестве конструктора.

например.

class UserService{
    public function __construct(UserMapper $userMapper, AddressMapper $addressMapper, AppointmentsMapper $appointmentsMapper){}
    public function loadById($id) : User {
        $user = $this->userMapper->find($id);
        $appointments = $this->appointmentsMapper->findByUser($user);
        $user->setAppointments($appointments);
        $address = $this->addressMapper->findByUser($user);
        $user->setAddress($address);
        //..etc..
    }
}

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

Прочитав очень интересную статью о MaltBlue о гидраторах агрегатов, я попытался применить этот подход, чтобы упростить процесс создания объектов. Мне нравится идея создания HydratingResulset с RowObjectPrototype, установленным для объекта, который будет возвращен.

Я думаю, что мне нужно несколько советов о том, как сделать эту работу в реальном мире. Например, при использовании AggregateHydrator я могу загрузить историю встреч пользователей на основе идентификатора пользователя, переданного в гидратор.

class UserModelHydratorFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator) {
        $serviceManager = $serviceLocator->getServiceLocator();

        /**
         * Core hydration
         */
        $arrayHydrator = new ArraySerializable();
        $arrayHydrator->addStrategy('dateRegistered', new DateTimeStrategy());

        $aggregateHydrator = new AggregateHydrator();
        $aggregateHydrator->add($arrayHydrator);
        $aggregateHydrator->add($serviceLocator->get('Hydrator\Address'));
        $aggregateHydrator->add($serviceLocator->get('Hydrator\Appointments'));
        return $aggregateHydrator;
    }
}

... например, с гидратором пользовательских адресов, который выглядит следующим образом:

class UserAddressHydrator implements HydratorInterface{
    protected $locationMapper;

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

    public function hydrate(array $data, $object){
        if(!$object instanceof User){
            return;
        }

        if(array_key_exists('userId', $data)){
            $object->setAddress($this->locationMapper->findByClientId($data['userId']));
        }
        return $object;
    }
}

Это работает отлично. Несмотря на использование подхода AggregateHydrator, это означает, что для каждого объекта, имеющего в качестве свойства Address, требуется собственный гидратор. Таким образом, другой (почти идентичный) гидратор понадобился бы, если бы я строил модель компании, у которой также был адрес, поскольку вышеупомянутый гидратор жестко запрограммирован для заполнения модели пользователя (и ожидает данные, содержащие ключ userId). Это означает, что для каждого отношения / взаимодействия (has-a) потребуется собственный гидратор, чтобы генерировать это. Это нормально? Поэтому мне понадобится UserAddressHydrator, CompanyAddressHydrator, SupplierAddressHydrator, AppointmentAddressHydrator - все почти идентично приведенному выше коду, просто заполняя другой объект?

Было бы намного чище иметь один AddressHydrator, который принимает addressId и возвращает модель Address. Это заставило меня взглянуть на стратегии гидратора, которые, кажется, идеальны, хотя они работают только с одним значением, поэтому не могут, например, просмотреть входящий массив, чтобы увидеть, существует ли ключ идентификации pk/fk / и загрузить на основе этого,

Я был бы признателен за разъяснение этого подхода, мне кажется, что я заблудился на этом пути.

1 ответ

Вы абсолютно правы. Стратегии Hydrator работают только с одним значением, которое соответствует члену вашей организации. Таким образом, вы должны добавить несколько стратегий для вашего гидратора. С другой стороны, вы можете наследовать от \Zend\Hydrator\AbstractHydrator и перезаписать addStrategy() метод для обработки массивов с более чем одним именем. С этим решением вы можете установить тот же Hydrator на значения массива, к которому вы добавляете addStrategy(),

Простая Гидраторская Стратегия

В этом примере показано использование простой стратегии гидратора.

class UserHydratorFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $oServiceLocator)
    {
        $oHydrator = (new ClassMethods(false))
            ->addStrategy('address', new AddressHydratorStrategy())
            ->addStrategy('company', new AddressHydratorStrategy());

        return $oHydrator;
    }
}

Это пример фабрики гидраторов для объекта пользователя. На этом заводе нормальный ClassMethods гидратор называется. Этот тип гидратора предполагает использование методов получения и установки в вашей сущности для гидрирования членов сущности. Кроме того, добавлены стратегии для членов адреса и компании.

class AddressHydratorStrategy extends DefaultStrategy
{
    public function hydrate($aData)
    {
        return (new ClassMethods(false))
            ->hydrate($aData, new AdressEntity())
    }
}

Стратегия просто позволяет добавлять виды дочерних объектов. Если в ваших данных есть адрес или ключ компании, к ним будет добавлен объект адреса.

class UserEntity
{
    protected $name;

    protected $address;

    protected $company;

    public function getName() : string
    {
        return $this->name;
    }

    public function setName(string $sName) : UserEntity
    {
        $this-> name = $sName;
        return $this;
    }

    public function getAddress() : AddressEntity
    {
        return $this->address;
    }

    public function setAddress(AddressEntity $oAddress) : UserEntity
    {
        $this->address = $oAddress;
        return $this;
    }

    public function getCompany() : AddressEntity
    {
        return $this->company;
    }

    public function setCompany(AddressEntity $oCompany) : UserEntity
    {
        $this->company = $oCompany;
        return $this; 
    }
}

Вы заметили подсказки типа? Например, setAddress метод занимает AddressEntity объект как параметр. Этот объект будет сгенерирован стратегией, которую мы добавили к ClassMethods увлажняющий.

Далее следует вызов гидратора с некоторыми данными, которые приведут к нестестовому комплексу. UserEntity объект.

$oUserHydrator = $this->getServiceLocator(UserHydrator::class);
$oUserHydrator->hydrate(
    [
        'name' => 'Marcel',
        'address' => 
        [
            'street' => 'bla',
            'zipcode' => 'blubb',
        ],
        'company' => 
        [
            'street' => 'yadda',
            'zipcode' => 'yadda 2',
        ],
    ], 
    new UserEntity()
);

Использование в результирующих наборах

Чтобы прояснить ситуацию, вот небольшой пример того, как я использую гидраторы и стратегии непосредственно в наборах результатов.

class UserTableGateway extends TableGateway
{
    public function __construct(Adapter $oAdapter, $oUserHydrator)
    {
        $oPrototype = new HydratingResultSet(
            $oHydrator,
            new UserEntity()
        );

        parent::__construct('user_table', $oAdapter, null, $oPrototype);
    }

    public function fetchUser()
    {
        // here a complex join sql query is fired
        // the resultset is of the hydrated prototype
    }
}

В этом примере TableGateway класс инициализируется с прототипом, который является HydratingResultSet, Он использует гидратор от UserHydratorFactory, Стратегии Hydrator имеют смысл, когда сложные данные напрямую выбираются из базы данных или другого источника, такого как веб-сервис, который возвращает вложенные данные.

Заключение

Для меня лично работа с гидраторными стратегиями имеет больше смысла, чем работа с агрегатным гидратором. Конечно, вы можете добавить в стратегию не более одного имени. В противном случае вы можете, как я сказал в начале, перезаписать addStrategy Метод в унаследованном классе в соответствии с вашими требованиями. Стратегии гидратора в моих глазах менее кодирующие.

Другие вопросы по тегам