Skip to content

Commit bb9c09b

Browse files
committed
Optimize position ordering
1 parent 813dccd commit bb9c09b

1 file changed

Lines changed: 39 additions & 41 deletions

File tree

src/Manager/ClosureManager.php

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -102,58 +102,56 @@ public function moveItem(ItemInterface $item, ItemInterface $newParent = null, i
102102
// Update position of the moved item
103103
$item->setPosition($position);
104104

105-
// Get all siblings (items with same parent) to adjust their positions
106-
$siblings = $this->getSiblings($item, $newParent);
107-
108-
// Reorder siblings to make room for the moved item at the specified position
109-
$currentPos = 0;
110-
foreach ($siblings as $sibling) {
111-
if ($sibling === $item) {
112-
continue; // Skip the item being moved
113-
}
114-
115-
if ($currentPos === $position) {
116-
++$currentPos; // Skip the position where we're inserting the moved item
117-
}
118-
119-
$sibling->setPosition($currentPos);
120-
++$currentPos;
121-
}
105+
// Optimize: Only update positions for affected siblings using bulk SQL update
106+
// This is much faster than loading all siblings into memory
107+
$this->updateSiblingPositions($item, $newParent, $position);
122108

123109
$manager->flush();
124110
}
125111

126112
/**
127-
* Get all sibling items (items with the same parent)
128-
*
129-
* @return ItemInterface[]
113+
* Update sibling positions efficiently using bulk SQL update
114+
* Only updates items at or after the target position
130115
*/
131-
private function getSiblings(ItemInterface $item, ?ItemInterface $parent): array
132-
{
116+
private function updateSiblingPositions(
117+
ItemInterface $item,
118+
?ItemInterface $newParent,
119+
int $position,
120+
): void {
133121
$navigation = $item->getNavigation();
134122
if (null === $navigation) {
135-
return [];
136-
}
137-
138-
if (null === $parent) {
139-
// Get root items for this navigation
140-
return $this->closureRepository->findRootItems($navigation);
123+
return;
141124
}
142125

143-
// Get direct children of the parent
144-
$childClosures = $this->closureRepository->findBy([
145-
'ancestor' => $parent,
146-
'depth' => 1,
147-
]);
148-
149-
$siblings = [];
150-
foreach ($childClosures as $closure) {
151-
$descendant = $closure->getDescendant();
152-
if ($descendant !== null) {
153-
$siblings[] = $descendant;
154-
}
126+
$manager = $this->getManager($item);
127+
$qb = $manager->createQueryBuilder();
128+
$qb->update(ItemInterface::class, 'i')
129+
->set('i.position', 'i.position + 1')
130+
->where('i.navigation = :navigation')
131+
->andWhere('i.position >= :position')
132+
->andWhere('i.id != :itemId')
133+
->setParameter('navigation', $navigation)
134+
->setParameter('position', $position)
135+
->setParameter('itemId', $item->getId());
136+
137+
// Add parent constraint
138+
if (null === $newParent) {
139+
// Root items: items that don't have parent closure relationships (depth > 0)
140+
$qb->andWhere('NOT EXISTS (
141+
SELECT 1 FROM ' . $this->closureRepository->getClassName() . ' c
142+
WHERE c.descendant = i.id AND c.depth > 0
143+
)');
144+
} else {
145+
// Children of specific parent: items with closure relationship to parent at depth 1
146+
$qb->andWhere('EXISTS (
147+
SELECT 1 FROM ' . $this->closureRepository->getClassName() . ' c
148+
WHERE c.descendant = i.id
149+
AND c.ancestor = :parentId
150+
AND c.depth = 1
151+
)')
152+
->setParameter('parentId', $newParent->getId());
155153
}
156154

157-
return $siblings;
155+
$qb->getQuery()->execute();
158156
}
159157
}

0 commit comments

Comments
 (0)