Древовидная структура с гидратированной моделью в Pomm-проекте

У меня есть простая таблица базы данных, которая реализует древовидную структуру через parent_id приписывать. Что-то вроде этого:

+----+------+-----------+
| id | name | parent_id |
+----+------+-----------+
|  1 | test | null      |
+----+------+-----------+
|  2 | tes2 | 1         |
+----+------+-----------+
|  3 | tes3 | 2         |
+----+------+-----------+
|  4 | tst  | 2         |
+----+------+-----------+

Я хочу получить объект PHP с древовидной структурой. Таким образом, объект Category будет иметь подкатегории свойств, которые будут списком объектов Category и так далее. Я хочу получить этот объект непосредственно из базы данных PostgreSQL через рекурсивный SQL-запрос с Pomm. Цель не в том, чтобы перебрать полученные данные и построить такой объект в PHP. Я хочу прямой процесс PostreSQL -> Pomm -> Object.

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

Пока у меня есть это:

$sql = <<<SQL
with recursive
cat as (select c.* FROM :category c where parent_id is null),
subcat as (
select c.* from :category c join cat on c.parent_id=cat.id
union all
select c.* from :category c join subcat on c.parent_id=subcat.id
)
select :projection from cat cc, subcat
where cc.id=subcat.parent_id
group by :group_fields
SQL;

$projection = $this->createProjection()
->setField('subcategories', 'array_agg(subcat)', 'public.category[]');
$sql = strtr($sql, [
':category' => $this->structure->getRelation(),
':projection' => $projection->formatFieldsWithFieldAlias('cc'),
':group_fields' => $this->createProjection()->formatFields('cc'),
]);

Мой вопрос, если это возможно с Pomm и если да, то как?

1

Решение

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

$iterator = $this->query($sql, $array_of_parameters);

foreach ($iterator as $entity) {
$entity->getParentId();
}

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

Тривиальная идея состоит в том, чтобы получить один результат, содержащий все результаты как вложенные объекты:

with recursive
cat as (
select * from test_tree tt where not exists (select parent_id from test_tree tt2 where tt2.parent_id = tt.id)
union all
select tt.*, array_agg(child) from test_tree tt join cat child on tt.id = child.parent_id group by tt.id
)
select * from cat

Но, к сожалению, невозможно использовать агрегатные функции в рекурсивном члене CTE.

Другая идея заключалась бы в том, чтобы индексировать результаты по идентификатору, дающему потомков для каждого parent_id, и использовать эти итераторы Pomm с возможностью прокрутки для их получения:

with
tree as (
select
tt.id,
array_agg(child) as children
from
test_tree tt
join lateral (select * from test_tree tt2 where tt2.parent_id = tt.id) child on (true) group by tt.id
)
select
idx as id,
tree.children
from
generate_series(1, (select max(id) from test_tree)) idx
left join tree on tree.id = idx

какие выводы:

┌────┬─────────────────────────────────────────┐
│ id │                children                 │
├────┼─────────────────────────────────────────┤
│  1 │ {"(2,\"test 2\",1)","(3,\"test 3\",1)"} │
│  2 │ {"(4,\"test 4\",2)","(5,\"test 5\",2)"} │
│  3 │ {"(6,\"test 6\",3)"}                    │
│  4 │ ¤                                       │
│  5 │ ¤                                       │
│  6 │ {"(7,\"test 7\",6)"}                    │
│  7 │ ¤                                       │
└────┴─────────────────────────────────────────┘
(7 rows)

Тогда набор результатов будет упорядочен по parent_id так $iterator->get($parent_id) вернул бы массив дочерних объектов (или ноль), но это больше похоже на хак, чем на реальную функцию.

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

1

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

Других решений пока нет …

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