2626)]
2727final class MigrateAttendancesFastCommand extends Command
2828{
29- private const RESOURCE_TYPE_ID_ATTENDANCE = 10 ;
30- private const RESOURCE_TYPE_GROUP_ATTENDANCE = 10 ;
29+ public const RESOURCE_TYPE_ID_ATTENDANCE = 10 ;
30+ public const RESOURCE_TYPE_GROUP_ATTENDANCE = 10 ;
3131
3232 public function __construct (
3333 private readonly EntityManagerInterface $ em ,
@@ -72,11 +72,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7272 $ hasAttendanceTitle = $ this ->tableHasColumn ('c_attendance ' , 'title ' );
7373 $ hasAttendanceName = $ this ->tableHasColumn ('c_attendance ' , 'name ' );
7474
75+ // At this migration stage, c_attendance.session_id is expected to be removed.
76+ // We only rely on c_item_property.session_id when available.
77+ $ hasItemPropertySessionId = $ hasItemProperty && $ this ->tableHasColumn ('c_item_property ' , 'session_id ' );
78+
7579 if (!$ hasItemProperty && !$ hasAttendanceCId ) {
7680 $ io ->error ('Cannot determine attendance->course mapping: c_item_property does not exist and c_attendance.c_id does not exist. ' );
7781 return Command::FAILURE ;
7882 }
7983
84+ if ($ hasItemProperty && !$ hasItemPropertySessionId ) {
85+ $ io ->note ('c_item_property.session_id is not available. Session context will be stored as NULL in resource_link. ' );
86+ }
87+
8088 $ courseIds = $ this ->getCourseIdsToProcess ($ hasItemProperty , $ hasAttendanceCId );
8189
8290 if (0 === \count ($ courseIds )) {
@@ -126,7 +134,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
126134 hasItemProperty: $ hasItemProperty ,
127135 hasAttendanceCId: $ hasAttendanceCId ,
128136 hasAttendanceTitle: $ hasAttendanceTitle ,
129- hasAttendanceName: $ hasAttendanceName
137+ hasAttendanceName: $ hasAttendanceName ,
138+ hasItemPropertySessionId: $ hasItemPropertySessionId
130139 );
131140
132141 if (0 === \count ($ attendanceRows )) {
@@ -141,9 +150,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
141150 try {
142151 foreach ($ attendanceRows as $ row ) {
143152 $ attendanceId = (int ) $ row ['iid ' ];
144-
145153 $ attendanceTitle = $ this ->pickAttendanceTitle ($ row , $ attendanceId );
146154
155+ // session_id is read from c_item_property (when available).
156+ // Normalize 0 -> NULL as expected by resource_link.session_id.
147157 $ attendanceSessionId = isset ($ row ['session_id ' ]) ? (int ) $ row ['session_id ' ] : 0 ;
148158 $ attendanceSessionId = 0 === $ attendanceSessionId ? null : $ attendanceSessionId ;
149159
@@ -174,6 +184,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
174184 $ uuid = Uuid::v4 ();
175185 $ uuidValue = $ uuidIsBinary ? $ uuid ->toBinary () : $ uuid ->toRfc4122 ();
176186
187+ // Keep the slug stable and unique by appending the iid.
177188 $ slug = (string ) $ this ->slugger ->slug ($ attendanceTitle .'- ' .$ attendanceId )->lower ();
178189
179190 $ resourceNodeId = $ this ->insertResourceNode (
@@ -207,9 +218,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
207218 'user_id ' => $ toUserId ,
208219 ]);
209220
221+ // resource_node.path should be aligned with the course tree: <coursePath>-<nodeId>/
210222 $ newPath = $ coursePath .'- ' .$ resourceNodeId .'/ ' ;
211223 $ this ->connection ->update ('resource_node ' , ['path ' => $ newPath ], ['id ' => $ resourceNodeId ]);
212224
225+ // Mark attendance as migrated by storing the new resource_node_id.
213226 $ this ->connection ->update ('c_attendance ' , ['resource_node_id ' => $ resourceNodeId ], ['iid ' => $ attendanceId ]);
214227
215228 $ displayOrder ++;
@@ -267,14 +280,18 @@ private function fetchPendingAttendancesForCourse(
267280 bool $ hasItemProperty ,
268281 bool $ hasAttendanceCId ,
269282 bool $ hasAttendanceTitle ,
270- bool $ hasAttendanceName
283+ bool $ hasAttendanceName ,
284+ bool $ hasItemPropertySessionId
271285 ): array {
272286 $ selectTitle = $ hasAttendanceTitle ? 'a.title ' : 'NULL AS title ' ;
273287 $ selectName = $ hasAttendanceName ? 'a.name ' : 'NULL AS name ' ;
274288
289+ // session_id comes ONLY from c_item_property when available, otherwise NULL.
290+ $ selectSession = $ hasItemPropertySessionId ? 'ip.session_id ' : 'NULL ' ;
291+
275292 if ($ hasItemProperty ) {
276293 return $ this ->connection ->fetchAllAssociative (
277- "SELECT a.iid, {$ selectTitle }, {$ selectName }, a. session_id
294+ "SELECT a.iid, {$ selectTitle }, {$ selectName }, { $ selectSession } AS session_id
278295 FROM c_attendance a
279296 INNER JOIN c_item_property ip
280297 ON ip.tool = 'attendance'
@@ -286,10 +303,11 @@ private function fetchPendingAttendancesForCourse(
286303 );
287304 }
288305
289- // Fallback using legacy c_id
306+ // Fallback using legacy c_id (no c_item_property available).
307+ // At this stage, we cannot infer a session_id, so we store NULL.
290308 if ($ hasAttendanceCId ) {
291309 return $ this ->connection ->fetchAllAssociative (
292- "SELECT a.iid, {$ selectTitle }, {$ selectName }, a. session_id
310+ "SELECT a.iid, {$ selectTitle }, {$ selectName }, NULL AS session_id
293311 FROM c_attendance a
294312 WHERE a.c_id = :cid AND a.resource_node_id IS NULL
295313 ORDER BY a.iid " ,
0 commit comments