自己参照関係(それが呼ばれる隣接リスト)で相互に接続されたメニュー項目を保持する非常に単純なエンティティ(WpmMenu)がありますか?だから私の実体では私は持っています:
protected $id
protected $parent_id
protected $level
protected $name
すべてのゲッター/セッターとの関係は次のとおりです。
/**
* @ORM\OneToMany(targetEntity="WpmMenu", mappedBy="parent")
*/
protected $children;
/**
* @ORM\ManyToOne(targetEntity="WpmMenu", inversedBy="children", fetch="LAZY")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onUpdate="CASCADE", onDelete="CASCADE")
*/
protected $parent;
public function __construct() {
$this->children = new ArrayCollection();
}
そして、すべてが正常に動作します。メニューツリーをレンダリングするとき、リポジトリからルート要素を取得し、その子を取得してから、各子をループし、その子を取得して、各アイテムをレンダリングするまでこれを再帰的に実行します。
何が起こるか(そして私が解決策を探していること)はこれです:現在私は5つのレベル= 1のアイテムを持っており、これらのアイテムのそれぞれには3つのレベル= 2のアイテムが添付されています(そして将来的にはレベル=3のアイテムを使用します同じように)。メニューツリーのすべての要素を取得するには、Doctrineを実行します。
- ルート要素の1つのクエリ+
- ルート要素の5つの子(レベル= 1)を取得するための1つのクエリ+
- レベル1の各アイテムの3つの子(レベル= 2)を取得するための5つのクエリ+
- 各レベル2アイテムの子(レベル= 3)を取得するための15クエリ(5x3)
合計:22クエリ
したがって、これに対する解決策を見つける必要があります。理想的には、クエリを1つだけにします。
これが私がやろうとしていることです。 エンティティリポジトリ(WpmMenuRepository)で、queryBuilderを使用して、レベル順に並べられたすべてのメニュー項目のフラット配列を取得します。ルート要素(WpmMenu)を取得し、ロードされた要素の配列からその子を「手動で」追加します。次に、これを子供に対して再帰的に実行します。このようにすることで、同じツリーを1つのクエリで作成できます。
だからこれは私が持っているものです:
WpmMenuRepository:
public function setupTree() {
$qb = $this->createQueryBuilder("res");
/** @var Array */
$res = $qb->select("res")->orderBy('res.level', 'DESC')->addOrderBy('res.name','DESC')->getQuery()->getResult();
/** @var WpmMenu */
$treeRoot = array_pop($res);
$treeRoot->setupTreeFromFlatCollection($res);
return($treeRoot);
}
そして、私のWpmMenuエンティティには次のものがあります。
function setupTreeFromFlatCollection(Array $flattenedDoctrineCollection){
//ADDING IMMEDIATE CHILDREN
for ($i=count($flattenedDoctrineCollection)-1 ; $i>=0; $i--) {
/** @var WpmMenu */
$docRec = $flattenedDoctrineCollection[$i];
if (($docRec->getLevel()-1) == $this->getLevel()) {
if ($docRec->getParentId() == $this->getId()) {
$docRec->setParent($this);
$this->addChild($docRec);
array_splice($flattenedDoctrineCollection, $i, 1);
}
}
}
//CALLING CHILDREN RECURSIVELY TO ADD REST
foreach ($this->children as &$child) {
if ($child->getLevel() > 0) {
if (count($flattenedDoctrineCollection) > 0) {
$flattenedDoctrineCollection = $child->setupTreeFromFlatCollection($flattenedDoctrineCollection);
} else {
break;
}
}
}
return($flattenedDoctrineCollection);
}
そして、これが起こることです:
すべてがうまくいきますが、私は各メニュー項目が2回存在することになります。;)22のクエリの代わりに、現在23のクエリがあります。したがって、実際にケースを悪化させました。
実際に起こることは、「手動で」追加された子を追加した場合でも、WpmMenuエンティティはデータベースと同期しているとは見なされず、子に対してforeachループを実行するとすぐに、ORMで読み込みがトリガーされることです。すでに「手動で」追加されたものと同じ子をロードして追加します。
Q:この動作をブロック/無効にして、これらのエンティティにデータベースと同期していることを通知する方法はありますか?追加のクエリは必要ありませんか?