@@ -156,20 +156,27 @@ public function indexJson(
156156 EntityManagerInterface $ em ,
157157 AssetRepository $ assetRepository
158158 ): Response {
159- $ requestData = json_decode ($ request ->getContent (), true );
160- // Sort behaviour
161- if (!empty ($ requestData ) && isset ($ requestData ['toolItem ' ])) {
162- $ index = $ requestData ['index ' ];
163- $ toolItem = $ requestData ['toolItem ' ];
164- $ toolId = (int ) $ toolItem ['iid ' ];
165-
166- /** @var CTool $cTool */
167- $ cTool = $ em ->find (CTool::class, $ toolId );
168-
169- if ($ cTool ) {
170- $ cTool ->setPosition ($ index + 1 );
171- $ em ->persist ($ cTool );
172- $ em ->flush ();
159+ // Handle drag & drop sort for course tools
160+ if ($ request ->isMethod ('POST ' )) {
161+ $ requestData = json_decode ($ request ->getContent () ?: '' , true ) ?? [];
162+
163+ if (isset ($ requestData ['toolId ' ], $ requestData ['index ' ])) {
164+ $ course = $ this ->getCourse ();
165+ if (null === $ course ) {
166+ return $ this ->json (
167+ ['success ' => false , 'message ' => 'Course not found. ' ],
168+ Response::HTTP_BAD_REQUEST
169+ );
170+ }
171+
172+ $ sessionId = $ this ->getSessionId ();
173+ $ toolId = (int ) $ requestData ['toolId ' ];
174+ $ newIndex = (int ) $ requestData ['index ' ];
175+
176+ $ result = $ this ->reorderCourseTools ($ em , $ course , $ sessionId , $ toolId , $ newIndex );
177+ $ statusCode = $ result ['success ' ] ? Response::HTTP_OK : Response::HTTP_BAD_REQUEST ;
178+
179+ return $ this ->json ($ result , $ statusCode );
173180 }
174181 }
175182
@@ -1317,4 +1324,103 @@ private function isUserEnrolledInAnySession(User $user, EntityManagerInterface $
13171324
13181325 return $ enrollmentCount > 0 ;
13191326 }
1327+
1328+ /**
1329+ * Reorders all course tools for a given course / session after drag & drop.
1330+ *
1331+ * @return array<string, mixed>
1332+ */
1333+ private function reorderCourseTools (
1334+ EntityManagerInterface $ em ,
1335+ Course $ course ,
1336+ int $ sessionId ,
1337+ int $ toolId ,
1338+ int $ newIndex
1339+ ): array {
1340+ if ($ toolId <= 0 ) {
1341+ return [
1342+ 'success ' => false ,
1343+ 'message ' => 'Invalid tool id. ' ,
1344+ ];
1345+ }
1346+
1347+ /** @var CToolRepository $toolRepo */
1348+ $ toolRepo = $ em ->getRepository (CTool::class);
1349+
1350+ // Load all tools for this course + (optional) session ordered by current position
1351+ $ qb = $ toolRepo ->createQueryBuilder ('t ' )
1352+ ->andWhere ('t.course = :course ' )
1353+ ->setParameter ('course ' , $ course )
1354+ ->orderBy ('t.position ' , 'ASC ' )
1355+ ->addOrderBy ('t.iid ' , 'ASC ' );
1356+
1357+ if ($ sessionId > 0 ) {
1358+ $ qb
1359+ ->andWhere ('IDENTITY(t.session) = :sessionId ' )
1360+ ->setParameter ('sessionId ' , $ sessionId );
1361+ } else {
1362+ $ qb ->andWhere ('t.session IS NULL ' );
1363+ }
1364+
1365+ /** @var CTool[] $tools */
1366+ $ tools = $ qb ->getQuery ()->getResult ();
1367+
1368+ if (0 === \count ($ tools )) {
1369+ return [
1370+ 'success ' => false ,
1371+ 'message ' => 'No tools found for course / session. ' ,
1372+ ];
1373+ }
1374+
1375+ // Build an array of IDs to manipulate positions easily
1376+ $ ids = array_map (static fn (CTool $ tool ): int => $ tool ->getIid () ?? 0 , $ tools );
1377+
1378+ $ currentIndex = array_search ($ toolId , $ ids , true );
1379+ if (false === $ currentIndex ) {
1380+ return [
1381+ 'success ' => false ,
1382+ 'message ' => 'Tool not found in current course / session. ' ,
1383+ ];
1384+ }
1385+
1386+ // Clamp the new index into a valid range
1387+ $ newIndex = max (0 , min ($ newIndex , \count ($ tools ) - 1 ));
1388+
1389+ if ($ newIndex === $ currentIndex ) {
1390+ return [
1391+ 'success ' => true ,
1392+ 'unchanged ' => true ,
1393+ 'from ' => $ currentIndex ,
1394+ 'to ' => $ newIndex ,
1395+ 'total ' => \count ($ tools ),
1396+ ];
1397+ }
1398+
1399+ // Move the ID in the array (remove at old index, insert at new index)
1400+ $ idToMove = $ ids [$ currentIndex ];
1401+ array_splice ($ ids , $ currentIndex , 1 );
1402+ array_splice ($ ids , $ newIndex , 0 , [$ idToMove ]);
1403+
1404+ // Rewrite all positions in DB using a simple DQL UPDATE per tool
1405+ // Positions will be 0-based: 0,1,2,...
1406+ foreach ($ ids as $ pos => $ id ) {
1407+ $ em ->createQueryBuilder ()
1408+ ->update (CTool::class, 't ' )
1409+ ->set ('t.position ' , ':pos ' )
1410+ ->where ('t.iid = :iid ' )
1411+ ->setParameter ('pos ' , $ pos )
1412+ ->setParameter ('iid ' , $ id )
1413+ ->getQuery ()
1414+ ->execute ();
1415+ }
1416+
1417+ return [
1418+ 'success ' => true ,
1419+ 'from ' => $ currentIndex ,
1420+ 'to ' => $ newIndex ,
1421+ 'total ' => \count ($ tools ),
1422+ 'courseId ' => $ course ->getId (),
1423+ 'sessionId ' => $ sessionId ,
1424+ ];
1425+ }
13201426}
0 commit comments