Сначала я прочитал следующие два вопроса stackoverflow, но они не дали мне ответа:
В моем приложении у меня есть таблица базы данных сотрудников, которая имеет множество свойств, но наиболее интересным для меня в настоящее время является manager_id
и bank_id
которые являются внешними ключами.
manager_id
является внешним ключом для другого сотрудника (как вы можете себе представить, у сотрудника может быть один менеджер)bank_id
является внешним ключом для другой модели / таблицы базы данных с именем bank
— потому что сотрудник может иметь банковский счет 😉Теперь в моем EmployeeTable.php
файл у меня есть те магические методы где я получаю результаты базы данных.
Чтобы получить одного сотрудника, я делаю это:
/**
* @param int $id
*
* @return Employee
*/
public function getEmployeeById($id)
{
$rowset = $this->tableGateway->select(['id' => (int) $id]);
/** @var Employee $row */
$row = $rowset->current();
if (!$row) {
throw new RuntimeException(sprintf(
'Could not find row with identifier %d',
(int) $id
));
}
return $row;
}
Но без каких-либо sql присоединяется У меня есть только manager_id и bank_id в возвращенном объекте сотрудника.
Вопрос: Как лучше всего получать необходимую информацию?
Пока у меня есть две мысли:
Первый
Должен ли я — если $row
не пусто — позвоните, например, bankTable
объект (через внедрение зависимости), который имеет getBankById
метод.
Тогда я продлю Employee.php
модель с $bank
свойство с геттером / сеттером и до return
утверждение в моем getEmployeeId
метод я бы сделал что-то вроде этого:
$row->setBank($this->bankTable->getBankById($row->bank_id));
Но я боюсь рекурсивного цикла, делающего это для manager_id
потому что я бы назвал тот же метод, в котором я сейчас нахожусь.
второй
Или я должен продлить getEmployeeById
метод с левым соединением, чтобы получить данные из банковской таблицы следующим образом:
$select = $this->tableGateway->getSql()->select()
->join(['b' => 'bank'], 'bank_id = m.id',
[
'bank.id' => 'id',
'bank.description' => 'description',
'bank.bic' => 'bic',
],
Select::JOIN_LEFT)
->join(['m' => 'employee'], 'manager_id = m.id',
[
'manager.id' => 'id',
'manager.forename' => 'forename',
'manager.surname' => 'surname',
// and all the other properties
],
Select::JOIN_LEFT);
$result = $this->tableGateway->selectWith($select);
$row= $result->current();
$resultSet = $this->hydrator->hydrate($this->hydrator->extract($row), $row);
К сожалению, я должен дать соединенные столбцы псевдонимы, иначе я бы перезаписал id
от сотрудника с идентификатором банка и т. д.
После такого вида SQL-выражения вы можете увидеть, что я извлеку результат, чтобы получить свойства как значения, а затем их гидрировать.
Гидратация будет выглядеть так:
/**
* @param array $data
* @param Employee $object
*
* @return Employee
*/
public function hydrate(array $data, $object)
{
if (!$object instanceof Employee) {
throw new \BadMethodCallException(sprintf(
'%s expects the provided $object to be a PHP Employee object)',
__METHOD__
));
}
$employee = new Employee();
$employee->exchangeArray($data);
$bank = new Bank();
$bank->exchangeArray($data, 'bank.');
$employee->setBank($bank);
$manager = new Employee();
$manager->exchangeArray($data, 'manager.');
$employee->setManager($manager);
return $employee;
}
В результате этого у меня есть чистый employee
модель (без этих дополнительных столбцов псевдонимов) и дополнительно 2 новых свойства, которые являются объектами другого сотрудника (менеджера) и банка.
Но это выглядит довольно перегруженным …
Спасибо за чтение до сих пор — Если у вас есть какие-либо советы или рекомендации, они тепло приветствуются!
РЕДАКТИРОВАТЬ
Я отредактировал мой EmployeeTableFactory
сделать следующее (об увлажнении):
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new HydratingResultSet();
$resultSetPrototype->setHydrator(new EmployeeHydrator());
$resultSetPrototype->setObjectPrototype(new Employee());
$tableGateway = new TableGateway('employee', $dbAdapter, null, $resultSetPrototype);
return new EmployeeTable($tableGateway);
}
Я изменил свой EmployeeHydrator
реализовать HydratorInterface потому что я уже использовал экстракт и теперь он соответствует необходимому интерфейсу для resultSetPrototype->setHydrator()
метод.
Теперь все становится довольно легко в getEmployeeById
метод, потому что с помощью следующего кода у меня уже есть готовый объект сотрудника и все связанные объекты внешнего ключа (из-за моего EmployeeHydrator
)
$result = $this->tableGateway->selectWith($select);
$row = $result->current(); // the given employee object result already hydrated!
return $row;
Мне нравится эта реализация
Задача ещё не решена.
Других решений пока нет …