rev_for_update( $course_id, $actor ); if ( '' !== $rev && ! hash_equals( $current, $rev ) ) { $found = $current; return array(); } $placed = $curriculum->add_units( $course_id, $unit_ids, $section_id > 0 ? $section_id : null, $actor ); self::move_to( $course_id, array_values( $placed ), $section_id, $position, $actor ); return $placed; } ); $stale = $found; return $out; } private static function move_to( int $course_id, array $item_ids, int $section_id, int $position, Actor $actor ): void { if ( array() === $item_ids ) { return; } $repo = Services::curriculum_repo(); $repo->forget(); $placements = $repo->placements_of_course( $course_id ); $sections = array_map( static fn( $s ): int => $s->id, $repo->sections_of_courses( array( $course_id ) )[ $course_id ] ?? array() ); $moving = array_flip( $item_ids ); $others = array(); $here = array(); $by_section = array(); foreach ( $placements as $p ) { $sid = (int) $p->item->section_id; if ( $sid === $section_id && isset( $moving[ $p->item->id ] ) ) { $here[ $p->item->id ] = true; continue; } $by_section[ $sid ][] = $p->item->id; if ( $sid === $section_id ) { $others[] = $p->item->id; } } $position = max( 0, min( $position, count( $others ) ) ); if ( count( $others ) === $position ) { return; } $ordered = array_values( array_filter( $item_ids, static fn( int $id ): bool => isset( $here[ $id ] ) ) ); $by_section[ $section_id ] = array_merge( array_slice( $others, 0, $position ), $ordered, array_slice( $others, $position ) ); $entries = array(); foreach ( array_merge( array( 0 ), $sections ) as $sid ) { foreach ( $by_section[ $sid ] ?? array() as $id ) { $entries[] = array( 'item_id' => $id, 'section_id' => 0 === $sid ? null : $sid, ); } } if ( count( $entries ) !== count( $placements ) ) { DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'position' ) ); } Services::curriculum()->apply_layout( $course_id, new CurriculumLayout( $sections, $entries ), $actor ); } }