在Pomm项目的水合模型的树结构

时间:2015-07-21 13:30:44

标签: php postgresql pomm

我有简单的数据库表,它通过 parent_id 属性实现树结构。像这样:

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

我想获取具有树结构的PHP对象。因此,对象类别将具有属性子类别,它将是类别对象的列表,依此类推。我希望通过Pomm的递归sql查询直接从PostgreSQL数据库获取此对象。目标不是遍历获取的数据并在PHP中构建此类对象。我想直接过程PostreSQL - > Pomm - >对象

至于现在,我只在第一级获得我想要的东西。因此,第一级别类别具有子类别,即类别实体列表。然而,下一级(深度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 个答案:

答案 0 :(得分: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的递归术语中不可能使用聚合函数。

另一个想法是将id上的结果索引为每个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)将返回子实体数组(或null),但这看起来更像是一个hack而不是真实的功能。

从另一端解决问题,似乎可以创建专门的灵活实体embed a nested set design pattern在内部递归儿童。