@@ -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