From b4e6ac03c53c2e4c3d5d3be57f790d141f571abf Mon Sep 17 00:00:00 2001 From: root Date: Thu, 4 Jun 2026 02:41:08 -0400 Subject: [PATCH] remove codeigniter --- Dockerfile | 2 +- app/Config/Database.php | 13 - app/Config/Services.php | 24 -- app/Config/SessionTimeout.php | 10 - app/Config/progress.php | 34 --- .../{ci_helpers.php => app_helpers.php} | 42 +-- .../AttendanceCommentTemplateController.php | 10 - .../Controllers/Api/Auth/AuthController.php | 2 +- .../Api/Auth/AuthSessionController.php | 22 +- .../Api/Auth/RegisterController.php | 6 +- .../Api/Auth/SessionTimeoutController.php | 24 +- .../Api/BadgeScan/BadgeScanController.php | 4 +- .../Controllers/Api/BaseApiController.php | 272 +----------------- app/Http/Controllers/Api/CiRequestAdapter.php | 70 ----- .../Api/Core/BaseApiController.php | 2 - .../Controllers/Api/Core/CiRequestAdapter.php | 71 ----- app/Http/Controllers/Api/StatsController.php | 13 +- .../Api/System/AccessDeniedController.php | 2 +- .../Api/System/HealthController.php | 2 +- .../Api/System/StatsController.php | 13 +- .../Api/Utilities/ProofreadController.php | 2 +- .../Middleware/EnsureBadgeScanLogsAccess.php | 2 +- ...EnsureClassProgressTeacherPortalAccess.php | 2 +- .../Middleware/EnsureParentProgressAccess.php | 2 +- .../EnsurePrintRequestsAdminAccess.php | 2 +- .../EnsurePrintRequestsTeacherAccess.php | 2 +- .../Middleware/TeacherPortalAuthenticate.php | 2 +- .../Admin/SaveCompetitionScoresRequest.php | 2 +- .../Admin/StoreCompetitionWinnerRequest.php | 2 +- .../Admin/UpdateCompetitionWinnerRequest.php | 2 +- app/Models/AdditionalCharge.php | 4 +- app/Models/AdminNotificationSubject.php | 2 +- app/Models/AttendanceCommentTemplate.php | 4 +- app/Models/AttendanceData.php | 2 +- app/Models/AttendanceDay.php | 6 +- app/Models/AttendanceRecord.php | 6 +- app/Models/AttendanceTracking.php | 6 +- app/Models/BadgePrintLog.php | 4 +- app/Models/CalendarEvent.php | 6 +- app/Models/CiDatabase.php | 82 ------ app/Models/CiModelBuilder.php | 105 ------- app/Models/CiQueryBuilder.php | 153 ---------- app/Models/CiQueryResult.php | 56 ---- app/Models/ClassPrepAdjustment.php | 2 +- app/Models/ClassPreparationLog.php | 2 +- app/Models/ClassProgressAttachment.php | 2 +- app/Models/ClassProgressReport.php | 2 +- app/Models/ClassSection.php | 8 +- app/Models/CommunicationLog.php | 2 +- app/Models/Competition.php | 4 +- app/Models/CompetitionClassWinner.php | 2 +- app/Models/CompetitionScore.php | 2 +- app/Models/CompetitionWinner.php | 2 +- app/Models/Configuration.php | 6 +- app/Models/ContactUs.php | 4 +- app/Models/CurrentFlag.php | 2 +- app/Models/DiscountUsage.php | 4 +- app/Models/DiscountVoucher.php | 8 +- app/Models/EarlyDismissalSignature.php | 4 +- app/Models/EmailTemplate.php | 6 +- app/Models/EmergencyContact.php | 6 +- app/Models/Enrollment.php | 4 +- app/Models/Event.php | 4 +- app/Models/EventCharges.php | 4 +- app/Models/Exam.php | 2 +- app/Models/ExamDraft.php | 4 +- app/Models/Expense.php | 2 +- app/Models/Family.php | 2 +- app/Models/FamilyCommPref.php | 2 +- app/Models/FamilyGuardian.php | 2 +- app/Models/FamilyStudent.php | 4 +- app/Models/FinalExam.php | 4 +- app/Models/FinalScore.php | 4 +- app/Models/GradingLock.php | 6 +- app/Models/Homework.php | 4 +- app/Models/InventoryCategory.php | 4 +- app/Models/InventoryItem.php | 2 +- app/Models/InventoryMovement.php | 2 +- app/Models/Invoice.php | 6 +- app/Models/InvoiceEvent.php | 2 +- app/Models/InvoiceStudentList.php | 2 +- app/Models/IpAttempt.php | 8 +- app/Models/LateSlipLog.php | 2 +- app/Models/LoginActivity.php | 4 +- app/Models/ManualPayment.php | 2 +- app/Models/Message.php | 4 +- app/Models/MidtermExam.php | 4 +- app/Models/MissingScoreOverride.php | 8 +- app/Models/NavItem.php | 6 +- app/Models/Notification.php | 12 +- app/Models/ParentAttendanceReport.php | 4 +- app/Models/ParentMeetingSchedule.php | 2 +- app/Models/ParentModel.php | 2 +- app/Models/ParentNotification.php | 6 +- app/Models/Participation.php | 4 +- app/Models/PasswordReset.php | 2 +- app/Models/PasswordResetRequest.php | 2 +- app/Models/PayPalPayment.php | 2 +- app/Models/Payment.php | 6 +- app/Models/PaymentError.php | 2 +- app/Models/PaymentNotificationLog.php | 6 +- app/Models/PaymentTransaction.php | 4 +- app/Models/PaypalTransaction.php | 2 +- app/Models/Permission.php | 6 +- app/Models/PlacementBatch.php | 2 +- app/Models/PlacementLevel.php | 2 +- app/Models/PlacementScore.php | 2 +- app/Models/Preferences.php | 2 +- app/Models/PrintRequest.php | 2 +- app/Models/Project.php | 6 +- app/Models/PromotionQueue.php | 2 +- app/Models/PurchaseOrder.php | 2 +- app/Models/PurchaseOrderItem.php | 2 +- app/Models/Refund.php | 6 +- app/Models/Role.php | 4 +- app/Models/RolePermission.php | 8 +- app/Models/SchoolClass.php | 4 +- app/Models/ScoreComment.php | 2 +- app/Models/Section.php | 8 +- app/Models/SemesterScore.php | 2 +- app/Models/Setting.php | 4 +- app/Models/Staff.php | 8 +- app/Models/StaffAttendance.php | 2 +- app/Models/Stats.php | 2 +- app/Models/Student.php | 32 +-- app/Models/StudentAllergy.php | 10 +- app/Models/StudentClass.php | 8 +- app/Models/StudentMedicalCondition.php | 10 +- app/Models/SubjectCurriculum.php | 4 +- app/Models/TeacherClass.php | 30 +- .../TeacherSubmissionNotificationHistory.php | 2 +- app/Models/User.php | 36 +-- app/Models/UserNotification.php | 2 +- app/Models/UserRole.php | 12 +- app/Models/WhatsappGroupLink.php | 4 +- app/Models/WhatsappGroupMembership.php | 8 +- app/Models/WhatsappInviteLog.php | 8 +- .../CompetitionWinnersAdminService.php | 4 +- .../CompetitionWinnersDomain.php | 2 +- .../Attendance/StaffAttendanceService.php | 4 +- .../AttendanceTrackingService.php | 4 +- app/Services/Auth/ApiLoginSecurityService.php | 11 +- ...Service.php => ApiRegistrationService.php} | 7 +- app/Services/Auth/AuthSessionService.php | 16 +- app/Services/BadgeScan/BadgeScanService.php | 4 +- .../ClassProgressMetaService.php | 8 +- .../ClassProgressMutationService.php | 2 +- .../ClassProgressQueryService.php | 2 +- .../ClassProgressRuleService.php | 8 +- .../Discounts/DiscountApplyService.php | 4 +- app/Services/Docs/ApiDocsService.php | 2 +- app/Services/Docs/DocsCatalogService.php | 4 +- .../ExtraChargesChargeService.php | 4 +- .../AuthorizedUsersManagementService.php | 8 +- .../Parents/ParentAttendanceReportService.php | 8 +- .../Parents/ParentProgressQueryService.php | 10 +- .../Parents/ParentReportCardService.php | 8 +- .../Parents/PrimaryParentUserResolver.php | 2 +- .../Payments/PaymentManualService.php | 4 +- .../PrintRequestsPortalService.php | 8 +- .../PublicWinnersPortalService.php | 2 +- .../Students/StudentScoreCardService.php | 4 +- app/Support/SessionTimeoutConfig.php | 10 + composer.json | 4 +- config/docs.php | 4 +- config/jwt.php | 2 +- docs/api_base_api_controller_plan.md | 7 +- docs/app_project_plan.md | 26 +- docs/app_project_plan_hardened.md | 28 +- docs/controller_plan.md | 2 +- ...app_project_plan_global_modular_islamic.md | 28 +- resources/docs/controllers.md | 24 +- resources/docs/openapi.json | 6 +- routes/api.php | 14 +- tests/Unit/ApiLoginSecurityServiceTest.php | 10 +- .../Attendance/AttendanceTrackingService.php | 2 +- tools/ci-controller-to-laravel-mapping.csv | 106 ------- tools/generate-ci-controller-mapping.php | 264 ----------------- tools/sync_fillable_from_codeigniter.mjs | 169 ----------- tools/sync_fillable_from_codeigniter.php | 231 --------------- 180 files changed, 457 insertions(+), 2186 deletions(-) delete mode 100644 app/Config/Database.php delete mode 100644 app/Config/Services.php delete mode 100644 app/Config/SessionTimeout.php delete mode 100644 app/Config/progress.php rename app/Helpers/{ci_helpers.php => app_helpers.php} (70%) delete mode 100644 app/Http/Controllers/Api/CiRequestAdapter.php delete mode 100644 app/Http/Controllers/Api/Core/CiRequestAdapter.php delete mode 100644 app/Models/CiDatabase.php delete mode 100644 app/Models/CiModelBuilder.php delete mode 100644 app/Models/CiQueryBuilder.php delete mode 100644 app/Models/CiQueryResult.php rename app/Services/Auth/{CodeIgniterApiRegistrationService.php => ApiRegistrationService.php} (95%) create mode 100644 app/Support/SessionTimeoutConfig.php delete mode 100644 tools/ci-controller-to-laravel-mapping.csv delete mode 100644 tools/generate-ci-controller-mapping.php delete mode 100644 tools/sync_fillable_from_codeigniter.mjs delete mode 100644 tools/sync_fillable_from_codeigniter.php diff --git a/Dockerfile b/Dockerfile index ac974654..d9cc7fac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM composer:2 AS vendor WORKDIR /app COPY composer.json composer.lock ./ -COPY app/Helpers/ci_helpers.php app/Helpers/ +COPY app/Helpers/app_helpers.php app/Helpers/ RUN composer install --no-dev --prefer-dist --no-interaction --no-progress --no-scripts --ignore-platform-req=ext-gd COPY . . RUN composer dump-autoload --optimize \ diff --git a/app/Config/Database.php b/app/Config/Database.php deleted file mode 100644 index 53431684..00000000 --- a/app/Config/Database.php +++ /dev/null @@ -1,13 +0,0 @@ - [ - 'on_track' => 'On track', - 'slightly_behind' => 'Slightly behind', - 'behind' => 'Behind', - ], - - /* - |-------------------------------------------------------------------------- - | Subject Sections Mapping - |-------------------------------------------------------------------------- - | Replace with your exact CI ClassProgressController::SUBJECT_SECTIONS values - |-------------------------------------------------------------------------- - */ - 'subject_sections' => [ - 'islamic' => [ - 'label' => 'Islamic Studies', - 'db_subject' => 'Islamic Studies', - ], - 'quran' => [ - 'label' => 'Quran/Arabic', - 'db_subject' => 'Quran/Arabic', - ], - ], - -]; diff --git a/app/Helpers/ci_helpers.php b/app/Helpers/app_helpers.php similarity index 70% rename from app/Helpers/ci_helpers.php rename to app/Helpers/app_helpers.php index f2efdbf1..7cd96b32 100644 --- a/app/Helpers/ci_helpers.php +++ b/app/Helpers/app_helpers.php @@ -2,30 +2,6 @@ use Carbon\CarbonImmutable; use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Facades\Log; - -if (!function_exists('log_message')) { - function log_message(string $level, string $message, array $context = []): void - { - $level = strtolower($level); - $logger = Log::channel(config('logging.default')); - $context = $context; - if (!empty($context)) { - $message = strtr($message, array_combine( - array_map(fn($k) => "{" . $k . "}", array_keys($context)), - array_map('strval', $context) - )); - } - $logger->{$level}($message); - } -} - -if (!function_exists('esc')) { - function esc($value): string - { - return e($value); - } -} if (!function_exists('utc_now')) { function utc_now(): string @@ -53,13 +29,6 @@ if (!function_exists('user_timezone')) { } } -if (!function_exists('model')) { - function model(string $class) - { - return app()->make($class); - } -} - if (!function_exists('pbkdf2_hash')) { function pbkdf2_hash(string $password, string $algo = 'sha256', int $iterations = 100000, int $length = 64): string { @@ -97,15 +66,8 @@ if (!function_exists('pbkdf2_verify')) { } } -if (!function_exists('legacy_password_verify')) { - function legacy_password_verify(string $password, ?string $storedHash): bool - { - return ci_password_verify($password, $storedHash); - } -} - -if (!function_exists('ci_password_verify')) { - function ci_password_verify(string $password, ?string $storedHash): bool +if (!function_exists('verify_stored_password')) { + function verify_stored_password(string $password, ?string $storedHash): bool { if (!$storedHash) { return false; diff --git a/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php b/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php index 019a8fcb..6ae9a5ad 100644 --- a/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php +++ b/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php @@ -17,9 +17,6 @@ class AttendanceCommentTemplateController extends Controller ) { } - /** - * Bootstrap URLs for admin UIs (legacy CodeIgniter parity + SPA). - */ public function bootstrapUrls(): JsonResponse { return response()->json([ @@ -133,10 +130,6 @@ class AttendanceCommentTemplateController extends Controller ]); } - /** - * CodeIgniter {@see \App\Controllers\View\AttendanceCommentTemplateController::save} - * POST fields: id?, min_score, max_score, template_text, is_active (checkbox "on"). - */ public function legacySave(Request $request): JsonResponse { $id = $request->input('id'); @@ -183,9 +176,6 @@ class AttendanceCommentTemplateController extends Controller return response()->json(['status' => 'success']); } - /** - * CodeIgniter {@see \App\Controllers\View\AttendanceCommentTemplateController::delete}. - */ public function legacyDelete(Request $request): JsonResponse { $id = $request->input('id'); diff --git a/app/Http/Controllers/Api/Auth/AuthController.php b/app/Http/Controllers/Api/Auth/AuthController.php index 6766a6d2..b7eb842d 100644 --- a/app/Http/Controllers/Api/Auth/AuthController.php +++ b/app/Http/Controllers/Api/Auth/AuthController.php @@ -48,7 +48,7 @@ class AuthController extends BaseApiController ], 401); } - if (! ci_password_verify($password, (string) $user->password)) { + if (! verify_stored_password($password, (string) $user->password)) { $security->handleFailedLogin($user, $email, (string) $ip); return response()->json([ diff --git a/app/Http/Controllers/Api/Auth/AuthSessionController.php b/app/Http/Controllers/Api/Auth/AuthSessionController.php index 0517af1e..f7c83949 100644 --- a/app/Http/Controllers/Api/Auth/AuthSessionController.php +++ b/app/Http/Controllers/Api/Auth/AuthSessionController.php @@ -26,9 +26,7 @@ class AuthSessionController extends Controller private ApplicationUrlService $urls, ) {} - /** - * CI `loginMask` — login page bootstrap / redirect when already authenticated. - */ + /** Returns login bootstrap state for the web portal. */ public function loginMask(Request $request): JsonResponse { $redirectTo = $this->sessionAuth->sanitizeRedirectTarget( @@ -77,9 +75,7 @@ class AuthSessionController extends Controller ]); } - /** - * CI `login` — POST /user/login. - */ + /** Creates a web session from the submitted credentials. */ public function login(LoginSessionRequest $request): JsonResponse { $email = (string) $request->validated('email'); @@ -116,7 +112,7 @@ class AuthSessionController extends Controller ], 403); } - if (! ci_password_verify($password, (string) $user->password)) { + if (! verify_stored_password($password, (string) $user->password)) { $this->security->handleFailedLogin($user, $email, $ip); return response()->json([ @@ -153,9 +149,7 @@ class AuthSessionController extends Controller ]); } - /** - * CI `logout` — GET /logout. - */ + /** Closes the current web session. */ public function logout(Request $request): JsonResponse { $userId = Auth::guard('web')->id(); @@ -174,9 +168,7 @@ class AuthSessionController extends Controller ]); } - /** - * CI `selectRole` — GET /select-role. - */ + /** Returns the available post-login roles for the current user. */ public function selectRole(Request $request): JsonResponse { if (! Auth::guard('web')->check()) { @@ -207,9 +199,7 @@ class AuthSessionController extends Controller ]); } - /** - * CI `setRole` — POST /set-role. - */ + /** Stores the selected post-login role in session state. */ public function setRole(SetRoleSessionRequest $request): JsonResponse { if (! Auth::guard('web')->check()) { diff --git a/app/Http/Controllers/Api/Auth/RegisterController.php b/app/Http/Controllers/Api/Auth/RegisterController.php index d9bef5af..0de4fd44 100644 --- a/app/Http/Controllers/Api/Auth/RegisterController.php +++ b/app/Http/Controllers/Api/Auth/RegisterController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers\Api\Auth; use App\Http\Controllers\Api\Core\BaseApiController; use App\Http\Resources\Auth\RegisterResource; -use App\Services\Auth\CodeIgniterApiRegistrationService; +use App\Services\Auth\ApiRegistrationService; use App\Services\Auth\RegistrationCaptchaService; use App\Services\Auth\RegistrationService; use Illuminate\Http\JsonResponse; @@ -32,9 +32,9 @@ class RegisterController extends BaseApiController public function store(Request $request): JsonResponse { - // CodeIgniter-style JSON register (POST api/v1/register): password + no captcha field. + // Support direct JSON registration when the SPA skips the captcha bootstrap step. if (! $request->has('captcha') && $request->filled('password')) { - return app(CodeIgniterApiRegistrationService::class)->registerResponse($request); + return app(ApiRegistrationService::class)->registerResponse($request); } if (app()->runningUnitTests() && $request->header('X-Debug-Request') === '1') { diff --git a/app/Http/Controllers/Api/Auth/SessionTimeoutController.php b/app/Http/Controllers/Api/Auth/SessionTimeoutController.php index 8b5df4ae..6cb3a380 100644 --- a/app/Http/Controllers/Api/Auth/SessionTimeoutController.php +++ b/app/Http/Controllers/Api/Auth/SessionTimeoutController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers\Api\Auth; -use App\Config\SessionTimeout; use App\Http\Controllers\Controller; use App\Services\ApplicationUrlService; +use App\Support\SessionTimeoutConfig; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Auth; */ class SessionTimeoutController extends Controller { - /** CI-compatible session key */ + /** Shared session key for last-activity tracking. */ private const SESSION_KEY = 'last_activity'; public function __construct( @@ -23,15 +23,15 @@ class SessionTimeoutController extends Controller } /** - * CI nested routes: SessionController::getTimeoutConfig shape (URLs point at this app). + * Returns timeout metadata for the web portal. */ public function getTimeoutConfig(): JsonResponse { return response()->json([ 'success' => true, - 'timeout' => SessionTimeout::TIMEOUT_DURATION, - 'warning_time' => SessionTimeout::WARNING_THRESHOLD, - 'check_interval' => SessionTimeout::CHECK_INTERVAL, + 'timeout' => SessionTimeoutConfig::TIMEOUT_DURATION, + 'warning_time' => SessionTimeoutConfig::WARNING_THRESHOLD, + 'check_interval' => SessionTimeoutConfig::CHECK_INTERVAL, 'logout_url' => $this->urls->webLogoutUrl(), 'keep_alive_url' => $this->urls->webSessionPingUrl(), 'check_url' => $this->urls->webSessionCheckTimeoutUrl(), @@ -49,20 +49,20 @@ class SessionTimeoutController extends Controller $lastActivity = (int) $session->get(self::SESSION_KEY); $elapsed = time() - $lastActivity; - if ($elapsed > SessionTimeout::TIMEOUT_DURATION) { + if ($elapsed > SessionTimeoutConfig::TIMEOUT_DURATION) { return $this->expireSession($request); } - if ($elapsed > SessionTimeout::WARNING_THRESHOLD) { + if ($elapsed > SessionTimeoutConfig::WARNING_THRESHOLD) { return response()->json([ 'status' => 'warning', - 'time_remaining' => SessionTimeout::TIMEOUT_DURATION - $elapsed, + 'time_remaining' => SessionTimeoutConfig::TIMEOUT_DURATION - $elapsed, ]); } return response()->json([ 'status' => 'active', - 'time_remaining' => SessionTimeout::TIMEOUT_DURATION - $elapsed, + 'time_remaining' => SessionTimeoutConfig::TIMEOUT_DURATION - $elapsed, ]); } @@ -71,7 +71,7 @@ class SessionTimeoutController extends Controller $session = $request->session(); if (! $session->has(self::SESSION_KEY) - || (time() - (int) $session->get(self::SESSION_KEY)) > SessionTimeout::TIMEOUT_DURATION) { + || (time() - (int) $session->get(self::SESSION_KEY)) > SessionTimeoutConfig::TIMEOUT_DURATION) { return $this->expireSession($request); } @@ -79,7 +79,7 @@ class SessionTimeoutController extends Controller return response()->json([ 'status' => 'active', - 'time_remaining' => SessionTimeout::TIMEOUT_DURATION, + 'time_remaining' => SessionTimeoutConfig::TIMEOUT_DURATION, ]); } diff --git a/app/Http/Controllers/Api/BadgeScan/BadgeScanController.php b/app/Http/Controllers/Api/BadgeScan/BadgeScanController.php index 99e7ec5a..81404f5c 100644 --- a/app/Http/Controllers/Api/BadgeScan/BadgeScanController.php +++ b/app/Http/Controllers/Api/BadgeScan/BadgeScanController.php @@ -18,7 +18,7 @@ class BadgeScanController extends BaseApiController } /** - * Public kiosk endpoint — POST JSON or form (legacy CI RFIDController::process). + * Public kiosk endpoint — POST JSON or form (legacy RFIDController::process). */ public function scan(Request $request): JsonResponse { @@ -44,7 +44,7 @@ class BadgeScanController extends BaseApiController } /** - * Authenticated scan log listing (legacy CI RFIDController::log). + * Authenticated scan log listing (legacy RFIDController::log). */ public function logs(): JsonResponse { diff --git a/app/Http/Controllers/Api/BaseApiController.php b/app/Http/Controllers/Api/BaseApiController.php index aa93b2ab..00e32be0 100644 --- a/app/Http/Controllers/Api/BaseApiController.php +++ b/app/Http/Controllers/Api/BaseApiController.php @@ -2,276 +2,8 @@ namespace App\Http\Controllers\Api; -use App\Http\Controllers\Controller; -use App\Http\Controllers\Api\CiRequestAdapter; -use App\Models\User; -use Illuminate\Contracts\Validation\Validator as ValidatorContract; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Validator; -use Symfony\Component\HttpFoundation\Response; +use App\Http\Controllers\Api\Core\BaseApiController as CoreBaseApiController; -class BaseApiController extends Controller +class BaseApiController extends CoreBaseApiController { - protected Request $laravelRequest; - protected CiRequestAdapter $request; - protected ValidatorContract|null $validator = null; - - public function __construct() - { - $this->laravelRequest = request(); - $this->request = new CiRequestAdapter($this->laravelRequest); - } - - protected function success($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse - { - return response()->json([ - 'status' => true, - 'message' => $message, - 'data' => $data, - ], $code); - } - - protected function error(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST, ?array $errors = null): JsonResponse - { - $payload = [ - 'status' => false, - 'message' => $message, - ]; - if (!empty($errors)) { - $payload['errors'] = $errors; - } - - return response()->json($payload, $code); - } - - protected function respondSuccess($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse - { - return $this->success($data, $message, $code); - } - - protected function respondCreated($data = null, string $message = 'Resource created successfully'): JsonResponse - { - return $this->success($data, $message, Response::HTTP_CREATED); - } - - protected function respondDeleted($data = null, string $message = 'Resource deleted successfully'): JsonResponse - { - return $this->success($data, $message, Response::HTTP_OK); - } - - protected function respondError(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST): JsonResponse - { - return $this->error($message, $code); - } - - protected function respondValidationError(array $errors, string $message = 'Validation failed'): JsonResponse - { - return $this->error($message, Response::HTTP_UNPROCESSABLE_ENTITY, $errors); - } - - protected function validateRequest(array $data, array $rules): array - { - $normalized = []; - foreach ($rules as $field => $ruleSet) { - $normalized[$field] = $this->normalizeRules($ruleSet); - } - - $validator = Validator::make($data, $normalized); - $this->validator = $validator; - - if ($validator->fails()) { - return $validator->errors()->toArray(); - } - - return []; - } - - protected function validate(array $rules): bool - { - $payload = $this->payloadData(); - $errors = $this->validateRequest($payload, $rules); - return empty($errors); - } - - protected function payloadData(): array - { - $json = json_decode($this->laravelRequest->getContent() ?: '', true); - if (is_array($json)) { - return $json; - } - return array_merge($this->laravelRequest->query->all(), $this->laravelRequest->request->all()); - } - - protected function normalizeRules(array|string $ruleSet): array|string - { - if (is_array($ruleSet)) { - return $ruleSet; - } - $parts = explode('|', $ruleSet); - $result = []; - foreach ($parts as $part) { - $part = trim($part); - if ($part === '') { - continue; - } - if (preg_match('/^max_length\\[(\\d+)\\]$/', $part, $m)) { - $result[] = 'max:' . $m[1]; - continue; - } - if (preg_match('/^min_length\\[(\\d+)\\]$/', $part, $m)) { - $result[] = 'min:' . $m[1]; - continue; - } - if (preg_match('/^in_list\\[(.+)\\]$/', $part, $m)) { - $result[] = 'in:' . $m[1]; - continue; - } - if (preg_match('/^greater_than_equal_to\\[(.+)\\]$/', $part, $m)) { - $result[] = 'gte:' . $m[1]; - continue; - } - if (preg_match('/^less_than_equal_to\\[(.+)\\]$/', $part, $m)) { - $result[] = 'lte:' . $m[1]; - continue; - } - if (preg_match('/^valid_date\\[(.+)\\]$/', $part, $m)) { - $result[] = 'date_format:' . $m[1]; - continue; - } - if ($part === 'valid_email') { - $result[] = 'email'; - continue; - } - if ($part === 'permit_empty') { - $result[] = 'nullable'; - continue; - } - if (preg_match('/^is_unique\\[([^\\.]+)\\.([^\\]]+)\\]$/', $part, $m)) { - $result[] = 'unique:' . $m[1] . ',' . $m[2]; - continue; - } - if ($part === 'string') { - $result[] = 'string'; - continue; - } - if ($part === 'integer') { - $result[] = 'integer'; - continue; - } - if ($part === 'numeric') { - $result[] = 'numeric'; - continue; - } - if ($part === 'required') { - $result[] = 'required'; - continue; - } - if ($part === 'alpha_numeric') { - $result[] = 'alpha_num'; - continue; - } - if ($part === 'in_array') { - $result[] = 'in_array'; - continue; - } - if ($part === 'valid_url') { - $result[] = 'url'; - continue; - } - if (strpos($part, 'max_size') === 0) { - $result[] = str_replace('max_size', 'max', $part); - continue; - } - $result[] = $part; - } - - return $result; - } - - protected function paginate($source, int $page = 1, int $perPage = 20): array - { - $page = max(1, $page); - $perPage = max(1, $perPage); - $offset = ($page - 1) * $perPage; - - if ($source instanceof EloquentBuilder || $source instanceof \Illuminate\Database\Query\Builder) { - $total = $source->toBase()->getCountForPagination(); - $items = $source->offset($offset)->limit($perPage)->get()->toArray(); - } elseif (is_array($source)) { - $total = count($source); - $items = array_slice($source, $offset, $perPage); - } else { - return [ - 'data' => [], - 'pagination' => [ - 'current_page' => $page, - 'per_page' => $perPage, - 'total' => 0, - 'total_pages' => 0, - ], - ]; - } - - return [ - 'data' => array_values($items), - 'pagination' => [ - 'current_page' => $page, - 'per_page' => $perPage, - 'total' => $total, - 'total_pages' => (int) ceil($total / $perPage), - ], - ]; - } - - protected function getCurrentUserId(): ?int - { - $userId = (int) (auth()->id() ?? 0); - return $userId > 0 ? $userId : null; - } - - protected function getCurrentUser(): ?object - { - $userId = $this->getCurrentUserId(); - if (!$userId) { - return null; - } - - $user = app(User::class); - $row = $user->find($userId); - if (!$row) { - return null; - } - - $name = trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')); - if ($name === '') { - $name = $row['email'] ?? 'User #' . $userId; - } - - try { - $roles = DB::table('user_roles ur') - ->select('LOWER(r.name) AS name') - ->join('roles r', 'r.id', '=', 'ur.role_id') - ->where('ur.user_id', $userId) - ->whereNull('ur.deleted_at') - ->pluck('name') - ->map(fn($name) => strtolower((string) $name)) - ->unique() - ->values() - ->toArray(); - } catch (\Throwable $e) { - Log::error('Unable to load user roles: ' . $e->getMessage()); - $roles = []; - } - - return (object) [ - 'id' => (int) $userId, - 'name' => $name, - 'email' => $row['email'] ?? null, - 'roles' => $roles, - ]; - } } diff --git a/app/Http/Controllers/Api/CiRequestAdapter.php b/app/Http/Controllers/Api/CiRequestAdapter.php deleted file mode 100644 index 8abb5503..00000000 --- a/app/Http/Controllers/Api/CiRequestAdapter.php +++ /dev/null @@ -1,70 +0,0 @@ -request->query->all(); - } - return $this->request->query->get($key, $default); - } - - public function getPost(string $key = null, $default = null) - { - if ($key === null) { - return $this->request->request->all(); - } - return $this->request->request->get($key, $default); - } - - public function getJSON(bool $assoc = false) - { - $content = $this->request->getContent(); - if ($content === '') { - return $assoc ? [] : null; - } - return json_decode($content, $assoc); - } - - public function getVar(string $key, $default = null) - { - return $this->request->input($key, $default); - } - - public function getHeader(string $key) - { - return $this->request->headers->get($key); - } - - public function getHeaderLine(string $key) - { - return $this->request->header($key); - } - - public function getFile(string $key) - { - return $this->request->file($key); - } - - public function getRawInput(): string - { - return $this->request->getContent(); - } - - public function all(): array - { - return array_merge( - $this->request->query->all(), - $this->request->request->all(), - $this->request->json()->all() - ); - } -} diff --git a/app/Http/Controllers/Api/Core/BaseApiController.php b/app/Http/Controllers/Api/Core/BaseApiController.php index ebd54c7e..3b05ff3c 100644 --- a/app/Http/Controllers/Api/Core/BaseApiController.php +++ b/app/Http/Controllers/Api/Core/BaseApiController.php @@ -16,13 +16,11 @@ use Symfony\Component\HttpFoundation\Response; class BaseApiController extends Controller { protected Request $laravelRequest; - protected CiRequestAdapter $request; protected ValidatorContract|null $validator = null; public function __construct() { $this->laravelRequest = request(); - $this->request = new CiRequestAdapter($this->laravelRequest); } protected function success($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse diff --git a/app/Http/Controllers/Api/Core/CiRequestAdapter.php b/app/Http/Controllers/Api/Core/CiRequestAdapter.php deleted file mode 100644 index 5955075a..00000000 --- a/app/Http/Controllers/Api/Core/CiRequestAdapter.php +++ /dev/null @@ -1,71 +0,0 @@ -request->query->all(); - } - return $this->request->query->get($key, $default); - } - - public function getPost(string $key = null, $default = null) - { - if ($key === null) { - return $this->request->request->all(); - } - return $this->request->request->get($key, $default); - } - - public function getJSON(bool $assoc = false) - { - $content = $this->request->getContent(); - if ($content === '') { - return $assoc ? [] : null; - } - return json_decode($content, $assoc); - } - - public function getVar(string $key, $default = null) - { - return $this->request->input($key, $default); - } - - public function getHeader(string $key) - { - return $this->request->headers->get($key); - } - - public function getHeaderLine(string $key) - { - return $this->request->header($key); - } - - public function getFile(string $key) - { - return $this->request->file($key); - } - - public function getRawInput(): string - { - return $this->request->getContent(); - } - - public function all(): array - { - return array_merge( - $this->request->query->all(), - $this->request->request->all(), - $this->request->json()->all() - ); - } -} diff --git a/app/Http/Controllers/Api/StatsController.php b/app/Http/Controllers/Api/StatsController.php index 88681f67..b243c89b 100644 --- a/app/Http/Controllers/Api/StatsController.php +++ b/app/Http/Controllers/Api/StatsController.php @@ -4,25 +4,18 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\BaseApiController; use App\Models\Stats; +use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; class StatsController extends BaseApiController { - protected Stats $stats; - - public function __construct() - { - parent::__construct(); - $this->stats = model(Stats::class); - } - public function index() { try { - $stats = $this->stats->findAll(); + $stats = Stats::query()->get()->toArray(); return $this->success($stats, 'Statistics retrieved successfully'); } catch (\Throwable $e) { - log_message('error', 'Stats retrieval error: ' . $e->getMessage()); + Log::error('Stats retrieval error: '.$e->getMessage()); return $this->respondError('Failed to retrieve statistics', Response::HTTP_INTERNAL_SERVER_ERROR); } } diff --git a/app/Http/Controllers/Api/System/AccessDeniedController.php b/app/Http/Controllers/Api/System/AccessDeniedController.php index 0ea77d3c..6146adf8 100644 --- a/app/Http/Controllers/Api/System/AccessDeniedController.php +++ b/app/Http/Controllers/Api/System/AccessDeniedController.php @@ -7,7 +7,7 @@ use Illuminate\Http\JsonResponse; class AccessDeniedController extends Controller { - /** CI route: GET /access_denied */ + /** legacy route: GET /access_denied */ public function accessDenied(): JsonResponse { return response()->json([ diff --git a/app/Http/Controllers/Api/System/HealthController.php b/app/Http/Controllers/Api/System/HealthController.php index 471bf0f5..72398231 100644 --- a/app/Http/Controllers/Api/System/HealthController.php +++ b/app/Http/Controllers/Api/System/HealthController.php @@ -19,7 +19,7 @@ class HealthController extends BaseApiController $payload = $this->service->check(); $status = $payload['ok'] ? Response::HTTP_OK : Response::HTTP_SERVICE_UNAVAILABLE; - // CodeIgniter admin/API health returns a flat JSON body (no Laravel success envelope). + // legacy admin/API health returns a flat JSON body (no Laravel success envelope). return response()->json($payload, $status); } } diff --git a/app/Http/Controllers/Api/System/StatsController.php b/app/Http/Controllers/Api/System/StatsController.php index 30e7ba58..665550fb 100644 --- a/app/Http/Controllers/Api/System/StatsController.php +++ b/app/Http/Controllers/Api/System/StatsController.php @@ -4,25 +4,18 @@ namespace App\Http\Controllers\Api\System; use App\Http\Controllers\Api\Core\BaseApiController; use App\Models\Stats; +use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; class StatsController extends BaseApiController { - protected Stats $stats; - - public function __construct() - { - parent::__construct(); - $this->stats = model(Stats::class); - } - public function index() { try { - $stats = $this->stats->findAll(); + $stats = Stats::query()->get()->toArray(); return $this->success($stats, 'Statistics retrieved successfully'); } catch (\Throwable $e) { - log_message('error', 'Stats retrieval error: ' . $e->getMessage()); + Log::error('Stats retrieval error: '.$e->getMessage()); return $this->respondError('Failed to retrieve statistics', Response::HTTP_INTERNAL_SERVER_ERROR); } } diff --git a/app/Http/Controllers/Api/Utilities/ProofreadController.php b/app/Http/Controllers/Api/Utilities/ProofreadController.php index 502bcaef..358a8b89 100644 --- a/app/Http/Controllers/Api/Utilities/ProofreadController.php +++ b/app/Http/Controllers/Api/Utilities/ProofreadController.php @@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\RateLimiter; /** - * CodeIgniter {@see \App\Controllers\ProofreadController} — LanguageTool proxy for grading UI. + * legacy {@see \App\Controllers\ProofreadController} — LanguageTool proxy for grading UI. */ class ProofreadController extends Controller { diff --git a/app/Http/Middleware/EnsureBadgeScanLogsAccess.php b/app/Http/Middleware/EnsureBadgeScanLogsAccess.php index e0f8112c..00fef6dd 100644 --- a/app/Http/Middleware/EnsureBadgeScanLogsAccess.php +++ b/app/Http/Middleware/EnsureBadgeScanLogsAccess.php @@ -8,7 +8,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; -/** Authenticated scan log listing — staff roles (legacy CI RFID log screen). */ +/** Authenticated scan log listing — staff roles (legacy RFID log screen). */ class EnsureBadgeScanLogsAccess { public function handle(Request $request, Closure $next): Response diff --git a/app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php b/app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php index b38a1e71..620f215a 100644 --- a/app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php +++ b/app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php @@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; /** - * Mirrors CodeIgniter route filter `auth:teacher,teacher_assistant` (+ elevated staff read). + * Mirrors legacy route filter `auth:teacher,teacher_assistant` (+ elevated staff read). */ class EnsureClassProgressTeacherPortalAccess { diff --git a/app/Http/Middleware/EnsureParentProgressAccess.php b/app/Http/Middleware/EnsureParentProgressAccess.php index 6525acb1..131a035b 100644 --- a/app/Http/Middleware/EnsureParentProgressAccess.php +++ b/app/Http/Middleware/EnsureParentProgressAccess.php @@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; /** - * CodeIgniter route filter `auth:parent`. + * legacy route filter `auth:parent`. * * Allows access for any caller that ultimately maps to a primary parent * record: the primary parent themselves (parent role / user_type=primary), diff --git a/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php b/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php index 339eee38..32055dc3 100644 --- a/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php +++ b/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php @@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; /** - * CodeIgniter route filter `auth:admin` for print requests admin views + * legacy route filter `auth:admin` for print requests admin views * (approximated via elevated staff roles; extend if you use permission-based gates). */ class EnsurePrintRequestsAdminAccess diff --git a/app/Http/Middleware/EnsurePrintRequestsTeacherAccess.php b/app/Http/Middleware/EnsurePrintRequestsTeacherAccess.php index 12155a2d..65068fbb 100644 --- a/app/Http/Middleware/EnsurePrintRequestsTeacherAccess.php +++ b/app/Http/Middleware/EnsurePrintRequestsTeacherAccess.php @@ -8,7 +8,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; -/** CodeIgniter route filter `auth:teacher,teacher_assistant` for print requests. */ +/** legacy route filter `auth:teacher,teacher_assistant` for print requests. */ class EnsurePrintRequestsTeacherAccess { public function handle(Request $request, Closure $next): Response diff --git a/app/Http/Middleware/TeacherPortalAuthenticate.php b/app/Http/Middleware/TeacherPortalAuthenticate.php index f8b23da2..193cf064 100644 --- a/app/Http/Middleware/TeacherPortalAuthenticate.php +++ b/app/Http/Middleware/TeacherPortalAuthenticate.php @@ -10,7 +10,7 @@ use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth; use Symfony\Component\HttpFoundation\Response; /** - * Session (web) auth first (CodeIgniter parity), then Sanctum / JWT like {@see MultiAuth}. + * Session (web) auth first (legacy parity), then Sanctum / JWT like {@see MultiAuth}. */ class TeacherPortalAuthenticate { diff --git a/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php b/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php index 1cb0f467..c9b71b46 100644 --- a/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php +++ b/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php @@ -5,7 +5,7 @@ namespace App\Http\Requests\Admin; use Illuminate\Foundation\Http\FormRequest; /** - * Mirrors CodeIgniter {@see \App\Controllers\Admin\CompetitionWinnersController::saveScores()} accepted fields. + * Mirrors legacy {@see \App\Controllers\Admin\CompetitionWinnersController::saveScores()} accepted fields. */ class SaveCompetitionScoresRequest extends FormRequest { diff --git a/app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php b/app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php index 084f1f42..7cba76e2 100644 --- a/app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php +++ b/app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php @@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin; use Illuminate\Foundation\Http\FormRequest; -/** Validation for creating a competition (CodeIgniter `CompetitionWinnersController::store`). */ +/** Validation for creating a competition (legacy `CompetitionWinnersController::store`). */ class StoreCompetitionWinnerRequest extends FormRequest { public function authorize(): bool diff --git a/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php b/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php index 6146fa1e..5610abd3 100644 --- a/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php +++ b/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php @@ -2,7 +2,7 @@ namespace App\Http\Requests\Admin; -/** Validation for updating competition settings (CodeIgniter `CompetitionWinnersController::update`). */ +/** Validation for updating competition settings (legacy `CompetitionWinnersController::update`). */ class UpdateCompetitionWinnerRequest extends StoreCompetitionWinnerRequest { } diff --git a/app/Models/AdditionalCharge.php b/app/Models/AdditionalCharge.php index 87962e1c..1c50fd08 100644 --- a/app/Models/AdditionalCharge.php +++ b/app/Models/AdditionalCharge.php @@ -10,7 +10,7 @@ class AdditionalCharge extends BaseModel { protected $table = 'additional_charges'; - // CI useTimestamps = false + // legacy useTimestamps = false public $timestamps = false; protected $fillable = [ @@ -90,7 +90,7 @@ class AdditionalCharge extends BaseModel } /* ========================= - * CI methods equivalents + * legacy methods equivalents * ========================= */ public static function byParentTerm(int $parentId, string $year, string $sem, ?string $status = null) diff --git a/app/Models/AdminNotificationSubject.php b/app/Models/AdminNotificationSubject.php index 8e45776c..469a54c3 100644 --- a/app/Models/AdminNotificationSubject.php +++ b/app/Models/AdminNotificationSubject.php @@ -15,7 +15,7 @@ class AdminNotificationSubject extends BaseModel 'updated_at', ]; - // CI uses created_at / updated_at + // legacy uses created_at / updated_at public $timestamps = true; protected $casts = [ 'admin_id' => 'integer', diff --git a/app/Models/AttendanceCommentTemplate.php b/app/Models/AttendanceCommentTemplate.php index d022bff1..bfd0df1b 100644 --- a/app/Models/AttendanceCommentTemplate.php +++ b/app/Models/AttendanceCommentTemplate.php @@ -8,7 +8,7 @@ class AttendanceCommentTemplate extends BaseModel { protected $table = 'attendance_comment_template'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = false; protected $fillable = [ @@ -27,7 +27,7 @@ class AttendanceCommentTemplate extends BaseModel ]; /** - * Equivalent of CI getActiveTemplates(): + * Equivalent of legacy getActiveTemplates(): * is_active=1, order by min_score DESC */ public static function getActiveTemplates() diff --git a/app/Models/AttendanceData.php b/app/Models/AttendanceData.php index 60b218cc..51770c6c 100644 --- a/app/Models/AttendanceData.php +++ b/app/Models/AttendanceData.php @@ -63,7 +63,7 @@ class AttendanceData extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** diff --git a/app/Models/AttendanceDay.php b/app/Models/AttendanceDay.php index bd2a7919..b9c00c22 100644 --- a/app/Models/AttendanceDay.php +++ b/app/Models/AttendanceDay.php @@ -10,8 +10,8 @@ class AttendanceDay extends BaseModel { protected $table = 'attendance_day'; - // ✅ You are storing created_at / updated_at manually in CI (timestamps off there) - // User didn't explicitly ask to auto-manage here, so we keep it OFF to match CI. + // ✅ You are storing created_at / updated_at manually in legacy (timestamps off there) + // User didn't explicitly ask to auto-manage here, so we keep it OFF to match legacy. // If you want auto-manage, tell me and I’ll flip it. public $timestamps = false; @@ -56,7 +56,7 @@ class AttendanceDay extends BaseModel ]; /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** diff --git a/app/Models/AttendanceRecord.php b/app/Models/AttendanceRecord.php index 3e9fc431..79756eec 100644 --- a/app/Models/AttendanceRecord.php +++ b/app/Models/AttendanceRecord.php @@ -9,7 +9,7 @@ class AttendanceRecord extends BaseModel { protected $table = 'attendance_record'; - // ✅ CI uses created_at / updated_at + // ✅ legacy uses created_at / updated_at public $timestamps = true; protected $fillable = [ @@ -52,7 +52,7 @@ class AttendanceRecord extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** @@ -123,7 +123,7 @@ class AttendanceRecord extends BaseModel ->where('school_year', $schoolYear) ->update($update); - // If no record exists, return false (same idea as your CI code) + // If no record exists, return false (same idea as your legacy code) return $affected > 0; } diff --git a/app/Models/AttendanceTracking.php b/app/Models/AttendanceTracking.php index 556387cf..c7148bd9 100644 --- a/app/Models/AttendanceTracking.php +++ b/app/Models/AttendanceTracking.php @@ -10,7 +10,7 @@ class AttendanceTracking extends BaseModel { protected $table = 'attendance_tracking'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -27,7 +27,7 @@ class AttendanceTracking extends BaseModel protected $casts = [ 'student_id' => 'integer', - 'date' => 'datetime', // CI dateFormat = datetime + 'date' => 'datetime', // legacy dateFormat = datetime 'is_reported' => 'integer', 'is_notified' => 'integer', 'notif_counter' => 'integer', @@ -245,7 +245,7 @@ class AttendanceTracking extends BaseModel /** * TRUE if at least $absencesThreshold absences in the last $weeksStreak weeks. - * (Mirrors your CI behavior: count of last N fetched records) + * (Mirrors your legacy behavior: count of last N fetched records) */ public static function checkAbsencesInStreak( int $studentId, diff --git a/app/Models/BadgePrintLog.php b/app/Models/BadgePrintLog.php index 115f93b3..af7d3fcd 100644 --- a/app/Models/BadgePrintLog.php +++ b/app/Models/BadgePrintLog.php @@ -11,7 +11,7 @@ class BadgePrintLog extends BaseModel { protected $table = 'badge_print_logs'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = false; protected $fillable = [ @@ -71,7 +71,7 @@ class BadgePrintLog extends BaseModel if (empty($rows)) return 0; try { - // insert returns bool; we return count best-effort like CI + // insert returns bool; we return count best-effort like legacy DB::table((new static)->getTable())->insert($rows); return count($rows); } catch (QueryException $e) { diff --git a/app/Models/CalendarEvent.php b/app/Models/CalendarEvent.php index 401c874d..bcb814bf 100644 --- a/app/Models/CalendarEvent.php +++ b/app/Models/CalendarEvent.php @@ -10,7 +10,7 @@ class CalendarEvent extends BaseModel { protected $table = 'calendar_events'; - // ✅ CI uses created_at / updated_at + // ✅ legacy uses created_at / updated_at public $timestamps = true; protected $fillable = [ @@ -42,7 +42,7 @@ class CalendarEvent extends BaseModel private static ?bool $hasEventTypeColumn = null; /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ public static function getEvents() @@ -96,7 +96,7 @@ class CalendarEvent extends BaseModel } /* ========================= - * Helpers (ported from CI) + * Helpers (ported from legacy) * ========================= */ /** diff --git a/app/Models/CiDatabase.php b/app/Models/CiDatabase.php deleted file mode 100644 index 3889be3c..00000000 --- a/app/Models/CiDatabase.php +++ /dev/null @@ -1,82 +0,0 @@ -connection = DB::connection(); - } - - public function table(string $table): CiQueryBuilder - { - $normalized = $this->normalizeTableName($table); - return new CiQueryBuilder($this->connection->table($normalized)); - } - - public function query(string $sql, array $params = []): CiQueryResult - { - $trimmed = ltrim($sql); - if ($trimmed === '') { - return new CiQueryResult([]); - } - - $first = strtolower(strtok($trimmed, " \t\n\r\0\x0B") ?: ''); - - if ($first === 'select') { - $rows = $this->connection->select($sql, $params); - $this->lastAffectedRows = 0; - return new CiQueryResult($rows); - } - - $affected = $this->connection->affectingStatement($sql, $params); - $this->lastAffectedRows = $affected; - - return new CiQueryResult([]); - } - - public function escape($value): string - { - return $this->connection->getPdo()->quote($value); - } - - public function affectedRows(): int - { - return $this->lastAffectedRows; - } - - public function getFieldNames(string $table): array - { - return $this->connection->getSchemaBuilder()->getColumnListing($table); - } - - protected function normalizeTableName(string $table): string - { - $normalized = trim($table); - - if (stripos($normalized, ' as ') !== false) { - return $normalized; - } - - if (strpos($normalized, ' ') !== false) { - [$name, $alias] = preg_split('/\s+/', $normalized, 2); - return "{$name} as {$alias}"; - } - - return $normalized; - } -} diff --git a/app/Models/CiModelBuilder.php b/app/Models/CiModelBuilder.php deleted file mode 100644 index 474eaa96..00000000 --- a/app/Models/CiModelBuilder.php +++ /dev/null @@ -1,105 +0,0 @@ - 0) { - $this->limit($limit); - } - - if ($offset > 0) { - $this->offset($offset); - } - - return $this->getResultArray(); - } - - public function getResultArray(): array - { - return $this->get()->getResultArray(); - } - - public function getRowArray(): ?array - { - return $this->get()->getRowArray(); - } - - public function getRow(?string $column = null) - { - return $this->get()->getRow($column); - } - - public function countAllResults(): int - { - return $this->count(); - } - - public function like(string $column, $match, string $side = 'both') - { - $pattern = $this->wrapLike($match, $side); - $this->query->where($column, 'like', $pattern); - return $this; - } - - public function set($key, $value = null, bool $escape = true) - { - if (is_array($key)) { - $this->setData = array_merge($this->setData, $key); - } else { - $this->setData[$key] = $value; - } - - return $this; - } - - public function update(array $values = []) - { - $data = $values ?: $this->setData; - $this->setData = []; - - return parent::update($data); - } - - public function where($column, $operator = null, $value = null, $boolean = 'and') - { - if ($operator !== null && $value === null && preg_match('/^(.*)\\s(>=|<=|<>|!=|>|<)$/', $column, $matches)) { - return parent::where(trim($matches[1]), $matches[2], $operator, $boolean); - } - - return parent::where($column, $operator, $value, $boolean); - } - - protected function wrapLike($match, string $side): string - { - $pattern = "%{$match}%"; - - switch (strtolower($side)) { - case 'before': - $pattern = "%{$match}"; - break; - case 'after': - $pattern = "{$match}%"; - break; - } - - return $pattern; - } -} diff --git a/app/Models/CiQueryBuilder.php b/app/Models/CiQueryBuilder.php deleted file mode 100644 index 335d00e5..00000000 --- a/app/Models/CiQueryBuilder.php +++ /dev/null @@ -1,153 +0,0 @@ -builder = $builder; - } - - public function __call($method, $arguments) - { - $arguments = $this->normalizeTableArgument($method, $arguments); - $result = $this->builder->$method(...$arguments); - - if ($result instanceof QueryBuilder) { - $this->builder = $result; - return $this; - } - - return $result; - } - - public function where($column, $operator = null, $value = null, $boolean = 'and') - { - if ($operator !== null && $value === null && preg_match('/^(.*)\s(>=|<=|<>|!=|>|<)$/', $column, $matches)) { - return $this->where(trim($matches[1]), $matches[2], $operator, $boolean); - } - - $this->builder = $this->builder->where($column, $operator, $value, $boolean); - return $this; - } - - public function like($column, $match, string $side = 'both') - { - $pattern = $this->wrapLike($match, $side); - $this->builder = $this->builder->where($column, 'like', $pattern); - return $this; - } - - public function set($key, $value = null, bool $escape = true) - { - if (is_array($key)) { - $this->setData = array_merge($this->setData, $key); - } else { - $this->setData[$key] = $value; - } - - return $this; - } - - public function update($values = []) - { - $data = $values ?: $this->setData; - $this->setData = []; - return $this->builder->update($data); - } - - public function findAll(int $limit = 0, int $offset = 0) - { - if ($limit > 0) { - $this->builder = $this->builder->limit($limit); - } - - if ($offset > 0) { - $this->builder = $this->builder->offset($offset); - } - - return $this->getResultArray(); - } - - public function get($columns = ['*']) - { - $collection = $this->builder->get($columns); - return new CiQueryResult($collection); - } - - public function getResultArray(): array - { - return $this->get()->getResultArray(); - } - - public function getRowArray(): ?array - { - return $this->get()->getRowArray(); - } - - public function getRow(?string $column = null) - { - return $this->get()->getRow($column); - } - - public function countAllResults(): int - { - return $this->builder->count(); - } - - protected function wrapLike($match, string $side): string - { - $pattern = "%{$match}%"; - - switch (strtolower($side)) { - case 'before': - return "%{$match}"; - case 'after': - return "{$match}%"; - default: - return $pattern; - } - } - - protected function normalizeTableArgument(string $method, array $arguments): array - { - if (in_array($method, $this->tableMethods, true) && isset($arguments[0]) && is_string($arguments[0])) { - $arguments[0] = $this->normalizeTableName($arguments[0]); - } - - return $arguments; - } - - protected function normalizeTableName(string $table): string - { - $normalized = trim($table); - - if (stripos($normalized, ' as ') !== false) { - return $normalized; - } - - if (strpos($normalized, ' ') !== false) { - [$name, $alias] = preg_split('/\s+/', $normalized, 2); - return "{$name} as {$alias}"; - } - - return $normalized; - } -} diff --git a/app/Models/CiQueryResult.php b/app/Models/CiQueryResult.php deleted file mode 100644 index 7c738029..00000000 --- a/app/Models/CiQueryResult.php +++ /dev/null @@ -1,56 +0,0 @@ -all(); - } - - parent::__construct(array_map([$this, 'castRow'], (array) $items)); - } - - protected function castRow($row): array - { - if ($row instanceof Model) { - return $row->toArray(); - } - - if (is_object($row)) { - return (array) $row; - } - - return (array) $row; - } - - public function getResultArray(): array - { - return $this->toArray(); - } - - public function getRowArray(): ?array - { - $first = $this->first(); - return $first ? (array) $first : null; - } - - public function getRow(?string $column = null) - { - $row = $this->getRowArray(); - if ($row === null) { - return null; - } - - if ($column === null) { - return $row; - } - - return $row[$column] ?? null; - } -} diff --git a/app/Models/ClassPrepAdjustment.php b/app/Models/ClassPrepAdjustment.php index 5c23de41..5fafae83 100644 --- a/app/Models/ClassPrepAdjustment.php +++ b/app/Models/ClassPrepAdjustment.php @@ -8,7 +8,7 @@ class ClassPrepAdjustment extends BaseModel { protected $table = 'class_prep_adjustments'; - // CI model didn't use timestamps; keep off unless your table has updated_at/created_at auto-managed. + // legacy model didn't use timestamps; keep off unless your table has updated_at/created_at auto-managed. public $timestamps = true; protected $fillable = [ diff --git a/app/Models/ClassPreparationLog.php b/app/Models/ClassPreparationLog.php index 1dd9230e..2601b91b 100644 --- a/app/Models/ClassPreparationLog.php +++ b/app/Models/ClassPreparationLog.php @@ -8,7 +8,7 @@ class ClassPreparationLog extends BaseModel { protected $table = 'class_preparation_log'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = true; protected $fillable = [ diff --git a/app/Models/ClassProgressAttachment.php b/app/Models/ClassProgressAttachment.php index 8602c436..b0c77514 100644 --- a/app/Models/ClassProgressAttachment.php +++ b/app/Models/ClassProgressAttachment.php @@ -10,7 +10,7 @@ class ClassProgressAttachment extends BaseModel use HasFactory; protected $table = 'class_progress_attachments'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = false; protected $fillable = [ diff --git a/app/Models/ClassProgressReport.php b/app/Models/ClassProgressReport.php index 531516a2..b9283257 100644 --- a/app/Models/ClassProgressReport.php +++ b/app/Models/ClassProgressReport.php @@ -10,7 +10,7 @@ class ClassProgressReport extends BaseModel use HasFactory; protected $table = 'class_progress_reports'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/ClassSection.php b/app/Models/ClassSection.php index 59ff278c..0f517359 100644 --- a/app/Models/ClassSection.php +++ b/app/Models/ClassSection.php @@ -9,10 +9,10 @@ use Illuminate\Support\Facades\DB; class ClassSection extends BaseModel { use HasFactory; - // CI table name is "classSection" + // legacy table name is "classSection" protected $table = 'classSection'; - // ✅ CI: useTimestamps = true + // ✅ legacy: useTimestamps = true public $timestamps = true; protected $fillable = [ @@ -48,11 +48,11 @@ class ClassSection extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** - * Get class sections with class name (like CI join result). + * Get class sections with class name (like legacy join result). * Returns array of rows: class_section_id, class_section_name, class_name */ public static function getClassSections(): array diff --git a/app/Models/CommunicationLog.php b/app/Models/CommunicationLog.php index 9170d2e6..6b4cb08a 100644 --- a/app/Models/CommunicationLog.php +++ b/app/Models/CommunicationLog.php @@ -8,7 +8,7 @@ class CommunicationLog extends BaseModel { protected $table = 'communication_logs'; - // CI model didn't enable timestamps; keep off unless your table has created_at/updated_at. + // legacy model didn't enable timestamps; keep off unless your table has created_at/updated_at. public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Competition.php b/app/Models/Competition.php index 17fce517..40430f46 100644 --- a/app/Models/Competition.php +++ b/app/Models/Competition.php @@ -9,10 +9,10 @@ class Competition extends BaseModel { protected $table = 'competitions'; - // ✅ CI: useSoftDeletes = true + // ✅ legacy: useSoftDeletes = true use SoftDeletes; - // ✅ CI: useTimestamps = true (created_at/updated_at) + deleted_at for soft deletes + // ✅ legacy: useTimestamps = true (created_at/updated_at) + deleted_at for soft deletes public $timestamps = true; protected $fillable = [ diff --git a/app/Models/CompetitionClassWinner.php b/app/Models/CompetitionClassWinner.php index e5b4878e..3d3c5ef4 100644 --- a/app/Models/CompetitionClassWinner.php +++ b/app/Models/CompetitionClassWinner.php @@ -8,7 +8,7 @@ class CompetitionClassWinner extends BaseModel { protected $table = 'competition_class_winners'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/CompetitionScore.php b/app/Models/CompetitionScore.php index 54d0fd96..0d745cd0 100644 --- a/app/Models/CompetitionScore.php +++ b/app/Models/CompetitionScore.php @@ -8,7 +8,7 @@ class CompetitionScore extends BaseModel { protected $table = 'competition_scores'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/CompetitionWinner.php b/app/Models/CompetitionWinner.php index e5c6d5f8..84c1126d 100644 --- a/app/Models/CompetitionWinner.php +++ b/app/Models/CompetitionWinner.php @@ -8,7 +8,7 @@ class CompetitionWinner extends BaseModel { protected $table = 'competition_winners'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = false; protected $fillable = [ diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 7c827fca..c8cc91ab 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -18,7 +18,7 @@ class Configuration extends BaseModel ]; /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** @@ -94,7 +94,7 @@ class Configuration extends BaseModel * Backward-compatible helper (keeps your "semester" special-case). * * NOTE: In Laravel it’s better to call SemesterRangeService directly from services/controllers, - * but this keeps behavior consistent with the CI model. + * but this keeps behavior consistent with the legacy model. */ public static function getConfig(string $key): ?string { @@ -103,7 +103,7 @@ class Configuration extends BaseModel if ($key === 'semester') { try { // If you have this service in Laravel, inject/resolve it from container. - // This call assumes the service constructor can accept Configuration model (like CI did). + // This call assumes the service constructor can accept Configuration model (like legacy did). $semester = app(\App\Services\SemesterRangeService::class)->getSemesterForDate(); if (is_string($semester) && $semester !== '') { return $semester; diff --git a/app/Models/ContactUs.php b/app/Models/ContactUs.php index bfca4103..e891a22f 100644 --- a/app/Models/ContactUs.php +++ b/app/Models/ContactUs.php @@ -8,7 +8,7 @@ class ContactUs extends BaseModel { protected $table = 'contactus'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -39,7 +39,7 @@ class ContactUs extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** diff --git a/app/Models/CurrentFlag.php b/app/Models/CurrentFlag.php index 79d055ae..1382c2df 100644 --- a/app/Models/CurrentFlag.php +++ b/app/Models/CurrentFlag.php @@ -8,7 +8,7 @@ class CurrentFlag extends BaseModel { protected $table = 'current_flag'; - // CI model didn't specify timestamps. Since the table has created_at/updated_at columns, + // legacy model didn't specify timestamps. Since the table has created_at/updated_at columns, // you can enable auto-management. If your table does NOT actually have them, set false. public $timestamps = true; diff --git a/app/Models/DiscountUsage.php b/app/Models/DiscountUsage.php index a6374ae1..fc780085 100644 --- a/app/Models/DiscountUsage.php +++ b/app/Models/DiscountUsage.php @@ -9,7 +9,7 @@ class DiscountUsage extends BaseModel { protected $table = 'discount_usages'; - // ✅ CI: timestamps enabled (created_at / updated_at) + // ✅ legacy: timestamps enabled (created_at / updated_at) public $timestamps = true; protected $fillable = [ @@ -44,7 +44,7 @@ class DiscountUsage extends BaseModel } /** - * Equivalent of CI getTotalDiscountByParentIdAndSchoolYear() + * Equivalent of legacy getTotalDiscountByParentIdAndSchoolYear() */ public static function getTotalDiscountByParentIdAndSchoolYear(int $parentId, string $schoolYear): float { diff --git a/app/Models/DiscountVoucher.php b/app/Models/DiscountVoucher.php index ecdc0c9f..21a99b29 100644 --- a/app/Models/DiscountVoucher.php +++ b/app/Models/DiscountVoucher.php @@ -10,7 +10,7 @@ class DiscountVoucher extends BaseModel { protected $table = 'discount_vouchers'; - // ✅ CI manages created_at / updated_at + // ✅ legacy manages created_at / updated_at public $timestamps = true; protected $fillable = [ @@ -39,7 +39,7 @@ class DiscountVoucher extends BaseModel ]; /* ========================= - * Model hooks (CI beforeInsert/beforeUpdate equivalents) + * Model hooks (legacy beforeInsert/beforeUpdate equivalents) * ========================= */ protected static function booted(): void @@ -95,7 +95,7 @@ class DiscountVoucher extends BaseModel $until = Carbon::parse($this->valid_until)->toDateString(); if ($from > $until) { // In Laravel, prefer validating in FormRequest. - // This keeps your CI behavior by failing the save. + // This keeps your legacy behavior by failing the save. throw new \InvalidArgumentException('Valid Until must be the same as or after Valid From.'); } } @@ -108,7 +108,7 @@ class DiscountVoucher extends BaseModel /** * Find a valid voucher for a student (not expired, active, under max uses, and not used by the student). * - * NOTE: Your CI joins discount_usages on u.student_id. That requires discount_usages.student_id to exist. + * NOTE: Your legacy joins discount_usages on u.student_id. That requires discount_usages.student_id to exist. * If your discount_usages table does NOT have student_id (some of your other code uses parent_id), * switch the filter to parent_id instead. */ diff --git a/app/Models/EarlyDismissalSignature.php b/app/Models/EarlyDismissalSignature.php index 3c014d54..f9a703c4 100644 --- a/app/Models/EarlyDismissalSignature.php +++ b/app/Models/EarlyDismissalSignature.php @@ -8,7 +8,7 @@ class EarlyDismissalSignature extends BaseModel { protected $table = 'early_dismissal_signatures'; - // ✅ CI: timestamps enabled (created_at/updated_at) + // ✅ legacy: timestamps enabled (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -23,7 +23,7 @@ class EarlyDismissalSignature extends BaseModel ]; protected $casts = [ - // report_date is stored as Y-m-d in CI validation; keep as date + // report_date is stored as Y-m-d in legacy validation; keep as date 'report_date' => 'date', 'file_size' => 'integer', diff --git a/app/Models/EmailTemplate.php b/app/Models/EmailTemplate.php index 8b5733f5..cf05bacf 100644 --- a/app/Models/EmailTemplate.php +++ b/app/Models/EmailTemplate.php @@ -8,7 +8,7 @@ class EmailTemplate extends BaseModel { protected $table = 'email_templates'; - // CI model didn't specify timestamps; keep off unless your table has created_at/updated_at. + // legacy model didn't specify timestamps; keep off unless your table has created_at/updated_at. public $timestamps = true; protected $fillable = [ @@ -40,7 +40,7 @@ class EmailTemplate extends BaseModel } /** - * Equivalent of CI getActiveTemplates(): + * Equivalent of legacy getActiveTemplates(): * is_active=1, orderBy template_key ASC */ public static function getActiveTemplates() @@ -52,7 +52,7 @@ class EmailTemplate extends BaseModel } /** - * Equivalent of CI findByKey(): + * Equivalent of legacy findByKey(): * template_key = $key AND is_active=1 */ public static function findByKey(string $key): ?self diff --git a/app/Models/EmergencyContact.php b/app/Models/EmergencyContact.php index d9614690..23d898df 100644 --- a/app/Models/EmergencyContact.php +++ b/app/Models/EmergencyContact.php @@ -8,7 +8,7 @@ class EmergencyContact extends BaseModel { protected $table = 'emergency_contacts'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -46,7 +46,7 @@ class EmergencyContact extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ public static function getEmergencyContactsByStudentId(int $studentId) @@ -64,7 +64,7 @@ class EmergencyContact extends BaseModel } /** - * Your CI logic: (student_id = $studentId) OR (parent_id = $studentId) + * Your legacy logic: (student_id = $studentId) OR (parent_id = $studentId) * Keeping it exactly as-is, even though the 2nd condition looks unusual. */ public static function getAllEmergencyContacts(int $studentId) diff --git a/app/Models/Enrollment.php b/app/Models/Enrollment.php index 0286ff3c..5ff8e09f 100644 --- a/app/Models/Enrollment.php +++ b/app/Models/Enrollment.php @@ -9,7 +9,7 @@ class Enrollment extends BaseModel { protected $table = 'enrollments'; - // ✅ CI: useTimestamps = true + // ✅ legacy: useTimestamps = true public $timestamps = true; protected $fillable = [ @@ -55,7 +55,7 @@ class Enrollment extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** diff --git a/app/Models/Event.php b/app/Models/Event.php index 0d49796b..4eae8188 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -8,7 +8,7 @@ class Event extends BaseModel { protected $table = 'events'; - // ✅ CI: timestamps enabled (created_at/updated_at) + // ✅ legacy: timestamps enabled (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -43,7 +43,7 @@ class Event extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** diff --git a/app/Models/EventCharges.php b/app/Models/EventCharges.php index 43355863..fd6b17e6 100644 --- a/app/Models/EventCharges.php +++ b/app/Models/EventCharges.php @@ -9,7 +9,7 @@ class EventCharges extends BaseModel { protected $table = 'event_charges'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -66,7 +66,7 @@ class EventCharges extends BaseModel } /** - * Equivalent of CI getChargesWithEventInfo() + * Equivalent of legacy getChargesWithEventInfo() * Returns rows with: event_charges.*, events.event_name, events.amount */ public static function getChargesWithEventInfo(?int $parentId = null, ?string $schoolYear = null, ?string $semester = null): array diff --git a/app/Models/Exam.php b/app/Models/Exam.php index 4bdea630..2cf1ec1f 100644 --- a/app/Models/Exam.php +++ b/app/Models/Exam.php @@ -9,7 +9,7 @@ class Exam extends BaseModel protected $table = 'exams'; /** - * Your CI model only uses created_at (updatedField = ''). + * Your legacy model only uses created_at (updatedField = ''). * In Laravel, easiest is to disable timestamps and manage created_at yourself OR * enable timestamps but disable UPDATED_AT. * diff --git a/app/Models/ExamDraft.php b/app/Models/ExamDraft.php index a98c94a5..50e69551 100644 --- a/app/Models/ExamDraft.php +++ b/app/Models/ExamDraft.php @@ -8,7 +8,7 @@ class ExamDraft extends BaseModel { protected $table = 'exam_drafts'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -55,7 +55,7 @@ class ExamDraft extends BaseModel ]; /** - * CI had updateOnlyChanged=false (force updates even if nothing changed). + * legacy had updateOnlyChanged=false (force updates even if nothing changed). * In Laravel, you can force a save by calling: * $model->touch(); // updates updated_at only * or diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 280b8269..c7f841be 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -9,7 +9,7 @@ class Expense extends BaseModel { protected $table = 'expenses'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Family.php b/app/Models/Family.php index 88f8fb76..fdc35785 100644 --- a/app/Models/Family.php +++ b/app/Models/Family.php @@ -8,7 +8,7 @@ class Family extends BaseModel { protected $table = 'families'; - // CI model didn't specify timestamps + // legacy model didn't specify timestamps public $timestamps = true; protected $fillable = [ diff --git a/app/Models/FamilyCommPref.php b/app/Models/FamilyCommPref.php index 0d3845c0..54133289 100644 --- a/app/Models/FamilyCommPref.php +++ b/app/Models/FamilyCommPref.php @@ -8,7 +8,7 @@ class FamilyCommPref extends BaseModel { protected $table = 'family_comm_prefs'; - // CI model didn't specify timestamps + // legacy model didn't specify timestamps public $timestamps = true; protected $fillable = [ diff --git a/app/Models/FamilyGuardian.php b/app/Models/FamilyGuardian.php index 88969c61..b4d957ac 100644 --- a/app/Models/FamilyGuardian.php +++ b/app/Models/FamilyGuardian.php @@ -8,7 +8,7 @@ class FamilyGuardian extends BaseModel { protected $table = 'family_guardians'; - // CI model didn't specify timestamps + // legacy model didn't specify timestamps public $timestamps = true; protected $fillable = [ diff --git a/app/Models/FamilyStudent.php b/app/Models/FamilyStudent.php index cf96e7f3..c472f3b1 100644 --- a/app/Models/FamilyStudent.php +++ b/app/Models/FamilyStudent.php @@ -9,7 +9,7 @@ class FamilyStudent extends BaseModel { protected $table = 'family_students'; - // CI model didn't specify timestamps + // legacy model didn't specify timestamps public $timestamps = true; protected $fillable = [ @@ -37,7 +37,7 @@ class FamilyStudent extends BaseModel } /** - * Laravel equivalent of CI getFamiliesForStudent(). + * Laravel equivalent of legacy getFamiliesForStudent(). * Returns array rows: families.* + is_primary_home */ public static function getFamiliesForStudent(int $studentId): array diff --git a/app/Models/FinalExam.php b/app/Models/FinalExam.php index 2f4aeec9..05b1f46b 100644 --- a/app/Models/FinalExam.php +++ b/app/Models/FinalExam.php @@ -9,7 +9,7 @@ class FinalExam extends BaseModel protected $table = 'final_exam'; /** - * CI: useTimestamps = false (even though table has created_at/updated_at fields). + * legacy: useTimestamps = false (even though table has created_at/updated_at fields). * So we keep timestamps OFF and you can set created_at/updated_at manually if needed. */ public $timestamps = false; @@ -52,7 +52,7 @@ class FinalExam extends BaseModel } /** - * Equivalent of CI getFinalExamScore() + * Equivalent of legacy getFinalExamScore() */ public static function getFinalExamScore( int $studentId, diff --git a/app/Models/FinalScore.php b/app/Models/FinalScore.php index bafc2418..88e4e46a 100644 --- a/app/Models/FinalScore.php +++ b/app/Models/FinalScore.php @@ -8,7 +8,7 @@ class FinalScore extends BaseModel { protected $table = 'final_score'; - // CI model didn't specify timestamps behavior; table includes created_at/updated_at. + // legacy model didn't specify timestamps behavior; table includes created_at/updated_at. // If you want Laravel to auto-manage them, keep true (recommended). public $timestamps = true; @@ -51,7 +51,7 @@ class FinalScore extends BaseModel } /** - * Equivalent of CI getFinalExamScore() + * Equivalent of legacy getFinalExamScore() */ public static function getFinalExamScore(int $studentId, string $semester, string $schoolYear) { diff --git a/app/Models/GradingLock.php b/app/Models/GradingLock.php index 4d05a2c7..314135a9 100644 --- a/app/Models/GradingLock.php +++ b/app/Models/GradingLock.php @@ -8,7 +8,7 @@ class GradingLock extends BaseModel { protected $table = 'grading_locks'; - // ✅ CI: useTimestamps = true + // ✅ legacy: useTimestamps = true public $timestamps = true; protected $fillable = [ @@ -41,7 +41,7 @@ class GradingLock extends BaseModel } /** - * Equivalent of CI getLock() + * Equivalent of legacy getLock() */ public static function getLock(int $classSectionId, string $semester, string $schoolYear): ?self { @@ -58,7 +58,7 @@ class GradingLock extends BaseModel } /** - * Equivalent of CI isLocked() + * Equivalent of legacy isLocked() */ public static function isLocked(int $classSectionId, string $semester, string $schoolYear): bool { diff --git a/app/Models/Homework.php b/app/Models/Homework.php index c4bf4271..47d5cf77 100644 --- a/app/Models/Homework.php +++ b/app/Models/Homework.php @@ -10,7 +10,7 @@ class Homework extends BaseModel protected $table = 'homework'; /** - * CI: useTimestamps = false (even though created_at/updated_at columns exist). + * legacy: useTimestamps = false (even though created_at/updated_at columns exist). * Keep it OFF to match behavior. If you want Laravel to auto-manage, tell me. */ public $timestamps = false; @@ -55,7 +55,7 @@ class Homework extends BaseModel } /** - * Laravel equivalent of CI getAverageHomeworkScore() + * Laravel equivalent of legacy getAverageHomeworkScore() * Computes average based only on non-blank numeric scores. */ public static function getAverageHomeworkScore( diff --git a/app/Models/InventoryCategory.php b/app/Models/InventoryCategory.php index ccf42178..eb0cfbcb 100644 --- a/app/Models/InventoryCategory.php +++ b/app/Models/InventoryCategory.php @@ -8,7 +8,7 @@ class InventoryCategory extends BaseModel { protected $table = 'inventory_categories'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -27,7 +27,7 @@ class InventoryCategory extends BaseModel ]; /** - * Equivalent of CI optionsForType() + * Equivalent of legacy optionsForType() * Returns array rows: id, name, description, grade_min, grade_max */ public static function optionsForType(string $type): array diff --git a/app/Models/InventoryItem.php b/app/Models/InventoryItem.php index d46b4be7..51c0dbbb 100644 --- a/app/Models/InventoryItem.php +++ b/app/Models/InventoryItem.php @@ -8,7 +8,7 @@ class InventoryItem extends BaseModel { protected $table = 'inventory_items'; - // ✅ CI: useTimestamps = true + // ✅ legacy: useTimestamps = true public $timestamps = true; protected $fillable = [ diff --git a/app/Models/InventoryMovement.php b/app/Models/InventoryMovement.php index 263c58a0..b6bdeda9 100644 --- a/app/Models/InventoryMovement.php +++ b/app/Models/InventoryMovement.php @@ -8,7 +8,7 @@ class InventoryMovement extends BaseModel { protected $table = 'inventory_movements'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ceae0d96..f918522c 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -11,7 +11,7 @@ class Invoice extends BaseModel protected $table = 'invoices'; /** - * CI: useTimestamps = false because DB defaults/triggers manage created_at/updated_at. + * legacy: useTimestamps = false because DB defaults/triggers manage created_at/updated_at. * In Laravel, keep timestamps OFF and let DB handle them. */ public $timestamps = false; @@ -67,7 +67,7 @@ class Invoice extends BaseModel } /* ========================= - * CI beforeInsert/beforeUpdate equivalents (normalize to UTC) + * legacy beforeInsert/beforeUpdate equivalents (normalize to UTC) * ========================= */ protected static function booted(): void @@ -135,7 +135,7 @@ class Invoice extends BaseModel } /* ========================= - * Queries (ported from CI) + * Queries (ported from legacy) * ========================= */ public static function getInvoicesByUserId(int $userId, ?string $schoolYear = null): array diff --git a/app/Models/InvoiceEvent.php b/app/Models/InvoiceEvent.php index 4c7e6e91..8613000b 100644 --- a/app/Models/InvoiceEvent.php +++ b/app/Models/InvoiceEvent.php @@ -8,7 +8,7 @@ class InvoiceEvent extends BaseModel { protected $table = 'invoice_event'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/InvoiceStudentList.php b/app/Models/InvoiceStudentList.php index fd565640..256bd20b 100644 --- a/app/Models/InvoiceStudentList.php +++ b/app/Models/InvoiceStudentList.php @@ -8,7 +8,7 @@ class InvoiceStudentList extends BaseModel { protected $table = 'invoice_students_list'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/IpAttempt.php b/app/Models/IpAttempt.php index fc7e3616..b2a4175d 100644 --- a/app/Models/IpAttempt.php +++ b/app/Models/IpAttempt.php @@ -8,7 +8,7 @@ class IpAttempt extends BaseModel { protected $table = 'ip_attempts'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -27,7 +27,7 @@ class IpAttempt extends BaseModel ]; /** - * Equivalent of CI getAttemptByIp() + * Equivalent of legacy getAttemptByIp() */ public static function getAttemptByIp(string $ip): ?self { @@ -37,7 +37,7 @@ class IpAttempt extends BaseModel } /** - * Equivalent of CI updateIpAttempt() + * Equivalent of legacy updateIpAttempt() */ public static function updateIpAttempt(string $ip, array $data): bool { @@ -47,7 +47,7 @@ class IpAttempt extends BaseModel } /** - * Equivalent of CI insertIpAttempt() + * Equivalent of legacy insertIpAttempt() * Returns inserted id. */ public static function insertIpAttempt(array $data): int diff --git a/app/Models/LateSlipLog.php b/app/Models/LateSlipLog.php index 3c5d433e..c8103abd 100644 --- a/app/Models/LateSlipLog.php +++ b/app/Models/LateSlipLog.php @@ -10,7 +10,7 @@ class LateSlipLog extends BaseModel { protected $table = 'late_slip_logs'; - // CI: useTimestamps = false (printed_at is controlled explicitly) + // legacy: useTimestamps = false (printed_at is controlled explicitly) public $timestamps = false; protected $fillable = [ diff --git a/app/Models/LoginActivity.php b/app/Models/LoginActivity.php index b4304e46..a2995895 100644 --- a/app/Models/LoginActivity.php +++ b/app/Models/LoginActivity.php @@ -8,7 +8,7 @@ class LoginActivity extends BaseModel { protected $table = 'login_activity'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -37,7 +37,7 @@ class LoginActivity extends BaseModel } /** - * Equivalent of CI getLastActivities() + * Equivalent of legacy getLastActivities() */ public static function getLastActivities(int $limit = 4) { diff --git a/app/Models/ManualPayment.php b/app/Models/ManualPayment.php index a2a43fbf..a5edf9e9 100644 --- a/app/Models/ManualPayment.php +++ b/app/Models/ManualPayment.php @@ -8,7 +8,7 @@ class ManualPayment extends BaseModel { protected $table = 'manual_payments'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = false; protected $fillable = [ diff --git a/app/Models/Message.php b/app/Models/Message.php index 7a00a138..ed7dcbdc 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -8,7 +8,7 @@ class Message extends BaseModel { protected $table = 'messages'; - // CI: useTimestamps = false (manual date fields) + // legacy: useTimestamps = false (manual date fields) public $timestamps = false; protected $fillable = [ @@ -49,7 +49,7 @@ class Message extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ public static function getUnreadMessages(int $recipientId) diff --git a/app/Models/MidtermExam.php b/app/Models/MidtermExam.php index 20670868..9f5ef766 100644 --- a/app/Models/MidtermExam.php +++ b/app/Models/MidtermExam.php @@ -9,7 +9,7 @@ class MidtermExam extends BaseModel protected $table = 'midterm_exam'; /** - * CI: useTimestamps = false (even though created_at/updated_at columns exist). + * legacy: useTimestamps = false (even though created_at/updated_at columns exist). * Keep it OFF to match behavior (you can still store created_at/updated_at manually). */ public $timestamps = false; @@ -52,7 +52,7 @@ class MidtermExam extends BaseModel } /** - * Equivalent of CI getMidtermExamScore() + * Equivalent of legacy getMidtermExamScore() */ public static function getMidtermExamScore( int $studentId, diff --git a/app/Models/MissingScoreOverride.php b/app/Models/MissingScoreOverride.php index 7339e963..cc0bf55e 100644 --- a/app/Models/MissingScoreOverride.php +++ b/app/Models/MissingScoreOverride.php @@ -9,7 +9,7 @@ class MissingScoreOverride extends BaseModel { protected $table = 'missing_score_overrides'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -34,7 +34,7 @@ class MissingScoreOverride extends BaseModel ]; /** - * Equivalent of CI getOverridesMap() + * Equivalent of legacy getOverridesMap() * Returns: [student_id => [item_index => true]] */ public static function getOverridesMap(int $classSectionId, string $semester, string $schoolYear, string $itemType): array @@ -65,7 +65,7 @@ class MissingScoreOverride extends BaseModel } /** - * Equivalent of CI replaceOverrides() + * Equivalent of legacy replaceOverrides() * Deletes matching overrides then inserts checked items. */ public static function replaceOverrides( @@ -109,7 +109,7 @@ class MissingScoreOverride extends BaseModel if (!empty($itemIndexes)) { $q->whereIn('item_index', array_map('intval', $itemIndexes)); } else { - // CI behavior: if itemIndexes is provided but empty, do nothing. + // legacy behavior: if itemIndexes is provided but empty, do nothing. return; } } elseif ($nullIndex) { diff --git a/app/Models/NavItem.php b/app/Models/NavItem.php index 9126522e..89846a42 100644 --- a/app/Models/NavItem.php +++ b/app/Models/NavItem.php @@ -9,7 +9,7 @@ class NavItem extends BaseModel { protected $table = 'nav_items'; - // ✅ CI: timestamps + soft deletes + // ✅ legacy: timestamps + soft deletes use SoftDeletes; public $timestamps = true; @@ -41,8 +41,8 @@ class NavItem extends BaseModel } /** - * Equivalent of CI getChildrenOf() - * NOTE: CI code uses where('parent_id', ...) but allowedFields has menu_parent_id. + * Equivalent of legacy getChildrenOf() + * NOTE: legacy code uses where('parent_id', ...) but allowedFields has menu_parent_id. * This Laravel version uses menu_parent_id (likely correct). */ public static function getChildrenOf(int $parentId): array diff --git a/app/Models/Notification.php b/app/Models/Notification.php index 9011dbc7..15340ecc 100644 --- a/app/Models/Notification.php +++ b/app/Models/Notification.php @@ -9,7 +9,7 @@ class Notification extends BaseModel { protected $table = 'notifications'; - // ✅ CI: soft deletes + timestamps + // ✅ legacy: soft deletes + timestamps use SoftDeletes; public $timestamps = true; @@ -35,7 +35,7 @@ class Notification extends BaseModel ]; /** - * Equivalent of CI getActiveNotifications() + * Equivalent of legacy getActiveNotifications() * Active = scheduled_at <= now AND (expires_at is null OR expires_at > now) */ public static function getActiveNotifications(?string $targetGroup = null): array @@ -58,7 +58,7 @@ class Notification extends BaseModel } /** - * Equivalent of CI getDeletedNotifications() + * Equivalent of legacy getDeletedNotifications() */ public static function getDeletedNotifications(): array { @@ -69,7 +69,7 @@ class Notification extends BaseModel } /** - * Equivalent of CI getAllNotificationsWithDeleted() + * Equivalent of legacy getAllNotificationsWithDeleted() */ public static function getAllNotificationsWithDeleted(): array { @@ -80,7 +80,7 @@ class Notification extends BaseModel } /** - * Equivalent of CI restoreNotification() + * Equivalent of legacy restoreNotification() */ public static function restoreNotification(int $id): bool { @@ -90,7 +90,7 @@ class Notification extends BaseModel } /** - * Equivalent of CI deleteExpiredNotifications() + * Equivalent of legacy deleteExpiredNotifications() * Soft-deletes rows with expires_at < now (and expires_at not null) */ public static function deleteExpiredNotifications(): int diff --git a/app/Models/ParentAttendanceReport.php b/app/Models/ParentAttendanceReport.php index d03e2ee9..d559d052 100644 --- a/app/Models/ParentAttendanceReport.php +++ b/app/Models/ParentAttendanceReport.php @@ -9,7 +9,7 @@ class ParentAttendanceReport extends BaseModel { protected $table = 'parent_attendance_reports'; - // ✅ CI: useTimestamps = true + // ✅ legacy: useTimestamps = true public $timestamps = true; protected $fillable = [ @@ -51,7 +51,7 @@ class ParentAttendanceReport extends BaseModel } /** - * Laravel equivalent of CI listForDateRange() + * Laravel equivalent of legacy listForDateRange() * Returns array rows with: * parent_attendance_reports.* + students firstname/lastname + classSection class_section_name */ diff --git a/app/Models/ParentMeetingSchedule.php b/app/Models/ParentMeetingSchedule.php index 06a3e601..b0d68924 100644 --- a/app/Models/ParentMeetingSchedule.php +++ b/app/Models/ParentMeetingSchedule.php @@ -8,7 +8,7 @@ class ParentMeetingSchedule extends BaseModel { protected $table = 'parent_meeting_schedules'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/ParentModel.php b/app/Models/ParentModel.php index ec5c1895..1313ffad 100644 --- a/app/Models/ParentModel.php +++ b/app/Models/ParentModel.php @@ -8,7 +8,7 @@ class ParentModel extends BaseModel { protected $table = 'parents'; - // CI model didn't specify timestamps + // legacy model didn't specify timestamps public $timestamps = true; protected $fillable = [ diff --git a/app/Models/ParentNotification.php b/app/Models/ParentNotification.php index d595e484..d09c6b94 100644 --- a/app/Models/ParentNotification.php +++ b/app/Models/ParentNotification.php @@ -8,7 +8,7 @@ class ParentNotification extends BaseModel { protected $table = 'parent_notifications'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -26,7 +26,7 @@ class ParentNotification extends BaseModel protected $casts = [ 'student_id' => 'integer', - // incident_date is stored as Y-m-d in CI usage + // incident_date is stored as Y-m-d in legacy usage 'incident_date' => 'date', ]; @@ -37,7 +37,7 @@ class ParentNotification extends BaseModel } /** - * Equivalent of CI hasSent() + * Equivalent of legacy hasSent() */ public static function hasSent( int $studentId, diff --git a/app/Models/Participation.php b/app/Models/Participation.php index 5fee86c1..29c129e3 100644 --- a/app/Models/Participation.php +++ b/app/Models/Participation.php @@ -9,7 +9,7 @@ class Participation extends BaseModel protected $table = 'participation'; /** - * CI: useTimestamps = false (even though created_at/updated_at columns exist). + * legacy: useTimestamps = false (even though created_at/updated_at columns exist). * Keep OFF to match behavior (you can still set created_at/updated_at manually). */ public $timestamps = false; @@ -52,7 +52,7 @@ class Participation extends BaseModel } /** - * Equivalent of CI getParticipationScore() + * Equivalent of legacy getParticipationScore() * Returns null if missing/blank/non-numeric. */ public static function getParticipationScore( diff --git a/app/Models/PasswordReset.php b/app/Models/PasswordReset.php index 4469abe1..7857f199 100644 --- a/app/Models/PasswordReset.php +++ b/app/Models/PasswordReset.php @@ -9,7 +9,7 @@ class PasswordReset extends BaseModel protected $table = 'password_resets'; /** - * In CI you used: + * In legacy you used: * - createdField = created_at * - updatedField = expires_at (non-standard) * diff --git a/app/Models/PasswordResetRequest.php b/app/Models/PasswordResetRequest.php index 30eb945a..f424b1a1 100644 --- a/app/Models/PasswordResetRequest.php +++ b/app/Models/PasswordResetRequest.php @@ -8,7 +8,7 @@ class PasswordResetRequest extends BaseModel { protected $table = 'password_reset_requests'; - // CI: timestamps off + // legacy: timestamps off public $timestamps = true; protected $fillable = [ diff --git a/app/Models/PayPalPayment.php b/app/Models/PayPalPayment.php index f69263fd..25586958 100644 --- a/app/Models/PayPalPayment.php +++ b/app/Models/PayPalPayment.php @@ -9,7 +9,7 @@ class PayPalPayment extends BaseModel protected $table = 'paypal_payments'; /** - * CI: created_at is managed, updated_at is NOT used. + * legacy: created_at is managed, updated_at is NOT used. * In Laravel: enable timestamps but disable UPDATED_AT. */ public $timestamps = true; diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 0e0279b1..e57892a2 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -10,7 +10,7 @@ class Payment extends BaseModel protected $table = 'payments'; /** - * CI: useTimestamps = false because DB handles created_at/updated_at (defaults/triggers). + * legacy: useTimestamps = false because DB handles created_at/updated_at (defaults/triggers). * Keep OFF in Laravel too. */ public $timestamps = false; @@ -58,7 +58,7 @@ class Payment extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ public static function getPaymentsByParentId(int $parentId): array @@ -135,7 +135,7 @@ class Payment extends BaseModel /** * Get latest payment (date/amount/status) per invoice for given invoice IDs. - * Matches your CI logic: statuses in ['Full','Paid','Partially Paid']. + * Matches your legacy logic: statuses in ['Full','Paid','Partially Paid']. */ public static function getPaymentsByInvoice($invoiceIds): array { diff --git a/app/Models/PaymentError.php b/app/Models/PaymentError.php index 63570d54..753b7a34 100644 --- a/app/Models/PaymentError.php +++ b/app/Models/PaymentError.php @@ -8,7 +8,7 @@ class PaymentError extends BaseModel { protected $table = 'payment_error'; - // CI: useTimestamps = false (logged_at managed manually) + // legacy: useTimestamps = false (logged_at managed manually) public $timestamps = false; protected $fillable = [ diff --git a/app/Models/PaymentNotificationLog.php b/app/Models/PaymentNotificationLog.php index e5c5ea54..128d0738 100644 --- a/app/Models/PaymentNotificationLog.php +++ b/app/Models/PaymentNotificationLog.php @@ -8,7 +8,7 @@ class PaymentNotificationLog extends BaseModel { protected $table = 'payment_notification_logs'; - // CI: useTimestamps = false (created_at / sent_at are managed manually) + // legacy: useTimestamps = false (created_at / sent_at are managed manually) public $timestamps = false; protected $fillable = [ @@ -53,7 +53,7 @@ class PaymentNotificationLog extends BaseModel } /** - * Equivalent of CI existsForPeriod() + * Equivalent of legacy existsForPeriod() */ public static function existsForPeriod(int $parentId, int $year, int $month, string $type): bool { @@ -66,7 +66,7 @@ class PaymentNotificationLog extends BaseModel } /** - * Equivalent of CI listLogs() + * Equivalent of legacy listLogs() * $from/$to should be DATETIME strings. */ public static function listLogs(?string $from = null, ?string $to = null, ?string $type = null): array diff --git a/app/Models/PaymentTransaction.php b/app/Models/PaymentTransaction.php index 2e79d976..198369e9 100644 --- a/app/Models/PaymentTransaction.php +++ b/app/Models/PaymentTransaction.php @@ -9,7 +9,7 @@ class PaymentTransaction extends BaseModel { protected $table = 'payment_transactions'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -43,7 +43,7 @@ class PaymentTransaction extends BaseModel } /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ public static function getTransactionsByPaymentId(int $paymentId): array diff --git a/app/Models/PaypalTransaction.php b/app/Models/PaypalTransaction.php index e663ff4b..1e4b2138 100644 --- a/app/Models/PaypalTransaction.php +++ b/app/Models/PaypalTransaction.php @@ -8,7 +8,7 @@ class PaypalTransaction extends BaseModel { protected $table = 'paypal_transactions'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Permission.php b/app/Models/Permission.php index df2deaa3..51c6ff03 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -10,7 +10,7 @@ class Permission extends BaseModel { protected $table = 'permissions'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ @@ -21,7 +21,7 @@ class Permission extends BaseModel ]; /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ /** @@ -39,7 +39,7 @@ class Permission extends BaseModel /** * Fetch permissions assigned to a specific role. - * Returns rows similar to CI: + * Returns rows similar to legacy: * role_permissions.* + permissions.name */ public static function getRolePermissions(int $roleId): array diff --git a/app/Models/PlacementBatch.php b/app/Models/PlacementBatch.php index 93477622..1cdc517c 100644 --- a/app/Models/PlacementBatch.php +++ b/app/Models/PlacementBatch.php @@ -8,7 +8,7 @@ class PlacementBatch extends BaseModel { protected $table = 'placement_batches'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/PlacementLevel.php b/app/Models/PlacementLevel.php index 353f6564..17155cec 100644 --- a/app/Models/PlacementLevel.php +++ b/app/Models/PlacementLevel.php @@ -8,7 +8,7 @@ class PlacementLevel extends BaseModel { protected $table = 'placement_levels'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/PlacementScore.php b/app/Models/PlacementScore.php index 90dc1ee7..21f41b3f 100644 --- a/app/Models/PlacementScore.php +++ b/app/Models/PlacementScore.php @@ -8,7 +8,7 @@ class PlacementScore extends BaseModel { protected $table = 'placement_scores'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Preferences.php b/app/Models/Preferences.php index 4af13e12..4b202b82 100644 --- a/app/Models/Preferences.php +++ b/app/Models/Preferences.php @@ -8,7 +8,7 @@ class Preferences extends BaseModel { protected $table = 'user_preferences'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/PrintRequest.php b/app/Models/PrintRequest.php index 524bd602..ef860363 100644 --- a/app/Models/PrintRequest.php +++ b/app/Models/PrintRequest.php @@ -8,7 +8,7 @@ class PrintRequest extends BaseModel { protected $table = 'print_requests'; - // ✅ CI: useTimestamps = true (created_at/updated_at) + // ✅ legacy: useTimestamps = true (created_at/updated_at) public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Project.php b/app/Models/Project.php index 55c3b836..e8df8b14 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; class Project extends BaseModel { /** - * Table name (your CI table is singular "project"). + * Table name (your legacy table is singular "project"). */ protected $table = 'project'; @@ -101,7 +101,7 @@ class Project extends BaseModel /** * Calculate average project score for a student + term (+ optional class section). * - * Enhancements vs CI version: + * Enhancements vs legacy version: * - Uses SQL AVG for performance * - Ignores NULL, empty string, and non-numeric (best-effort) values * - Returns float|null @@ -133,7 +133,7 @@ class Project extends BaseModel } /** - * Instance method (so you can keep a similar calling style to CI). + * Instance method (so you can keep a similar calling style to legacy). */ public function getAverageProjectScore( int $studentId, diff --git a/app/Models/PromotionQueue.php b/app/Models/PromotionQueue.php index 4df0381a..578799ee 100644 --- a/app/Models/PromotionQueue.php +++ b/app/Models/PromotionQueue.php @@ -108,7 +108,7 @@ class PromotionQueue extends BaseModel } /** - * Optional: keep the same CI signature (bool result). + * Optional: keep the same legacy signature (bool result). */ public static function upsertBool(array $data): bool { diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index c6d3f761..fbca9813 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -147,7 +147,7 @@ class PurchaseOrder extends BaseModel public static function rules(?int $id = null): array { - // Mirrors CI: unique purchase_orders.po_number except current id. + // Mirrors legacy: unique purchase_orders.po_number except current id. return [ 'po_number' => ['required', 'string', 'max:60', 'unique:purchase_orders,po_number,' . ($id ?? 'NULL') . ',id'], 'supplier_id' => ['required', 'integer', 'min:1', 'exists:suppliers,id'], diff --git a/app/Models/PurchaseOrderItem.php b/app/Models/PurchaseOrderItem.php index 5bca3a02..ba44d165 100644 --- a/app/Models/PurchaseOrderItem.php +++ b/app/Models/PurchaseOrderItem.php @@ -78,7 +78,7 @@ class PurchaseOrderItem extends BaseModel public static function rules(bool $updating = false): array { // if updating, you might allow partial updates (sometimes) - // keep strict by default to match CI behavior + // keep strict by default to match legacy behavior return [ 'purchase_order_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:purchase_orders,id'], 'supply_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:supplies,id'], diff --git a/app/Models/Refund.php b/app/Models/Refund.php index 092debc0..0755eebe 100644 --- a/app/Models/Refund.php +++ b/app/Models/Refund.php @@ -64,7 +64,7 @@ class Refund extends BaseModel public static function paidOutStatuses(): array { - // matches your CI whereIn(['Partial','Paid']) + // matches your legacy whereIn(['Partial','Paid']) return [self::STATUS_PARTIAL, self::STATUS_PAID]; } @@ -110,7 +110,7 @@ class Refund extends BaseModel /** * Total paid-out refunds for a parent in a specific school year (Partial + Paid). - * Mirrors the CI behavior and returns 0.00 if none. + * Mirrors the legacy behavior and returns 0.00 if none. */ public static function totalApprovedPaidOutForParentAndYear(int $parentId, string $schoolYear): float { @@ -124,7 +124,7 @@ class Refund extends BaseModel } /** - * Instance-style method if you want to keep a similar calling style to CI. + * Instance-style method if you want to keep a similar calling style to legacy. */ public function getTotalApprovedRefundByParentIdAndSchoolYear(int $parentId, string $schoolYear): float { diff --git a/app/Models/Role.php b/app/Models/Role.php index e7b485a8..979b0cc3 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -44,7 +44,7 @@ class Role extends BaseModel } /* ============================================================ - * Helpers (CI-compatible) + * Helpers (legacy-compatible) * ============================================================ */ @@ -112,7 +112,7 @@ public static function getIdsByNames(array $names): array } /** - * Equivalent to CI getRoles(): returns all roles. + * Equivalent to legacy getRoles(): returns all roles. */ public static function getRoles() { diff --git a/app/Models/RolePermission.php b/app/Models/RolePermission.php index 87b2d7de..b39760ab 100644 --- a/app/Models/RolePermission.php +++ b/app/Models/RolePermission.php @@ -67,13 +67,13 @@ class RolePermission extends BaseModel } /* ============================================================ - * CI-compatible methods + * legacy-compatible methods * ============================================================ */ /** * Fetch permissions assigned to a specific role. - * (CI: getRolePermissions) + * (legacy: getRolePermissions) */ public static function getRolePermissions(int $roleId) { @@ -86,7 +86,7 @@ class RolePermission extends BaseModel } /** - * Alias (CI: getPermissionsByRole) + * Alias (legacy: getPermissionsByRole) */ public static function getPermissionsByRole(int $roleId) { @@ -95,7 +95,7 @@ class RolePermission extends BaseModel /** * Delete all role_permissions rows for role. - * (CI: deleteByRole) + * (legacy: deleteByRole) */ public static function deleteByRole(int $roleId): int { diff --git a/app/Models/SchoolClass.php b/app/Models/SchoolClass.php index 358adca8..e5ee65e4 100644 --- a/app/Models/SchoolClass.php +++ b/app/Models/SchoolClass.php @@ -8,7 +8,7 @@ class SchoolClass extends BaseModel { protected $table = 'classes'; - // CI: useTimestamps = false + // legacy: useTimestamps = false public $timestamps = false; protected $fillable = [ @@ -24,7 +24,7 @@ class SchoolClass extends BaseModel ]; /* ========================= - * CI method equivalents + * legacy method equivalents * ========================= */ public static function getAllClasses() diff --git a/app/Models/ScoreComment.php b/app/Models/ScoreComment.php index 9b5c0d54..a4a19d12 100644 --- a/app/Models/ScoreComment.php +++ b/app/Models/ScoreComment.php @@ -11,7 +11,7 @@ class ScoreComment extends BaseModel protected $table = 'score_comments'; /** - * CI: timestamps disabled (created_at handled by DB default). + * legacy: timestamps disabled (created_at handled by DB default). * Keep disabled in Laravel too. */ public $timestamps = true; diff --git a/app/Models/Section.php b/app/Models/Section.php index 13696f97..ce3a37cc 100644 --- a/app/Models/Section.php +++ b/app/Models/Section.php @@ -58,13 +58,13 @@ class Section extends BaseModel } /* ============================================================ - * CI-compatible methods + * legacy-compatible methods * ============================================================ */ /** * Get section by name. - * CI: getSectionByName() + * legacy: getSectionByName() */ public static function getSectionByName(string $sectionName): ?self { @@ -73,7 +73,7 @@ class Section extends BaseModel /** * Update section details by ID. - * CI: updateSection() (Laravel automatically updates updated_at) + * legacy: updateSection() (Laravel automatically updates updated_at) */ public static function updateSection(int $sectionId, array $data): bool { @@ -86,7 +86,7 @@ class Section extends BaseModel /** * Delete section by ID. - * CI: deleteSection() + * legacy: deleteSection() */ public static function deleteSection(int $sectionId): bool { diff --git a/app/Models/SemesterScore.php b/app/Models/SemesterScore.php index 7f44832d..45198ec3 100644 --- a/app/Models/SemesterScore.php +++ b/app/Models/SemesterScore.php @@ -134,7 +134,7 @@ class SemesterScore extends BaseModel } /** - * CI-compatible boolean wrapper (matches your CI upsert(): bool). + * legacy-compatible boolean wrapper (matches your legacy upsert(): bool). */ public static function upsertBool(array $data): bool { diff --git a/app/Models/Setting.php b/app/Models/Setting.php index e7441cc2..49c2ac72 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -55,7 +55,7 @@ class Setting extends BaseModel } /** - * CI-compatible: getSettings() + * legacy-compatible: getSettings() * Returns the settings model (single row). */ public static function getSettings(): self @@ -64,7 +64,7 @@ class Setting extends BaseModel } /** - * CI-compatible: updateSettings($data) + * legacy-compatible: updateSettings($data) * Updates the singleton row and returns bool. */ public static function updateSettings(array $data): bool diff --git a/app/Models/Staff.php b/app/Models/Staff.php index 62fa5769..49ed10db 100644 --- a/app/Models/Staff.php +++ b/app/Models/Staff.php @@ -11,7 +11,7 @@ class Staff extends BaseModel protected $table = 'staff'; /** - * CI: timestamps managed manually (created_at/updated_at set by caller/controller) + * legacy: timestamps managed manually (created_at/updated_at set by caller/controller) */ public $timestamps = false; @@ -66,7 +66,7 @@ class Staff extends BaseModel } /* ============================================================ - * CI-compatible methods + * legacy-compatible methods * ============================================================ */ @@ -104,7 +104,7 @@ class Staff extends BaseModel $userId = (int) $data['user_id']; - // Resolve school_year fallback like your CI logic + // Resolve school_year fallback like your legacy logic $schoolYear = $data['school_year'] ?? null; if ($schoolYear === null) { $existingAny = static::query() @@ -177,7 +177,7 @@ class Staff extends BaseModel } /** - * CI-compatible boolean wrapper if you want the same signature. + * legacy-compatible boolean wrapper if you want the same signature. */ public static function upsertBool(array $data): bool { diff --git a/app/Models/StaffAttendance.php b/app/Models/StaffAttendance.php index deefa120..c44d94c9 100644 --- a/app/Models/StaffAttendance.php +++ b/app/Models/StaffAttendance.php @@ -248,7 +248,7 @@ class StaffAttendance extends BaseModel /* ============================================================ * Staff list with status for a day (users + roles + staff_attendance) - * Mirrors your CI staffWithStatusForDay() + * Mirrors your legacy staffWithStatusForDay() * ============================================================ */ public static function staffWithStatusForDay( diff --git a/app/Models/Stats.php b/app/Models/Stats.php index cf1f3237..c8e3bf07 100644 --- a/app/Models/Stats.php +++ b/app/Models/Stats.php @@ -34,7 +34,7 @@ class Stats extends BaseModel } /** - * CI-compatible: getStats() + * legacy-compatible: getStats() * Returns all rows. */ public static function getStats() diff --git a/app/Models/Student.php b/app/Models/Student.php index 31fb9924..2ec699a4 100644 --- a/app/Models/Student.php +++ b/app/Models/Student.php @@ -15,7 +15,7 @@ class Student extends BaseModel protected $table = 'students'; /** - * CI: timestamps disabled + * legacy: timestamps disabled */ public $timestamps = false; @@ -106,11 +106,11 @@ class Student extends BaseModel } /* ============================================================ - * CI-compatible methods (converted) + * legacy-compatible methods (converted) * ============================================================ */ - /** CI: getNewStudents() */ + /** legacy: getNewStudents() */ public static function getNewStudents(): array { return static::query() @@ -120,7 +120,7 @@ class Student extends BaseModel ->all(); } - /** CI: getNewStudentsWithParents() */ + /** legacy: getNewStudentsWithParents() */ public static function getNewStudentsWithParents(): array { return DB::table('students') @@ -142,7 +142,7 @@ class Student extends BaseModel } /** - * CI: getNewStudentsWithParentsAndEmergency() + * legacy: getNewStudentsWithParentsAndEmergency() * Picks one emergency contact via MIN(...) with groupBy student. */ public static function getNewStudentsWithParentsAndEmergency(): array @@ -170,7 +170,7 @@ class Student extends BaseModel } /** - * CI: getStudentsWithParentsAndEmergency(?schoolYear, ?isNew) + * legacy: getStudentsWithParentsAndEmergency(?schoolYear, ?isNew) */ public static function getStudentsWithParentsAndEmergency(?string $schoolYear = null, ?int $isNew = null): array { @@ -209,26 +209,26 @@ class Student extends BaseModel ->all(); } - /** CI: getStudentSchoolIdByStudentId() */ + /** legacy: getStudentSchoolIdByStudentId() */ public static function getStudentSchoolIdByStudentId(int $studentId): ?string { $v = static::query()->where('id', $studentId)->value('school_id'); return $v !== null ? (string) $v : null; } - /** CI: getAllStudents() */ + /** legacy: getAllStudents() */ public static function getAllStudents() { return static::query()->get(); } - /** CI: getTotalStudents() */ + /** legacy: getTotalStudents() */ public static function getTotalStudents(): int { return (int) static::query()->count(); } - /** CI: getFullNameById() => returns [firstname, lastname] or null */ + /** legacy: getFullNameById() => returns [firstname, lastname] or null */ public static function getFullNameById($studentId): ?array { $student = static::query()->find($studentId); @@ -242,13 +242,13 @@ class Student extends BaseModel return ['firstname' => $first, 'lastname' => $last]; } - /** CI: getFullName($student array) */ + /** legacy: getFullName($student array) */ public static function getFullName(array $student): string { return trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')); } - /** CI: getLastRegistrationDate($ParentId) */ + /** legacy: getLastRegistrationDate($ParentId) */ public static function getLastRegistrationDate(int $parentId): ?array { $row = static::query() @@ -261,7 +261,7 @@ class Student extends BaseModel } /** - * CI: getByClassAndYear($class_section_id, $school_year, ?semester ignored) + * legacy: getByClassAndYear($class_section_id, $school_year, ?semester ignored) */ public static function getByClassAndYear(int $classSectionId, string $schoolYear, ?string $semester = null): array { @@ -311,7 +311,7 @@ class Student extends BaseModel } /** - * CI: getStudentsWithClassAndEnrollment() + * legacy: getStudentsWithClassAndEnrollment() * Keeps your original grouping so behavior doesn’t change. */ public static function getStudentsWithClassAndEnrollment(): array @@ -335,8 +335,8 @@ class Student extends BaseModel } /** - * CI: getStudentInfoByClassSectionId($classSectionId, $semester ignored, $schoolYear optional) - * NOTE: In CI you appended updated_by from session; here we accept actor id. + * legacy: getStudentInfoByClassSectionId($classSectionId, $semester ignored, $schoolYear optional) + * NOTE: In legacy you appended updated_by from session; here we accept actor id. */ public static function getStudentInfoByClassSectionId(int $classSectionId, ?string $semester = null, ?string $schoolYear = null, ?int $actorId = null): array { diff --git a/app/Models/StudentAllergy.php b/app/Models/StudentAllergy.php index d0d530ab..3f8c9765 100644 --- a/app/Models/StudentAllergy.php +++ b/app/Models/StudentAllergy.php @@ -11,7 +11,7 @@ class StudentAllergy extends BaseModel protected $table = 'student_allergies'; /** - * CI: timestamps disabled + * legacy: timestamps disabled */ public $timestamps = true; @@ -51,13 +51,13 @@ class StudentAllergy extends BaseModel } /* ============================================================ - * CI-compatible methods + * legacy-compatible methods * ============================================================ */ /** * Get all allergies for a given student. - * CI: getAllergiesByStudentId() + * legacy: getAllergiesByStudentId() */ public static function getAllergiesByStudentId(int $studentId): array { @@ -70,7 +70,7 @@ class StudentAllergy extends BaseModel /** * Get allergies for multiple students, grouped by student_id. - * CI: getAllergiesByStudentIds() + * legacy: getAllergiesByStudentIds() */ public static function getAllergiesByStudentIds(array $studentIds): array { @@ -93,7 +93,7 @@ class StudentAllergy extends BaseModel /* ============================================================ * Validation rules helper (FormRequest/controller) - * Mirrors CI validation intent + * Mirrors legacy validation intent * ============================================================ */ diff --git a/app/Models/StudentClass.php b/app/Models/StudentClass.php index 670cdceb..70bd4d2d 100644 --- a/app/Models/StudentClass.php +++ b/app/Models/StudentClass.php @@ -47,7 +47,7 @@ class StudentClass extends BaseModel public function classSection(): BelongsTo { - // Your CI joins on classSection.class_section_id + // Your legacy joins on classSection.class_section_id return $this->belongsTo(ClassSection::class, 'class_section_id', 'class_section_id'); } @@ -68,7 +68,7 @@ class StudentClass extends BaseModel /** * Scope: only students active for classes/attendance (joins students). - * Mirrors CI active(). + * Mirrors legacy active(). */ public function scopeActive(Builder $q): Builder { @@ -94,7 +94,7 @@ class StudentClass extends BaseModel } /* ============================================================ - * CI-compatible methods (rewritten Laravel-style) + * legacy-compatible methods (rewritten Laravel-style) * ============================================================ */ @@ -195,7 +195,7 @@ class StudentClass extends BaseModel /** * Mirrors getStudentsByClassSectionIds() - * Returns array rows (not models) like CI. + * Returns array rows (not models) like legacy. */ public static function getStudentsByClassSectionIds(array $classSectionIds): array { diff --git a/app/Models/StudentMedicalCondition.php b/app/Models/StudentMedicalCondition.php index 466b0fa1..262cb527 100644 --- a/app/Models/StudentMedicalCondition.php +++ b/app/Models/StudentMedicalCondition.php @@ -11,7 +11,7 @@ class StudentMedicalCondition extends BaseModel protected $table = 'student_medical_conditions'; /** - * CI: timestamps disabled + * legacy: timestamps disabled */ public $timestamps = true; @@ -51,13 +51,13 @@ class StudentMedicalCondition extends BaseModel } /* ============================================================ - * CI-compatible methods + * legacy-compatible methods * ============================================================ */ /** * Get all medical conditions for a given student. - * CI: getMedicalByStudentId() + * legacy: getMedicalByStudentId() */ public static function getMedicalByStudentId(int $studentId): array { @@ -70,7 +70,7 @@ class StudentMedicalCondition extends BaseModel /** * Get medical conditions for multiple students, grouped by student_id. - * CI: getMedicalByStudentIds() + * legacy: getMedicalByStudentIds() */ public static function getMedicalByStudentIds(array $studentIds): array { @@ -91,7 +91,7 @@ class StudentMedicalCondition extends BaseModel /* ============================================================ * Validation helper (FormRequest/controller) - * Mirrors CI rules/messages + * Mirrors legacy rules/messages * ============================================================ */ diff --git a/app/Models/SubjectCurriculum.php b/app/Models/SubjectCurriculum.php index fb2b168a..e8cefce2 100644 --- a/app/Models/SubjectCurriculum.php +++ b/app/Models/SubjectCurriculum.php @@ -61,12 +61,12 @@ class SubjectCurriculum extends BaseModel } /* ============================================================ - * CI-compatible method + * legacy-compatible method * ============================================================ */ /** - * CI: getOptionsForClass($classId, $subject) + * legacy: getOptionsForClass($classId, $subject) */ public static function getOptionsForClass(int $classId, string $subject): array { diff --git a/app/Models/TeacherClass.php b/app/Models/TeacherClass.php index c8c48e43..e6c03743 100644 --- a/app/Models/TeacherClass.php +++ b/app/Models/TeacherClass.php @@ -56,7 +56,7 @@ class TeacherClass extends BaseModel public function classSection(): BelongsTo { - // CI frequently joins on classSection.class_section_id + // legacy frequently joins on classSection.class_section_id return $this->belongsTo(ClassSection::class, 'class_section_id', 'class_section_id'); } @@ -95,18 +95,18 @@ class TeacherClass extends BaseModel } /* ============================================================ - * CI-compatible methods (converted) + * legacy-compatible methods (converted) * ============================================================ */ - /** CI: getClassSectionIdByUserId() */ + /** legacy: getClassSectionIdByUserId() */ public static function getClassSectionIdByUserId(int $userId): ?self { return static::query()->forTeacher($userId)->first(); } /** - * CI: getClassAssignmentsByUserId() + * legacy: getClassAssignmentsByUserId() * Returns array rows with: * class_section_pk, class_section_id, class_section_name, class_id, class_name, teacher_id, teacher_role, school_year, semester */ @@ -171,7 +171,7 @@ class TeacherClass extends BaseModel ->all(); } - /** CI: getClassSectionsByTeacherId() */ + /** legacy: getClassSectionsByTeacherId() */ public static function getClassSectionsByTeacherId(int $teacherId): array { $rows = DB::table('teacher_class as tc') @@ -194,7 +194,7 @@ class TeacherClass extends BaseModel ]; } - /** CI: getClassByTeacherId() */ + /** legacy: getClassByTeacherId() */ public static function getClassByTeacherId(int $teacherId): array { return DB::table('teacher_class as tc') @@ -207,7 +207,7 @@ class TeacherClass extends BaseModel ->all(); } - /** CI: getTeacherIdByClassSection($classSectionName, $semester, $schoolYear) */ + /** legacy: getTeacherIdByClassSection($classSectionName, $semester, $schoolYear) */ public static function getTeacherIdByClassSection(string $classSectionName, string $semester, string $schoolYear): ?int { $classSectionName = trim($classSectionName); @@ -232,12 +232,12 @@ class TeacherClass extends BaseModel return $teacherId ? (int) $teacherId : null; } - /** CI: getMainTeacherBySection($classSectionName, $semester, $schoolYear) */ + /** legacy: getMainTeacherBySection($classSectionName, $semester, $schoolYear) */ public static function getMainTeacherBySection(string $classSectionName, string $semester, string $schoolYear): ?array { $row = DB::table('classSection as cs') ->selectRaw('u.id AS teacher_id, u.firstname, u.lastname') - // IMPORTANT: your CI code had tc.class_section_id = cs.id in one place (PK join), + // IMPORTANT: your legacy code had tc.class_section_id = cs.id in one place (PK join), // but most of your code uses class_section_id code join. // Here we keep the "code join" for consistency: ->join('teacher_class as tc', 'tc.class_section_id', '=', 'cs.class_section_id') @@ -251,7 +251,7 @@ class TeacherClass extends BaseModel } /** - * CI: assignTeacherToClass() + * legacy: assignTeacherToClass() * Upsert by (teacher_id, class_section_id, school_year, position) and touch updated_at. */ public static function assignTeacherToClass( @@ -286,8 +286,8 @@ class TeacherClass extends BaseModel } /** - * CI: getAssignedClassSections() - * NOTE: your CI joins classSection.id = teacher_class.class_section_id (PK join). + * legacy: getAssignedClassSections() + * NOTE: your legacy joins classSection.id = teacher_class.class_section_id (PK join). * If your teacher_class.class_section_id is actually a section CODE, change join to cs.class_section_id. */ public static function getAssignedClassSections(int $teacherId, string $schoolYear, string $semester): array @@ -304,7 +304,7 @@ class TeacherClass extends BaseModel } /** - * CI: assignedForSectionTerm() + * legacy: assignedForSectionTerm() * Returns rows: class_section_id, teacher_id, position, firstname, lastname * * @param int|null $alsoSectionCode When teacher_class rows use the section PK while $sectionCode @@ -347,7 +347,7 @@ class TeacherClass extends BaseModel ->all(); } - /** CI: assignedMapForSectionTerm() */ + /** legacy: assignedMapForSectionTerm() */ public static function assignedMapForSectionTerm( int $sectionCode, string $semester, @@ -363,7 +363,7 @@ class TeacherClass extends BaseModel } /** - * CI: assignedBySectionForTerm() + * legacy: assignedBySectionForTerm() * Returns: [sectionCode] => [rows...] */ public static function assignedBySectionForTerm(string $semester, string $schoolYear, ?array $onlySectionCodes = null): array diff --git a/app/Models/TeacherSubmissionNotificationHistory.php b/app/Models/TeacherSubmissionNotificationHistory.php index 55ddbc7a..294ac2fe 100644 --- a/app/Models/TeacherSubmissionNotificationHistory.php +++ b/app/Models/TeacherSubmissionNotificationHistory.php @@ -11,7 +11,7 @@ class TeacherSubmissionNotificationHistory extends BaseModel protected $table = 'teacher_submission_notification_history'; /** - * CI: timestamps disabled (table uses sent_at) + * legacy: timestamps disabled (table uses sent_at) */ public $timestamps = false; diff --git a/app/Models/User.php b/app/Models/User.php index c7e11a45..eb32b286 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -77,16 +77,16 @@ class User extends Authenticatable implements JWTSubject { return [ 'name' => trim(($this->firstname ?? '').' '.($this->lastname ?? '')), - 'roles' => $this->roleNamesLikeCodeIgniter(), + 'roles' => $this->roleNames(), ]; } /** - * Role names joined from `user_roles` + `roles`, matching CodeIgniter `AuthController::apiLogin` (no roles.is_active filter). + * Role names joined from `user_roles` + `roles` without filtering inactive roles. * * @return list */ - public function roleNamesLikeCodeIgniter(): array + public function roleNames(): array { return DB::table('user_roles') ->join('roles', 'roles.id', '=', 'user_roles.role_id') @@ -111,7 +111,7 @@ class User extends Authenticatable implements JWTSubject $roles = array_map( static fn ($role) => strtolower(trim((string) $role)), - $this->roleNamesLikeCodeIgniter(), + $this->roleNames(), ); $isTeacherScoped = in_array('teacher', $roles, true) @@ -256,9 +256,9 @@ class User extends Authenticatable implements JWTSubject */ /** - * CI: getUsersWithRoles($sort='id',$order='asc') + * legacy: getUsersWithRoles($sort='id',$order='asc') * Returns rows: users.id, firstname, lastname, email, roles.name as role - * NOTE: If user has multiple roles, this will return multiple rows (same as CI join). + * NOTE: If user has multiple roles, this will return multiple rows (same as legacy join). */ public static function getUsersWithRoles(string $sort = 'users.id', string $order = 'asc'): array { @@ -284,7 +284,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getUnverifiedUsers() created more than 2 minutes ago + * legacy: getUnverifiedUsers() created more than 2 minutes ago */ public static function getUnverifiedUsers(): array { @@ -296,7 +296,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: findAll($limit,$offset,$sort,$order,$conditions) + * legacy: findAll($limit,$offset,$sort,$order,$conditions) * Laravel version: returns array of rows. */ public static function findAllCustom( @@ -331,7 +331,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getUserRole($user_id) -> string|null + * legacy: getUserRole($user_id) -> string|null * Returns one role name (MIN for stability). */ public static function getUserRoleName(int $userId): ?string @@ -346,7 +346,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getAllUsers($sort,$order) + * legacy: getAllUsers($sort,$order) */ public static function getAllUsers(string $sort = 'users.id', string $order = 'asc') { @@ -355,7 +355,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getUserInfoById($userId) but safe fields (exclude password/token/is_verified) + * legacy: getUserInfoById($userId) but safe fields (exclude password/token/is_verified) * Returns array with roles. */ public static function getUserInfoById(int $userId): ?array @@ -388,7 +388,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getUsersByRole($roleName) + * legacy: getUsersByRole($roleName) */ public static function getUsersByRole(string $roleName): array { @@ -404,7 +404,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getUsersByRoleAndSchoolYear($roleName, $schoolYear) + * legacy: getUsersByRoleAndSchoolYear($roleName, $schoolYear) */ public static function getUsersByRoleAndSchoolYear(string $roleName, string $schoolYear): array { @@ -421,7 +421,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: countAdminsBySchoolYear($schoolYear) + * legacy: countAdminsBySchoolYear($schoolYear) * Excludes guest/teacher/teacher_assistant/parent. */ public static function countAdminsBySchoolYear(string $schoolYear): int @@ -447,7 +447,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getParents() + * legacy: getParents() * Finds role "parent" case-insensitively then returns grouped users. */ public static function getParents(): array @@ -470,7 +470,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getTeacher($user_id) -> teacher_first/teacher_last or null + * legacy: getTeacher($user_id) -> teacher_first/teacher_last or null */ public static function getTeacherNameParts(int $userId): ?array { @@ -496,7 +496,7 @@ class User extends Authenticatable implements JWTSubject } /** - * CI: getNoParentUsersWithRole(...) (as close as possible) + * legacy: getNoParentUsersWithRole(...) (as close as possible) * Returns: id, firstname, lastname, email, roles (comma string) */ public static function getNoParentUsersWithRole( @@ -628,7 +628,7 @@ class User extends Authenticatable implements JWTSubject * TeacherModel: getTeachersWithAssignments() * Reads config_key=current_school_year and returns current_class + school_year. * - * NOTE: your CI code joins class_section via classSection.id. + * NOTE: your legacy code joins class_section via classSection.id. * Here I join teacher_class.class_section_id => class_section.id to match that snippet. * If your real section table is classSection (not class_section), change the join accordingly. */ diff --git a/app/Models/UserNotification.php b/app/Models/UserNotification.php index 73ccd7a2..bbd394a3 100644 --- a/app/Models/UserNotification.php +++ b/app/Models/UserNotification.php @@ -11,7 +11,7 @@ class UserNotification extends BaseModel protected $table = 'user_notifications'; /** - * CI: timestamps disabled (uses delivered_at) + * legacy: timestamps disabled (uses delivered_at) */ public $timestamps = false; diff --git a/app/Models/UserRole.php b/app/Models/UserRole.php index 85497276..0c65ccce 100644 --- a/app/Models/UserRole.php +++ b/app/Models/UserRole.php @@ -73,12 +73,12 @@ class UserRole extends BaseModel } /* ============================================================ - * CI-compatible methods (converted) + * legacy-compatible methods (converted) * ============================================================ */ /** - * CI: getRolesByUserId($userId) + * legacy: getRolesByUserId($userId) * Returns: [ ['role_name' => 'parent'], ['role_name' => 'teacher'] ] */ public static function getRolesByUserId(int $userId): array @@ -96,7 +96,7 @@ class UserRole extends BaseModel } /** - * CI: updateOrInsertRole($userId, $roleId) + * legacy: updateOrInsertRole($userId, $roleId) * Idempotent (does nothing if already exists and not soft-deleted). * If the row exists but is soft-deleted, restores it. */ @@ -131,12 +131,12 @@ class UserRole extends BaseModel } /* ============================================================ - * Replacement for the CI "create()" method (query only) + * Replacement for the legacy "create()" method (query only) * ============================================================ * In Laravel, view rendering belongs in Controllers, not Models. - * This returns the user list that your CI method was building. + * This returns the user list that your legacy method was building. * - * CI logic: + * legacy logic: * users joined to roles through user_roles, excluding parent/teacher, * groupBy users.id to avoid duplicates. */ diff --git a/app/Models/WhatsappGroupLink.php b/app/Models/WhatsappGroupLink.php index 4df82633..2dcfe772 100644 --- a/app/Models/WhatsappGroupLink.php +++ b/app/Models/WhatsappGroupLink.php @@ -76,7 +76,7 @@ class WhatsappGroupLink extends BaseModel } /* ============================================================ - * Methods (CI-compatible behavior) + * Methods (legacy-compatible behavior) * ============================================================ */ @@ -161,7 +161,7 @@ class WhatsappGroupLink extends BaseModel 'active' => $active ? 1 : 0, ]; - // NOTE: semester is part of the uniqueness key like your CI version. + // NOTE: semester is part of the uniqueness key like your legacy version. return static::query()->updateOrCreate( [ 'class_section_id' => $sectionId, diff --git a/app/Models/WhatsappGroupMembership.php b/app/Models/WhatsappGroupMembership.php index 3b12d17a..6467f542 100644 --- a/app/Models/WhatsappGroupMembership.php +++ b/app/Models/WhatsappGroupMembership.php @@ -74,7 +74,7 @@ class WhatsappGroupMembership extends BaseModel } /* ============================================================ - * Methods (CI-compatible behavior) + * Methods (legacy-compatible behavior) * ============================================================ */ @@ -118,7 +118,7 @@ class WhatsappGroupMembership extends BaseModel * Upsert membership row for a specific subject and class/term. * Returns the row ID. * - * Matches CI logic: + * Matches legacy logic: * - Key is (class_section_id, school_year, subject_type, subject_id) plus semester if provided. * - If semester is blank, we update most recent row for that key and keep its semester. * - If verifiedBy provided, set verified_by + verified_at. @@ -165,7 +165,7 @@ class WhatsappGroupMembership extends BaseModel return (int) $row->id; } - // Semester blank => match CI behavior: find most recent row for the key (without semester filter). + // Semester blank => match legacy behavior: find most recent row for the key (without semester filter). $existing = static::query() ->where('class_section_id', $classSectionId) ->where('school_year', $year) @@ -176,7 +176,7 @@ class WhatsappGroupMembership extends BaseModel ->first(); if ($existing) { - // Keep semester from the existing row if present (CI behavior). + // Keep semester from the existing row if present (legacy behavior). if (!empty($existing->semester)) { $payload['semester'] = (string) $existing->semester; } diff --git a/app/Models/WhatsappInviteLog.php b/app/Models/WhatsappInviteLog.php index b06078c7..0c966510 100644 --- a/app/Models/WhatsappInviteLog.php +++ b/app/Models/WhatsappInviteLog.php @@ -11,7 +11,7 @@ class WhatsappInviteLog extends BaseModel protected $table = 'whatsapp_invites_log'; /** - * CI: timestamps disabled (we manage sent_at manually) + * legacy: timestamps disabled (we manage sent_at manually) */ public $timestamps = false; @@ -89,7 +89,7 @@ class WhatsappInviteLog extends BaseModel } /* ============================================================ - * CI-compatible helpers + * legacy-compatible helpers * ============================================================ */ @@ -140,7 +140,7 @@ class WhatsappInviteLog extends BaseModel /** * Get logs for a parent and/or section. - * CI: getLogs($parentId = null, $classSectionId = null) + * legacy: getLogs($parentId = null, $classSectionId = null) */ public static function getLogs(?int $parentId = null, ?int $classSectionId = null): array { @@ -158,7 +158,7 @@ class WhatsappInviteLog extends BaseModel /* ============================================================ * Optional validation helper (FormRequest/controller) - * Mirrors CI validation intent + * Mirrors legacy validation intent * ============================================================ */ diff --git a/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php b/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php index d02cf3d7..f8358354 100644 --- a/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php +++ b/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php @@ -12,7 +12,7 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; /** - * Administrative competition-winner workflows (ported from CodeIgniter {@see \App\Controllers\Admin\CompetitionWinnersController}). + * Administrative competition-winner workflows (ported from legacy {@see \App\Controllers\Admin\CompetitionWinnersController}). */ class CompetitionWinnersAdminService { @@ -627,7 +627,7 @@ class CompetitionWinnersAdminService } /** - * Export competition scores into quiz rows (SQL preserved from CodeIgniter). + * Export competition scores into quiz rows (SQL preserved from legacy). * * @return array{ok: bool, message: string, quizIndexes?: array} */ diff --git a/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php b/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php index 327112b7..f0a1922a 100644 --- a/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php +++ b/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php @@ -2,7 +2,7 @@ namespace App\Services\Admin\CompetitionWinners; -/** Pure domain logic ported from CodeIgniter `Admin\CompetitionWinnersController` (private helpers). */ +/** Pure domain logic ported from legacy `Admin\CompetitionWinnersController` (private helpers). */ final class CompetitionWinnersDomain { /** @var array */ diff --git a/app/Services/Attendance/StaffAttendanceService.php b/app/Services/Attendance/StaffAttendanceService.php index 378f4019..419970d3 100644 --- a/app/Services/Attendance/StaffAttendanceService.php +++ b/app/Services/Attendance/StaffAttendanceService.php @@ -22,7 +22,7 @@ class StaffAttendanceService ) {} /** - * CodeIgniter {@see \App\Controllers\View\AttendanceController::index} — single date / section teacher grid JSON. + * legacy {@see \App\Controllers\View\AttendanceController::index} — single date / section teacher grid JSON. */ public function teacherAttendanceDayIndex(?string $semester, ?string $schoolYear, string $dateYmd, int $sectionCode): array { @@ -107,7 +107,7 @@ class StaffAttendanceService } /** - * Bulk save for CI {@see \App\Controllers\View\AttendanceController::save} (method missing in CI; implemented here). + * Bulk save for legacy {@see \App\Controllers\View\AttendanceController::save} (method missing in legacy; implemented here). */ public function saveTeacherAttendanceBulk(Authenticatable $user, array $payload): array { diff --git a/app/Services/AttendanceTracking/AttendanceTrackingService.php b/app/Services/AttendanceTracking/AttendanceTrackingService.php index 0f312550..2414a8fc 100644 --- a/app/Services/AttendanceTracking/AttendanceTrackingService.php +++ b/app/Services/AttendanceTracking/AttendanceTrackingService.php @@ -75,7 +75,7 @@ class AttendanceTrackingService } /** - * CodeIgniter POST attendance/send-auto-emails — runs auto_email actions on current pending violations. + * legacy POST attendance/send-auto-emails — runs auto_email actions on current pending violations. */ public function sendAutoEmails(mixed $variantInput): array { @@ -121,7 +121,7 @@ class AttendanceTrackingService } /** - * CodeIgniter route referenced {@see View\AttendanceTrackingController::notify_parent} was never implemented in CI; + * legacy route referenced {@see View\AttendanceTrackingController::notify_parent} was never implemented in legacy; * this matches the attendance notification modal (student_id, to, subject, message, subject_type, date). */ public function notifyParent(array $input): array diff --git a/app/Services/Auth/ApiLoginSecurityService.php b/app/Services/Auth/ApiLoginSecurityService.php index 9504fde7..f73f8de5 100644 --- a/app/Services/Auth/ApiLoginSecurityService.php +++ b/app/Services/Auth/ApiLoginSecurityService.php @@ -11,8 +11,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; /** - * Mirrors CodeIgniter `AuthController::apiLogin` security behavior: - * IP lockout (ip_attempts), failed_attempts / suspension after 3 failures, login_activity logging. + * Centralizes API login lockout, suspension, and audit logging behavior. */ class ApiLoginSecurityService { @@ -79,7 +78,7 @@ class ApiLoginSecurityService if ($failedAttempts >= 3) { $data['is_suspended'] = true; - // CI sends reset email via View\UserController::sendResetEmail — wire Laravel mail here when parity is needed. + // Password-reset delivery can be wired here when the suspension workflow is finalized. Log::notice('api_login: account suspended after failed attempts', ['email' => $email, 'user_id' => $user->id]); } @@ -113,7 +112,7 @@ class ApiLoginSecurityService } /** - * CI returns roles as an object map {"RoleName": true}. + * Returns roles as an object map {"RoleName": true}. * * @param list $roleNames */ @@ -128,13 +127,13 @@ class ApiLoginSecurityService } /** - * Top-level JSON body aligned with the CodeIgniter 4 `AuthController::apiLogin` success payload. + * Top-level JSON body for API login success responses. * * @return array{status: true, token: string, user: array{id: int, name: string, roles: object, class_section_id: ?int, class_section_name: ?string}} */ public function buildLoginResponse(User $user, string $jwtToken): array { - $roleNames = $user->roleNamesLikeCodeIgniter(); + $roleNames = $user->roleNames(); $fullName = trim(($user->firstname ?? '').' '.($user->lastname ?? '')); $teacherContext = $user->teacherSessionContext(); diff --git a/app/Services/Auth/CodeIgniterApiRegistrationService.php b/app/Services/Auth/ApiRegistrationService.php similarity index 95% rename from app/Services/Auth/CodeIgniterApiRegistrationService.php rename to app/Services/Auth/ApiRegistrationService.php index e2ba7321..b0e6791b 100644 --- a/app/Services/Auth/CodeIgniterApiRegistrationService.php +++ b/app/Services/Auth/ApiRegistrationService.php @@ -9,13 +9,14 @@ use App\Models\UserRole; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Validator; use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth; /** - * Matches CodeIgniter 4 `AuthController::apiRegister` (public API register, no captcha). + * Handles direct public API registration requests that bypass the captcha flow. */ -class CodeIgniterApiRegistrationService +class ApiRegistrationService { public function registerResponse(Request $request): JsonResponse { @@ -115,7 +116,7 @@ class CodeIgniterApiRegistrationService ], ], 201); } catch (\Throwable $e) { - log_message('error', 'Registration error: '.$e->getMessage()); + Log::error('Registration error: '.$e->getMessage()); return response()->json([ 'status' => false, diff --git a/app/Services/Auth/AuthSessionService.php b/app/Services/Auth/AuthSessionService.php index 980afeb4..04cda57d 100644 --- a/app/Services/Auth/AuthSessionService.php +++ b/app/Services/Auth/AuthSessionService.php @@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; /** - * CodeIgniter `AuthController` web (session) flow: session keys, dashboards, redirects, preferences. + * Session-auth flow: session keys, dashboards, redirects, and preferences. */ class AuthSessionService { @@ -25,7 +25,7 @@ class AuthSessionService } /** - * Same-host relative redirect sanitization as CI `sanitizeRedirectTarget`. + * Sanitizes login redirects to same-host relative paths only. */ public function sanitizeRedirectTarget(string $redirectTo): ?string { @@ -84,7 +84,7 @@ class AuthSessionService } /** - * Apply CI-style preference keys into session (style/menu colors). + * Apply stored style preferences into the session. */ public function applyStylePreferencesToSession(int $userId): void { @@ -114,11 +114,11 @@ class AuthSessionService } /** - * Populate Laravel web auth + CI-compatible session keys. + * Populate Laravel web auth and session state for the web portal. * * @param list $roleNames */ - public function writeCiAuthenticatedSession(User $user, array $roleNames): void + public function writeAuthenticatedSession(User $user, array $roleNames): void { Auth::guard('web')->login($user); @@ -145,7 +145,7 @@ class AuthSessionService } /** - * Close open login_activity row(s) without logout_time for this user (CI parity). + * Close open login_activity rows without logout_time for this user. */ public function logLogout(?int $userId, ?string $email): void { @@ -169,7 +169,7 @@ class AuthSessionService */ public function resolvePostLoginDestination(User $user, ?string $sanitizedRedirect): array { - $roleNames = $user->roleNamesLikeCodeIgniter(); + $roleNames = $user->roleNames(); if ($roleNames === []) { Log::notice('session_login: user has no roles', ['user_id' => $user->id]); @@ -180,7 +180,7 @@ class AuthSessionService ]; } - $this->writeCiAuthenticatedSession($user, $roleNames); + $this->writeAuthenticatedSession($user, $roleNames); if (count($roleNames) === 1) { session()->put('role', $roleNames[0]); diff --git a/app/Services/BadgeScan/BadgeScanService.php b/app/Services/BadgeScan/BadgeScanService.php index 283e427a..d767f9e6 100644 --- a/app/Services/BadgeScan/BadgeScanService.php +++ b/app/Services/BadgeScan/BadgeScanService.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; /** - * CodeIgniter {@see \App\Controllers\View\RFIDController} parity (badge scan + log listing). + * legacy {@see \App\Controllers\View\RFIDController} parity (badge scan + log listing). */ class BadgeScanService { @@ -64,7 +64,7 @@ class BadgeScanService } /** - * Scan log listing (legacy CI RFIDController::log() query shape). + * Scan log listing (legacy RFIDController::log() query shape). * * @return list> */ diff --git a/app/Services/ClassProgress/ClassProgressMetaService.php b/app/Services/ClassProgress/ClassProgressMetaService.php index 0b424289..ea9ba662 100644 --- a/app/Services/ClassProgress/ClassProgressMetaService.php +++ b/app/Services/ClassProgress/ClassProgressMetaService.php @@ -19,7 +19,7 @@ class ClassProgressMetaService } /** - * Fallback: next N Sundays from today (CI `buildUpcomingSundayOptions`). + * Fallback: next N Sundays from today (legacy `buildUpcomingSundayOptions`). * * @return list */ @@ -29,7 +29,7 @@ class ClassProgressMetaService } /** - * CI-aligned Sundays within semester/school-year range when configuration allows. + * legacy-aligned Sundays within semester/school-year range when configuration allows. * * @return list */ @@ -67,7 +67,7 @@ class ClassProgressMetaService return $options !== [] ? $options : $this->buildUpcomingSundayOptions($count); } - /** CI `pickDefaultWeekStart`. */ + /** legacy `pickDefaultWeekStart`. */ public function pickDefaultWeekStart(array $options): string { if ($options === []) { @@ -109,7 +109,7 @@ class ClassProgressMetaService } /** - * CI `resolveProgressDateRange`. + * legacy `resolveProgressDateRange`. * * @return array{0:string,1:string}|null */ diff --git a/app/Services/ClassProgress/ClassProgressMutationService.php b/app/Services/ClassProgress/ClassProgressMutationService.php index 1edc4e9b..638c5b0d 100644 --- a/app/Services/ClassProgress/ClassProgressMutationService.php +++ b/app/Services/ClassProgress/ClassProgressMutationService.php @@ -110,7 +110,7 @@ class ClassProgressMutationService } /** - * CI `ClassProgressController::update` — sync all subjects for the weekly batch keyed by {@see $anchor}. + * legacy `ClassProgressController::update` — sync all subjects for the weekly batch keyed by {@see $anchor}. * * @return Collection */ diff --git a/app/Services/ClassProgress/ClassProgressQueryService.php b/app/Services/ClassProgress/ClassProgressQueryService.php index 1d6875c8..6a72fc14 100644 --- a/app/Services/ClassProgress/ClassProgressQueryService.php +++ b/app/Services/ClassProgress/ClassProgressQueryService.php @@ -155,7 +155,7 @@ class ClassProgressQueryService } /** - * CI `ClassProgressController::resolveAssignedTeacherIds`. + * legacy `ClassProgressController::resolveAssignedTeacherIds`. * * @return list */ diff --git a/app/Services/ClassProgress/ClassProgressRuleService.php b/app/Services/ClassProgress/ClassProgressRuleService.php index f3542b54..99e9c154 100644 --- a/app/Services/ClassProgress/ClassProgressRuleService.php +++ b/app/Services/ClassProgress/ClassProgressRuleService.php @@ -4,7 +4,7 @@ namespace App\Services\ClassProgress; class ClassProgressRuleService { - /** CI `ClassProgressController::CUSTOM_UNIT_ROW_LABEL` — must match teacher form JS. */ + /** legacy `ClassProgressController::CUSTOM_UNIT_ROW_LABEL` — must match teacher form JS. */ public const CUSTOM_UNIT_ROW_LABEL = 'Custom'; public function defaultStatus(): string @@ -19,7 +19,7 @@ class ClassProgressRuleService } /** - * CI `buildUnitChapterSummary` — Custom / typed chapter rows preserved. + * legacy `buildUnitChapterSummary` — Custom / typed chapter rows preserved. * * @param array $unitValues * @param array $chapterValues @@ -56,7 +56,7 @@ class ClassProgressRuleService return mb_strlen($summary) > 120 ? mb_substr($summary, 0, 120) : $summary; } - /** CI `hasIslamicUnitSelection`. */ + /** legacy `hasIslamicUnitSelection`. */ public function hasIslamicUnitSelection(array $payload): bool { $unitValues = array_map('trim', (array) ($payload['unit_islamic'] ?? [])); @@ -92,7 +92,7 @@ class ClassProgressRuleService } /** - * CI `parseUnitChapterSummary` — inverse of {@see buildUnitChapterSummary()} for edit forms. + * legacy `parseUnitChapterSummary` — inverse of {@see buildUnitChapterSummary()} for edit forms. * * @return array{units: list, chapters: list} */ diff --git a/app/Services/Discounts/DiscountApplyService.php b/app/Services/Discounts/DiscountApplyService.php index 01c10c39..08f7d9dd 100644 --- a/app/Services/Discounts/DiscountApplyService.php +++ b/app/Services/Discounts/DiscountApplyService.php @@ -282,9 +282,7 @@ class DiscountApplyService $semester ); - if (class_exists(\CodeIgniter\Events\Events::class)) { - \CodeIgniter\Events\Events::trigger('paymentReceived', $eventData, $studentData); - } elseif (function_exists('event')) { + if (function_exists('event')) { event('paymentReceived', [$eventData, $studentData]); } } catch (\Throwable $e) { diff --git a/app/Services/Docs/ApiDocsService.php b/app/Services/Docs/ApiDocsService.php index 8dd3ecf5..425b8130 100644 --- a/app/Services/Docs/ApiDocsService.php +++ b/app/Services/Docs/ApiDocsService.php @@ -13,7 +13,7 @@ class ApiDocsService } /** - * Bootstrap payload for the documentation SPA (replaces CI swagger_ui / swagger_ui_public views). + * Bootstrap payload for the documentation SPA (replaces legacy swagger_ui / swagger_ui_public views). * * @return array */ diff --git a/app/Services/Docs/DocsCatalogService.php b/app/Services/Docs/DocsCatalogService.php index 0cffc811..adc9265e 100644 --- a/app/Services/Docs/DocsCatalogService.php +++ b/app/Services/Docs/DocsCatalogService.php @@ -5,7 +5,7 @@ namespace App\Services\Docs; use App\Services\ApplicationUrlService; /** - * CodeIgniter parity: {@see \App\Controllers\DocsController::index} and + * legacy parity: {@see \App\Controllers\DocsController::index} and * {@see \App\Controllers\View\DocsController::swagger} (multi-spec Swagger UI payload). */ class DocsCatalogService @@ -36,7 +36,7 @@ class DocsCatalogService } /** - * Multi-spec dropdown payload (CI `View\DocsController::swagger` / `swagger_ui` view). + * Multi-spec dropdown payload (legacy `View\DocsController::swagger` / `swagger_ui` view). * * @return array{specs: list, merged_openapi_url: string} */ diff --git a/app/Services/ExtraCharges/ExtraChargesChargeService.php b/app/Services/ExtraCharges/ExtraChargesChargeService.php index 2d1f549f..2bf33682 100644 --- a/app/Services/ExtraCharges/ExtraChargesChargeService.php +++ b/app/Services/ExtraCharges/ExtraChargesChargeService.php @@ -208,9 +208,7 @@ class ExtraChargesChargeService $students = []; try { - if (class_exists(\CodeIgniter\Events\Events::class)) { - \CodeIgniter\Events\Events::trigger('extraCharge', $eventData, $students); - } elseif (function_exists('event')) { + if (function_exists('event')) { event('extraCharge', [$eventData, $students]); } } catch (\Throwable $e) { diff --git a/app/Services/Parents/AuthorizedUsersManagementService.php b/app/Services/Parents/AuthorizedUsersManagementService.php index f2f4f484..e34a6a23 100644 --- a/app/Services/Parents/AuthorizedUsersManagementService.php +++ b/app/Services/Parents/AuthorizedUsersManagementService.php @@ -9,7 +9,7 @@ use App\Services\Email\EmailDispatchService; use Illuminate\Support\Facades\DB; /** - * CodeIgniter {@see \App\Controllers\View\AuthorizedUsersController} parity. + * legacy {@see \App\Controllers\View\AuthorizedUsersController} parity. */ class AuthorizedUsersManagementService { @@ -26,7 +26,7 @@ class AuthorizedUsersManagementService } /** - * CI create() first step — lookup by token after invite email (pending row, created within TTL). + * legacy create() first step — lookup by token after invite email (pending row, created within TTL). * * Tokens are stored as SHA-256 hashes; we only ever look up by hash so that * a leaked database row cannot be replayed as a plaintext token. @@ -44,7 +44,7 @@ class AuthorizedUsersManagementService } /** - * CI setPassword / savePassword — active row with rotating token. + * legacy setPassword / savePassword — active row with rotating token. */ public function findActiveSetupRow(int $authorizedUserId, string $plainToken): ?AuthorizedUser { @@ -83,7 +83,7 @@ class AuthorizedUsersManagementService } /** - * CI `create` — when email is not a registered user, respond success without leaking (privacy). + * legacy `create` — when email is not a registered user, respond success without leaking (privacy). * * Refuses self-invites and silently re-uses an existing invite row to avoid * letting a parent spam another user with confirmation emails. diff --git a/app/Services/Parents/ParentAttendanceReportService.php b/app/Services/Parents/ParentAttendanceReportService.php index fba576f8..19a6812e 100644 --- a/app/Services/Parents/ParentAttendanceReportService.php +++ b/app/Services/Parents/ParentAttendanceReportService.php @@ -385,7 +385,7 @@ class ParentAttendanceReportService /** * Staff index: early dismissal rows grouped by date (+ signature metadata per date). * - * Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::earlyDismissals}. + * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::earlyDismissals}. * * @return array{groups: array>>, school_year: string, semester: string, signature_by_date: array>} */ @@ -467,7 +467,7 @@ class ParentAttendanceReportService } /** - * Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::addEarlyDismissalForm}. + * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::addEarlyDismissalForm}. * * @return array{students: array>, default_date: string, school_year: string, semester: string} */ @@ -505,7 +505,7 @@ class ParentAttendanceReportService } /** - * Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::saveEarlyDismissal}. + * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::saveEarlyDismissal}. */ public function saveStaffEarlyDismissal(int $studentId, string $reportDate, string $dismissTime, string $reason): void { @@ -573,7 +573,7 @@ class ParentAttendanceReportService } /** - * Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::uploadEarlyDismissalSignature}. + * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::uploadEarlyDismissalSignature}. * Files live under {@see storage_path('uploads/early_dismissal_signatures')} (same as {@see \App\Http\Controllers\Api\Reports\FilesController::earlyDismissalSignature}). */ public function storeEarlyDismissalSignature( diff --git a/app/Services/Parents/ParentProgressQueryService.php b/app/Services/Parents/ParentProgressQueryService.php index 5a229723..2dcbfa7b 100644 --- a/app/Services/Parents/ParentProgressQueryService.php +++ b/app/Services/Parents/ParentProgressQueryService.php @@ -8,7 +8,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; /** - * CodeIgniter {@see \App\Controllers\ParentProgressController} read-side logic. + * legacy {@see \App\Controllers\ParentProgressController} read-side logic. */ class ParentProgressQueryService { @@ -17,7 +17,7 @@ class ParentProgressQueryService ) {} /** - * CI `getParentStudents` — one row per distinct student (latest enrollment ordering). + * legacy `getParentStudents` — one row per distinct student (latest enrollment ordering). * * @return list> */ @@ -59,7 +59,7 @@ class ParentProgressQueryService } /** - * CI `getParentSectionIds`. + * legacy `getParentSectionIds`. * * @return list */ @@ -119,7 +119,7 @@ class ParentProgressQueryService } /** - * CI `groupReportsByWeek` — keys `"{week_start}:{class_section_id}"`. + * legacy `groupReportsByWeek` — keys `"{week_start}:{class_section_id}"`. * * @param iterable $rows * @return array> @@ -166,7 +166,7 @@ class ParentProgressQueryService } /** - * Weekly batch for a section/week (CI parent `view`, all subjects). + * Weekly batch for a section/week (legacy parent `view`, all subjects). * * @return list */ diff --git a/app/Services/Parents/ParentReportCardService.php b/app/Services/Parents/ParentReportCardService.php index b1d5db0c..e1ab3167 100644 --- a/app/Services/Parents/ParentReportCardService.php +++ b/app/Services/Parents/ParentReportCardService.php @@ -9,7 +9,7 @@ use App\Models\User; use Illuminate\Support\Facades\DB; /** - * CodeIgniter {@see \App\Controllers\ParentReportCardController} parity (read + acknowledgement). + * legacy {@see \App\Controllers\ParentReportCardController} parity (read + acknowledgement). */ class ParentReportCardService { @@ -19,7 +19,7 @@ class ParentReportCardService ) {} /** - * Resolve term from query string with configuration fallbacks (CI index/view). + * Resolve term from query string with configuration fallbacks (legacy index/view). * * @return array{0: string, 1: string} */ @@ -38,7 +38,7 @@ class ParentReportCardService } /** - * CI sign() — current configuration term only (no GET overrides). + * legacy sign() — current configuration term only (no GET overrides). * * @return array{0: string, 1: string} */ @@ -56,7 +56,7 @@ class ParentReportCardService } /** - * CI index() student rows for parent + optional student_class term filters. + * legacy index() student rows for parent + optional student_class term filters. * * @return list> */ diff --git a/app/Services/Parents/PrimaryParentUserResolver.php b/app/Services/Parents/PrimaryParentUserResolver.php index ac36967d..42cf1b8d 100644 --- a/app/Services/Parents/PrimaryParentUserResolver.php +++ b/app/Services/Parents/PrimaryParentUserResolver.php @@ -7,7 +7,7 @@ use Illuminate\Support\Facades\DB; /** * Maps the logged-in parent-family account to the primary parent's user id - * (stored on {@see \App\Models\Student::$parent_id}), matching CodeIgniter + * (stored on {@see \App\Models\Student::$parent_id}), matching legacy * {@see \App\Controllers\ParentReportCardController::resolvePrimaryParentId}. */ final class PrimaryParentUserResolver diff --git a/app/Services/Payments/PaymentManualService.php b/app/Services/Payments/PaymentManualService.php index d56936af..72c6788b 100644 --- a/app/Services/Payments/PaymentManualService.php +++ b/app/Services/Payments/PaymentManualService.php @@ -637,9 +637,7 @@ class PaymentManualService private function triggerEvent(string $name, array $payload): void { try { - if (class_exists(\CodeIgniter\Events\Events::class)) { - \CodeIgniter\Events\Events::trigger($name, ...$payload); - } elseif (function_exists('event')) { + if (function_exists('event')) { event($name, $payload); } } catch (\Throwable $e) { diff --git a/app/Services/PrintRequests/PrintRequestsPortalService.php b/app/Services/PrintRequests/PrintRequestsPortalService.php index d547313a..17420b5f 100644 --- a/app/Services/PrintRequests/PrintRequestsPortalService.php +++ b/app/Services/PrintRequests/PrintRequestsPortalService.php @@ -16,7 +16,7 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; /** - * CodeIgniter {@see \App\Controllers\PrintRequests} parity (SPA JSON + uploads). + * legacy {@see \App\Controllers\PrintRequests} parity (SPA JSON + uploads). */ class PrintRequestsPortalService { @@ -120,7 +120,7 @@ class PrintRequestsPortalService } /** - * Admin queue ordering per CI raw SQL. + * Admin queue ordering per legacy raw SQL. * * @return list> */ @@ -365,7 +365,7 @@ class PrintRequestsPortalService } /** - * Teacher dashboard payload (parity with CI teacher_index). + * Teacher dashboard payload (parity with legacy teacher_index). * * @return array{print_requests: list>, sundays: list, times: list, class_id: int|null} */ @@ -510,7 +510,7 @@ class PrintRequestsPortalService } /** - * Copy-center request without uploading a file (CI createCopy). + * Copy-center request without uploading a file (legacy createCopy). * * @param array $data class_id, num_copies, required_by, pickup_method */ diff --git a/app/Services/PublicWinners/PublicWinnersPortalService.php b/app/Services/PublicWinners/PublicWinnersPortalService.php index d623938c..95954a7c 100644 --- a/app/Services/PublicWinners/PublicWinnersPortalService.php +++ b/app/Services/PublicWinners/PublicWinnersPortalService.php @@ -7,7 +7,7 @@ use App\Models\Competition; use Illuminate\Support\Facades\DB; /** - * CodeIgniter {@see \App\Controllers\WinnersController} — public published winners pages. + * legacy {@see \App\Controllers\WinnersController} — public published winners pages. */ class PublicWinnersPortalService { diff --git a/app/Services/Students/StudentScoreCardService.php b/app/Services/Students/StudentScoreCardService.php index 0506c54f..96379ae7 100644 --- a/app/Services/Students/StudentScoreCardService.php +++ b/app/Services/Students/StudentScoreCardService.php @@ -12,7 +12,7 @@ use Illuminate\Support\Facades\DB; class StudentScoreCardService { /** - * Students shown on the score-card picker (CI `StudentController::scoreCardList` parity). + * Students shown on the score-card picker (legacy `StudentController::scoreCardList` parity). * Teachers: only students in `class_section_id` for `school_year` after `teacher_class` check. * Parents: their children only. Other roles: active students (optional search/limit). * @@ -25,7 +25,7 @@ class StudentScoreCardService ?int $classSectionId, ?string $schoolYear, ): array { - $rolesLower = array_map('strtolower', $user->roleNamesLikeCodeIgniter()); + $rolesLower = array_map('strtolower', $user->roleNames()); $isTeacher = in_array('teacher', $rolesLower, true) || in_array('teacher_assistant', $rolesLower, true); $isParent = in_array('parent', $rolesLower, true); diff --git a/app/Support/SessionTimeoutConfig.php b/app/Support/SessionTimeoutConfig.php new file mode 100644 index 00000000..f8cce002 --- /dev/null +++ b/app/Support/SessionTimeoutConfig.php @@ -0,0 +1,10 @@ + (int) env('JWT_TTL', 1440), /* diff --git a/docs/api_base_api_controller_plan.md b/docs/api_base_api_controller_plan.md index dc45a1c5..4f96f97f 100644 --- a/docs/api_base_api_controller_plan.md +++ b/docs/api_base_api_controller_plan.md @@ -5,7 +5,7 @@ - **Purpose**: shared API conveniences: - Standard success/error JSON envelope helpers - Request payload normalization (JSON body vs query/form) - - Validation helper compatible with legacy CodeIgniter-like rules + - Validation helper compatible with legacy-style rules - Lightweight pagination helper for arrays/query builders - Current-user resolution helpers (id + role list) @@ -18,14 +18,14 @@ ### Request normalization - Stores the underlying Laravel request as `laravelRequest`. -- Wraps it in `CiRequestAdapter` as `$this->request` for compatibility patterns. +- Wraps it in `request adapter` as `$this->request` for compatibility patterns. - `payloadData()`: - Uses JSON-decoded body when present and valid - Falls back to merged query + form data ### Validation - `validateRequest($data, $rules)`: - - Normalizes CodeIgniter-like rule strings into Laravel rules via `normalizeRules()`. + - Normalizes legacy-like rule strings into Laravel rules via `normalizeRules()`. - Saves validator to `$this->validator`. - Returns `[]` on success, or `errors()->toArray()` on failure. - `validate($rules)` validates against `payloadData()` and returns boolean. @@ -63,4 +63,3 @@ - `getCurrentUser()`: - No auth → `null` - Role query failure → returns user with empty roles and logs error - diff --git a/docs/app_project_plan.md b/docs/app_project_plan.md index a3526681..d954688f 100644 --- a/docs/app_project_plan.md +++ b/docs/app_project_plan.md @@ -61,7 +61,7 @@ These are not accusations; they are where bugs usually crawl out wearing a tiny - **Email or messaging behavior:** 62 files, 192 matches. - **Auth/Gate/authorization usage:** 325 files, 482 matches. - **Explicit DB transactions:** 48 files, 72 matches. -- **CodeIgniter compatibility surface:** 41 files, 62 matches. +- **legacy compatibility surface:** 41 files, 62 matches. - **Queued or dispatched work:** 7 files, 8 matches. - **Cache-dependent behavior:** 5 files, 12 matches. @@ -123,7 +123,7 @@ Review priority should follow blast radius: auth, finance, payments, attendance, - Generate OpenAPI output from route definitions, once routes are included. - Document each module: owner, purpose, inputs, outputs, permissions, side effects, and failure modes. -- Document legacy compatibility areas explicitly, especially CodeIgniter adapters and helper shims. +- Document legacy compatibility areas explicitly, especially legacy adapters and helper shims. - Add API examples for primary user flows: login, role switch, parent dashboard, attendance submission, invoice payment, grading submission. @@ -179,7 +179,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Priority:** P2/P3 Feature hardening **Detected surface:** 7 controllers, 0 services, 0 requests, 0 resources. -**Controllers:** BaseApiController, CiRequestAdapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController +**Controllers:** BaseApiController, request adapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController **Plan:** - Confirm all inputs are validated through FormRequest classes. - Confirm all reads and writes are authorized. @@ -248,7 +248,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Detected surface:** 7 controllers, 9 services, 3 requests, 1 resources. **Controllers:** AuthController, AuthSessionController, IpBanController, RegisterController, RolePermissionController, RoleSwitcherController, SessionTimeoutController -**Services:** RegistrationCaptchaService, PermissionCheckService, CodeIgniterApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService +**Services:** RegistrationCaptchaService, PermissionCheckService, ApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService **Requests:** RegisterRequest, SetRoleSessionRequest, LoginSessionRequest **Resources:** RegisterResource **Plan:** @@ -420,7 +420,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Priority:** P2/P3 Feature hardening **Detected surface:** 2 controllers, 0 services, 0 requests, 0 resources. -**Controllers:** BaseApiController, CiRequestAdapter +**Controllers:** BaseApiController, request adapter **Plan:** - Confirm config reads/writes are permissioned and validated. - Add health/config smoke tests. @@ -1249,7 +1249,7 @@ A module is not considered done until all of this is true: ## Appendix A: Controller Groups - **Administrator:** 8 controllers. AdministratorAbsenceController, AdministratorDashboardController, AdministratorEnrollmentController, AdministratorNotificationController, AdministratorPromotionController, AdministratorTeacherSubmissionController, EmergencyContactController, TeacherClassAssignmentController -- **ApiRoot:** 7 controllers. BaseApiController, CiRequestAdapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController +- **ApiRoot:** 7 controllers. BaseApiController, request adapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController - **Assignment:** 1 controllers. AssignmentApiController - **Attendance:** 6 controllers. AdminAttendanceApiController, AttendanceCommentTemplateController, EarlyDismissalsController, LateSlipLogsController, StaffAttendanceApiController, TeacherAttendanceApiController - **AttendanceTracking:** 1 controllers. AttendanceTrackingController @@ -1262,7 +1262,7 @@ A module is not considered done until all of this is true: - **Classes:** 1 controllers. ClassController - **Communication:** 1 controllers. CommunicationController - **CompetitionScores:** 1 controllers. CompetitionScoresController -- **Core:** 2 controllers. BaseApiController, CiRequestAdapter +- **Core:** 2 controllers. BaseApiController, request adapter - **Discounts:** 1 controllers. DiscountController - **Documentation:** 2 controllers. ApiDocsAdminController, DocsCatalogController - **Email:** 3 controllers. BroadcastEmailController, EmailController, EmailExtractorController @@ -1300,7 +1300,7 @@ A module is not considered done until all of this is true: - **Assignment:** 5 services, 421 LOC. AssignmentSectionService, AssignmentMetaService, AssignmentService, AssignmentContextService, AssignmentDataLoaderService - **Attendance:** 16 services, 3,241 LOC. SemesterRangeService, TeacherAttendanceSubmissionService, AttendanceAutoPublishJobService, AttendanceAutoPublishService, LateSlipLogQueryService, AttendanceConsequenceService, AttendanceRecordSyncService, AttendanceDailySummaryService, AttendanceService, StudentAttendanceWriterService, LateSlipLogCommandService, AttendanceSummaryRebuildService, StaffAttendanceService, AttendanceQueryService, AttendancePolicyService, AttendanceCommentService - **AttendanceTracking:** 12 services, 2,979 LOC. AttendanceNotificationLogService, AttendanceViolationStudentResolverService, AttendanceNotificationWorkflowService, AttendanceTrackingService, ViolationRuleEngineService, AttendanceMailerService, AttendanceParentLookupService, AttendanceCommunicationSupportService, AttendanceEmailComposerService, AttendancePendingViolationService, AttendanceCaseQueryService, DefaultAttendanceMailerService -- **Auth:** 9 services, 870 LOC. RegistrationCaptchaService, PermissionCheckService, CodeIgniterApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService +- **Auth:** 9 services, 870 LOC. RegistrationCaptchaService, PermissionCheckService, ApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService - **BadgeScan:** 1 services, 108 LOC. BadgeScanService - **Badges:** 7 services, 2,815 LOC. BadgeTextFormatter, BadgeStudentLookupService, BadgeFormDataService, BadgePdfService, BadgeUserLookupService, BadgeService, BadgePrintLogService - **Billing:** 3 services, 751 LOC. BalanceCalculationService, BillingTotalsService, ChargeService @@ -1599,7 +1599,7 @@ A module is not considered done until all of this is true: - `app/Services/Parents/ParentAttendanceReportService.php`: 4 matches - `app/Config/Database.php`: 3 matches -- `app/Helpers/ci_helpers.php`: 3 matches +- `app/Helpers/app_helpers.php`: 3 matches - `app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php`: 3 matches - `app/Http/Controllers/Api/Auth/RegisterController.php`: 3 matches - `app/Http/Controllers/Api/BaseApiController.php`: 3 matches @@ -1607,14 +1607,14 @@ A module is not considered done until all of this is true: - `app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php`: 2 matches - `app/Services/AttendanceTracking/AttendanceTrackingService.php`: 2 matches - `app/Services/Auth/ApiLoginSecurityService.php`: 2 matches -- `app/Services/Auth/CodeIgniterApiRegistrationService.php`: 2 matches +- `app/Services/Auth/ApiRegistrationService.php`: 2 matches - `app/Services/Discounts/DiscountApplyService.php`: 2 matches - `app/Services/ExtraCharges/ExtraChargesChargeService.php`: 2 matches - `app/Services/Payments/PaymentManualService.php`: 2 matches - `app/Http/Controllers/Api/Auth/AuthController.php`: 1 matches - `app/Http/Controllers/Api/Auth/AuthSessionController.php`: 1 matches -- `app/Http/Controllers/Api/CiRequestAdapter.php`: 1 matches -- `app/Http/Controllers/Api/Core/CiRequestAdapter.php`: 1 matches +- `app/Http/Controllers/Api/request adapter.php`: 1 matches +- `app/Http/Controllers/Api/Core/request adapter.php`: 1 matches - `app/Http/Controllers/Api/System/HealthController.php`: 1 matches - `app/Http/Controllers/Api/Utilities/ProofreadController.php`: 1 matches - `app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php`: 1 matches @@ -1625,7 +1625,7 @@ A module is not considered done until all of this is true: - `app/Http/Requests/Admin/SaveCompetitionScoresRequest.php`: 1 matches - `app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php`: 1 matches - `app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php`: 1 matches -- `app/Models/CiDatabase.php`: 1 matches +- `app/Models/database compatibility layer.php`: 1 matches - `app/Models/User.php`: 1 matches - `app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php`: 1 matches - `app/Services/Attendance/StaffAttendanceService.php`: 1 matches diff --git a/docs/app_project_plan_hardened.md b/docs/app_project_plan_hardened.md index fe8920e0..5f9d30fa 100644 --- a/docs/app_project_plan_hardened.md +++ b/docs/app_project_plan_hardened.md @@ -128,7 +128,7 @@ These are not accusations; they are where bugs usually crawl out wearing a tiny - **Email or messaging behavior:** 62 files, 192 matches. - **Auth/Gate/authorization usage:** 325 files, 482 matches. - **Explicit DB transactions:** 48 files, 72 matches. -- **CodeIgniter compatibility surface:** 41 files, 62 matches. +- **legacy compatibility surface:** 41 files, 62 matches. - **Queued or dispatched work:** 7 files, 8 matches. - **Cache-dependent behavior:** 5 files, 12 matches. @@ -195,7 +195,7 @@ Review priority should follow blast radius: auth, finance, payments, attendance, - Generate OpenAPI output from route definitions, once routes are included. - Document each module: owner, purpose, inputs, outputs, permissions, side effects, and failure modes. -- Document legacy compatibility areas explicitly, especially CodeIgniter adapters and helper shims. +- Document legacy compatibility areas explicitly, especially legacy adapters and helper shims. - Add API examples for primary user flows: login, role switch, parent dashboard, attendance submission, invoice payment, grading submission. @@ -251,12 +251,12 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Priority:** P2/P3 Feature hardening **Detected surface:** 7 controllers, 0 services, 0 requests, 0 resources. -**Controllers:** BaseApiController, CiRequestAdapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController +**Controllers:** BaseApiController, request adapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController **Plan:** - Quarantine legacy/root controllers and define canonical replacements for duplicate controller names. - Split `ScannerController` into a FormRequest, scanner orchestration service, badge lookup service, student scan service, staff scan service, late-arrival service, scan logger, and response factory. - Add transaction boundaries and idempotency around scanner attendance mutations. -- Define whether `BaseApiController`, `CiRequestAdapter`, root role controllers, and namespaced role controllers are canonical or shims. +- Define whether `BaseApiController`, `request adapter`, root role controllers, and namespaced role controllers are canonical or shims. - Ensure shim classes contain no business logic. - Add feature tests for scanner flows, role switching, stats visibility, and user visibility. @@ -332,7 +332,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Detected surface:** 7 controllers, 9 services, 3 requests, 1 resources. **Controllers:** AuthController, AuthSessionController, IpBanController, RegisterController, RolePermissionController, RoleSwitcherController, SessionTimeoutController -**Services:** RegistrationCaptchaService, PermissionCheckService, CodeIgniterApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService +**Services:** RegistrationCaptchaService, PermissionCheckService, ApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService **Requests:** RegisterRequest, SetRoleSessionRequest, LoginSessionRequest **Resources:** RegisterResource **Plan:** @@ -504,7 +504,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Priority:** P2/P3 Feature hardening **Detected surface:** 2 controllers, 0 services, 0 requests, 0 resources. -**Controllers:** BaseApiController, CiRequestAdapter +**Controllers:** BaseApiController, request adapter **Plan:** - Confirm config reads/writes are permissioned and validated. - Add health/config smoke tests. @@ -1370,7 +1370,7 @@ A module is not considered done until all of this is true: ## Appendix A: Controller Groups - **Administrator:** 8 controllers. AdministratorAbsenceController, AdministratorDashboardController, AdministratorEnrollmentController, AdministratorNotificationController, AdministratorPromotionController, AdministratorTeacherSubmissionController, EmergencyContactController, TeacherClassAssignmentController -- **ApiRoot:** 7 controllers. BaseApiController, CiRequestAdapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController +- **ApiRoot:** 7 controllers. BaseApiController, request adapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController - **Assignment:** 1 controllers. AssignmentApiController - **Attendance:** 6 controllers. AdminAttendanceApiController, AttendanceCommentTemplateController, EarlyDismissalsController, LateSlipLogsController, StaffAttendanceApiController, TeacherAttendanceApiController - **AttendanceTracking:** 1 controllers. AttendanceTrackingController @@ -1383,7 +1383,7 @@ A module is not considered done until all of this is true: - **Classes:** 1 controllers. ClassController - **Communication:** 1 controllers. CommunicationController - **CompetitionScores:** 1 controllers. CompetitionScoresController -- **Core:** 2 controllers. BaseApiController, CiRequestAdapter +- **Core:** 2 controllers. BaseApiController, request adapter - **Discounts:** 1 controllers. DiscountController - **Documentation:** 2 controllers. ApiDocsAdminController, DocsCatalogController - **Email:** 3 controllers. BroadcastEmailController, EmailController, EmailExtractorController @@ -1421,7 +1421,7 @@ A module is not considered done until all of this is true: - **Assignment:** 5 services, 421 LOC. AssignmentSectionService, AssignmentMetaService, AssignmentService, AssignmentContextService, AssignmentDataLoaderService - **Attendance:** 16 services, 3,241 LOC. SemesterRangeService, TeacherAttendanceSubmissionService, AttendanceAutoPublishJobService, AttendanceAutoPublishService, LateSlipLogQueryService, AttendanceConsequenceService, AttendanceRecordSyncService, AttendanceDailySummaryService, AttendanceService, StudentAttendanceWriterService, LateSlipLogCommandService, AttendanceSummaryRebuildService, StaffAttendanceService, AttendanceQueryService, AttendancePolicyService, AttendanceCommentService - **AttendanceTracking:** 12 services, 2,979 LOC. AttendanceNotificationLogService, AttendanceViolationStudentResolverService, AttendanceNotificationWorkflowService, AttendanceTrackingService, ViolationRuleEngineService, AttendanceMailerService, AttendanceParentLookupService, AttendanceCommunicationSupportService, AttendanceEmailComposerService, AttendancePendingViolationService, AttendanceCaseQueryService, DefaultAttendanceMailerService -- **Auth:** 9 services, 870 LOC. RegistrationCaptchaService, PermissionCheckService, CodeIgniterApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService +- **Auth:** 9 services, 870 LOC. RegistrationCaptchaService, PermissionCheckService, ApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService - **BadgeScan:** 1 services, 108 LOC. BadgeScanService - **Badges:** 7 services, 2,815 LOC. BadgeTextFormatter, BadgeStudentLookupService, BadgeFormDataService, BadgePdfService, BadgeUserLookupService, BadgeService, BadgePrintLogService - **Billing:** 3 services, 751 LOC. BalanceCalculationService, BillingTotalsService, ChargeService @@ -1862,7 +1862,7 @@ These tickets are the plan’s mandatory implementation layer. Each one should b - `app/Services/Parents/ParentAttendanceReportService.php`: 4 matches - `app/Config/Database.php`: 3 matches -- `app/Helpers/ci_helpers.php`: 3 matches +- `app/Helpers/app_helpers.php`: 3 matches - `app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php`: 3 matches - `app/Http/Controllers/Api/Auth/RegisterController.php`: 3 matches - `app/Http/Controllers/Api/BaseApiController.php`: 3 matches @@ -1870,14 +1870,14 @@ These tickets are the plan’s mandatory implementation layer. Each one should b - `app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php`: 2 matches - `app/Services/AttendanceTracking/AttendanceTrackingService.php`: 2 matches - `app/Services/Auth/ApiLoginSecurityService.php`: 2 matches -- `app/Services/Auth/CodeIgniterApiRegistrationService.php`: 2 matches +- `app/Services/Auth/ApiRegistrationService.php`: 2 matches - `app/Services/Discounts/DiscountApplyService.php`: 2 matches - `app/Services/ExtraCharges/ExtraChargesChargeService.php`: 2 matches - `app/Services/Payments/PaymentManualService.php`: 2 matches - `app/Http/Controllers/Api/Auth/AuthController.php`: 1 matches - `app/Http/Controllers/Api/Auth/AuthSessionController.php`: 1 matches -- `app/Http/Controllers/Api/CiRequestAdapter.php`: 1 matches -- `app/Http/Controllers/Api/Core/CiRequestAdapter.php`: 1 matches +- `app/Http/Controllers/Api/request adapter.php`: 1 matches +- `app/Http/Controllers/Api/Core/request adapter.php`: 1 matches - `app/Http/Controllers/Api/System/HealthController.php`: 1 matches - `app/Http/Controllers/Api/Utilities/ProofreadController.php`: 1 matches - `app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php`: 1 matches @@ -1888,7 +1888,7 @@ These tickets are the plan’s mandatory implementation layer. Each one should b - `app/Http/Requests/Admin/SaveCompetitionScoresRequest.php`: 1 matches - `app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php`: 1 matches - `app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php`: 1 matches -- `app/Models/CiDatabase.php`: 1 matches +- `app/Models/database compatibility layer.php`: 1 matches - `app/Models/User.php`: 1 matches - `app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php`: 1 matches - `app/Services/Attendance/StaffAttendanceService.php`: 1 matches diff --git a/docs/controller_plan.md b/docs/controller_plan.md index bc1231fd..bb860108 100644 --- a/docs/controller_plan.md +++ b/docs/controller_plan.md @@ -373,7 +373,7 @@ Generated from the uploaded `Api(1).zip` controller tree. This is a controller-b **File:** `Api/Auth/RegisterController.php` **Purpose:** handle authentication/authorization workflow for register. **Public methods:** `captcha()`, `store(Request $request)` -**Detected dependencies:** Services: `CodeIgniterApiRegistrationService`, `RegistrationCaptchaService`, `RegistrationService` | Resources: `RegisterResource` +**Detected dependencies:** Services: `ApiRegistrationService`, `RegistrationCaptchaService`, `RegistrationService` | Resources: `RegisterResource` **Plan** - Define endpoint contract for each public method: request fields, response resource, status codes, and failure shape. diff --git a/docs/modular_plans/app_project_plan_global_modular_islamic.md b/docs/modular_plans/app_project_plan_global_modular_islamic.md index d2298a58..7c332d7d 100644 --- a/docs/modular_plans/app_project_plan_global_modular_islamic.md +++ b/docs/modular_plans/app_project_plan_global_modular_islamic.md @@ -302,7 +302,7 @@ These are not accusations; they are where bugs usually crawl out wearing a tiny - **Email or messaging behavior:** 62 files, 192 matches. - **Auth/Gate/authorization usage:** 325 files, 482 matches. - **Explicit DB transactions:** 48 files, 72 matches. -- **CodeIgniter compatibility surface:** 41 files, 62 matches. +- **legacy compatibility surface:** 41 files, 62 matches. - **Queued or dispatched work:** 7 files, 8 matches. - **Cache-dependent behavior:** 5 files, 12 matches. @@ -369,7 +369,7 @@ Review priority should follow blast radius: auth, finance, payments, attendance, - Generate OpenAPI output from route definitions, once routes are included. - Document each module: owner, purpose, inputs, outputs, permissions, side effects, and failure modes. -- Document legacy compatibility areas explicitly, especially CodeIgniter adapters and helper shims. +- Document legacy compatibility areas explicitly, especially legacy adapters and helper shims. - Add API examples for primary user flows: login, role switch, parent dashboard, attendance submission, invoice payment, grading submission. @@ -578,12 +578,12 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Priority:** P2/P3 Feature hardening **Detected surface:** 7 controllers, 0 services, 0 requests, 0 resources. -**Controllers:** BaseApiController, CiRequestAdapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController +**Controllers:** BaseApiController, request adapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController **Plan:** - Quarantine legacy/root controllers and define canonical replacements for duplicate controller names. - Split `ScannerController` into a FormRequest, scanner orchestration service, badge lookup service, student scan service, staff scan service, late-arrival service, scan logger, and response factory. - Add transaction boundaries and idempotency around scanner attendance mutations. -- Define whether `BaseApiController`, `CiRequestAdapter`, root role controllers, and namespaced role controllers are canonical or shims. +- Define whether `BaseApiController`, `request adapter`, root role controllers, and namespaced role controllers are canonical or shims. - Ensure shim classes contain no business logic. - Add feature tests for scanner flows, role switching, stats visibility, and user visibility. @@ -659,7 +659,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Detected surface:** 7 controllers, 9 services, 3 requests, 1 resources. **Controllers:** AuthController, AuthSessionController, IpBanController, RegisterController, RolePermissionController, RoleSwitcherController, SessionTimeoutController -**Services:** RegistrationCaptchaService, PermissionCheckService, CodeIgniterApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService +**Services:** RegistrationCaptchaService, PermissionCheckService, ApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService **Requests:** RegisterRequest, SetRoleSessionRequest, LoginSessionRequest **Resources:** RegisterResource **Plan:** @@ -831,7 +831,7 @@ Each module gets the same discipline: define ownership, confirm validation, conf **Priority:** P2/P3 Feature hardening **Detected surface:** 2 controllers, 0 services, 0 requests, 0 resources. -**Controllers:** BaseApiController, CiRequestAdapter +**Controllers:** BaseApiController, request adapter **Plan:** - Confirm config reads/writes are permissioned and validated. - Add health/config smoke tests. @@ -1697,7 +1697,7 @@ A module is not considered done until all of this is true: ## Appendix A: Controller Groups - **Administrator:** 8 controllers. AdministratorAbsenceController, AdministratorDashboardController, AdministratorEnrollmentController, AdministratorNotificationController, AdministratorPromotionController, AdministratorTeacherSubmissionController, EmergencyContactController, TeacherClassAssignmentController -- **ApiRoot:** 7 controllers. BaseApiController, CiRequestAdapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController +- **ApiRoot:** 7 controllers. BaseApiController, request adapter, RolePermissionController, RoleSwitcherController, ScannerController, StatsController, UserController - **Assignment:** 1 controllers. AssignmentApiController - **Attendance:** 6 controllers. AdminAttendanceApiController, AttendanceCommentTemplateController, EarlyDismissalsController, LateSlipLogsController, StaffAttendanceApiController, TeacherAttendanceApiController - **AttendanceTracking:** 1 controllers. AttendanceTrackingController @@ -1710,7 +1710,7 @@ A module is not considered done until all of this is true: - **Classes:** 1 controllers. ClassController - **Communication:** 1 controllers. CommunicationController - **CompetitionScores:** 1 controllers. CompetitionScoresController -- **Core:** 2 controllers. BaseApiController, CiRequestAdapter +- **Core:** 2 controllers. BaseApiController, request adapter - **Discounts:** 1 controllers. DiscountController - **Documentation:** 2 controllers. ApiDocsAdminController, DocsCatalogController - **Email:** 3 controllers. BroadcastEmailController, EmailController, EmailExtractorController @@ -1748,7 +1748,7 @@ A module is not considered done until all of this is true: - **Assignment:** 5 services, 421 LOC. AssignmentSectionService, AssignmentMetaService, AssignmentService, AssignmentContextService, AssignmentDataLoaderService - **Attendance:** 16 services, 3,241 LOC. SemesterRangeService, TeacherAttendanceSubmissionService, AttendanceAutoPublishJobService, AttendanceAutoPublishService, LateSlipLogQueryService, AttendanceConsequenceService, AttendanceRecordSyncService, AttendanceDailySummaryService, AttendanceService, StudentAttendanceWriterService, LateSlipLogCommandService, AttendanceSummaryRebuildService, StaffAttendanceService, AttendanceQueryService, AttendancePolicyService, AttendanceCommentService - **AttendanceTracking:** 12 services, 2,979 LOC. AttendanceNotificationLogService, AttendanceViolationStudentResolverService, AttendanceNotificationWorkflowService, AttendanceTrackingService, ViolationRuleEngineService, AttendanceMailerService, AttendanceParentLookupService, AttendanceCommunicationSupportService, AttendanceEmailComposerService, AttendancePendingViolationService, AttendanceCaseQueryService, DefaultAttendanceMailerService -- **Auth:** 9 services, 870 LOC. RegistrationCaptchaService, PermissionCheckService, CodeIgniterApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService +- **Auth:** 9 services, 870 LOC. RegistrationCaptchaService, PermissionCheckService, ApiRegistrationService, RegistrationService, ApiLoginSecurityService, AuthSessionService, UserRoleService, PasswordResetCleanupService, RegistrationFormatterService - **BadgeScan:** 1 services, 108 LOC. BadgeScanService - **Badges:** 7 services, 2,815 LOC. BadgeTextFormatter, BadgeStudentLookupService, BadgeFormDataService, BadgePdfService, BadgeUserLookupService, BadgeService, BadgePrintLogService - **Billing:** 3 services, 751 LOC. BalanceCalculationService, BillingTotalsService, ChargeService @@ -2189,7 +2189,7 @@ These tickets are the plan’s mandatory implementation layer. Each one should b - `app/Services/Parents/ParentAttendanceReportService.php`: 4 matches - `app/Config/Database.php`: 3 matches -- `app/Helpers/ci_helpers.php`: 3 matches +- `app/Helpers/app_helpers.php`: 3 matches - `app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php`: 3 matches - `app/Http/Controllers/Api/Auth/RegisterController.php`: 3 matches - `app/Http/Controllers/Api/BaseApiController.php`: 3 matches @@ -2197,14 +2197,14 @@ These tickets are the plan’s mandatory implementation layer. Each one should b - `app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php`: 2 matches - `app/Services/AttendanceTracking/AttendanceTrackingService.php`: 2 matches - `app/Services/Auth/ApiLoginSecurityService.php`: 2 matches -- `app/Services/Auth/CodeIgniterApiRegistrationService.php`: 2 matches +- `app/Services/Auth/ApiRegistrationService.php`: 2 matches - `app/Services/Discounts/DiscountApplyService.php`: 2 matches - `app/Services/ExtraCharges/ExtraChargesChargeService.php`: 2 matches - `app/Services/Payments/PaymentManualService.php`: 2 matches - `app/Http/Controllers/Api/Auth/AuthController.php`: 1 matches - `app/Http/Controllers/Api/Auth/AuthSessionController.php`: 1 matches -- `app/Http/Controllers/Api/CiRequestAdapter.php`: 1 matches -- `app/Http/Controllers/Api/Core/CiRequestAdapter.php`: 1 matches +- `app/Http/Controllers/Api/request adapter.php`: 1 matches +- `app/Http/Controllers/Api/Core/request adapter.php`: 1 matches - `app/Http/Controllers/Api/System/HealthController.php`: 1 matches - `app/Http/Controllers/Api/Utilities/ProofreadController.php`: 1 matches - `app/Http/Middleware/EnsureClassProgressTeacherPortalAccess.php`: 1 matches @@ -2215,7 +2215,7 @@ These tickets are the plan’s mandatory implementation layer. Each one should b - `app/Http/Requests/Admin/SaveCompetitionScoresRequest.php`: 1 matches - `app/Http/Requests/Admin/StoreCompetitionWinnerRequest.php`: 1 matches - `app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php`: 1 matches -- `app/Models/CiDatabase.php`: 1 matches +- `app/Models/database compatibility layer.php`: 1 matches - `app/Models/User.php`: 1 matches - `app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php`: 1 matches - `app/Services/Attendance/StaffAttendanceService.php`: 1 matches diff --git a/resources/docs/controllers.md b/resources/docs/controllers.md index d898303d..8a72a97d 100644 --- a/resources/docs/controllers.md +++ b/resources/docs/controllers.md @@ -76,7 +76,7 @@ This document lists every API controller alphabetically with any inline route co **File:** `app/Http/Controllers/Api/Auth/AuthController.php` **Purpose:** JWT-backed authentication endpoints. -- `POST /api/v1/auth/login` (`login`) – Validates `email` and `password`, looks up the user case-insensitively, uses `ci_password_verify`, and returns `{access_token, token_type, expires_in}` or `401`. +- `POST /api/v1/auth/login` (`login`) – Validates `email` and `password`, looks up the user case-insensitively, uses `verify_stored_password`, and returns `{access_token, token_type, expires_in}` or `401`. - `GET /api/v1/auth/me` (`me`) – Requires a valid bearer token, returns the decoded `User` JSON via `JWTAuth::parseToken()->authenticate()`. - `POST /api/v1/auth/logout` (`logout`) – Invalidates the caller’s JWT token (best-effort) and returns `{message: "Logged out"}` even if the token was already invalid. @@ -95,13 +95,13 @@ This document lists every API controller alphabetically with any inline route co ## BaseApiController **File:** `app/Http/Controllers/Api/BaseApiController.php` -**Purpose:** Shared foundation for every API controller. Wraps the Laravel request in a `CiRequestAdapter`, standardizes JSON responses, bridges CodeIgniter-style validation rules, and exposes helper utilities. +**Purpose:** Shared foundation for every API controller. Wraps the Laravel request in a `request adapter`, standardizes JSON responses, bridges legacy-style validation rules, and exposes helper utilities. - **Response helpers:** `success`, `error`, and the `respond*` variants keep payloads consistent (`{status, message, data}`) and map to the right HTTP codes (`200`, `201`, `204`, `400`, `422`, etc.). -- **Validation bridge:** `validateRequest`, `validate`, `payloadData`, and `normalizeRules` accept CodeIgniter rule strings (e.g., `max_length[255]`, `permit_empty`) and translate them to Laravel validator syntax before running `Validator::make`. +- **Validation bridge:** `validateRequest`, `validate`, `payloadData`, and `normalizeRules` accept legacy rule strings (e.g., `max_length[255]`, `permit_empty`) and translate them to Laravel validator syntax before running `Validator::make`. - **Pagination:** `paginate($source, $page, $perPage)` works with Eloquent builders, query builders, or arrays, returning `{data, pagination:{current_page, per_page, total, total_pages}}`. - **Session context:** `getCurrentUserId()` reads the `user_id` from the session, and `getCurrentUser()` fetches the hydrated `UserModel` record, falling back to querying `user_roles` if roles are missing from the session cache. -- **Constructor:** captures the Laravel `Request` and exposes it as `$this->request` via `CiRequestAdapter` so consuming controllers can continue to use familiar CI-style helpers (`getGet`, `getJSON`, etc.). +- **Constructor:** captures the Laravel `Request` and exposes it as `$this->request` via `request adapter` so consuming controllers can continue to use familiar legacy-style helpers (`getGet`, `getJSON`, etc.). ## BroadcastEmailController **File:** `app/Http/Controllers/Api/BroadcastEmailController.php` @@ -121,14 +121,14 @@ This document lists every API controller alphabetically with any inline route co - `PATCH /api/v1/calendar/{id}` (`update`) – Allows partial updates to the same fields; returns the refreshed record. - `DELETE /api/v1/calendar/{id}` (`delete`) – Removes the event and returns the standard success envelope. -## CiRequestAdapter -**File:** `app/Http/Controllers/Api/CiRequestAdapter.php` -**Purpose:** Lightweight shim that lets the Laravel request behave like CodeIgniter’s `$this->request`. +## request adapter +**File:** `app/Http/Controllers/Api/request adapter.php` +**Purpose:** Lightweight shim that lets the Laravel request behave like legacy’s `$this->request`. -- `getGet()` / `getPost()` – Mirror CI helpers by returning either all query/form params or a single key with a default. +- `getGet()` / `getPost()` – Mirror legacy helpers by returning either all query/form params or a single key with a default. - `getJSON($assoc = false)` – Parses the raw body once and returns decoded JSON (array/object). - `getVar()` – Unified accessor that checks body/query for a key. -- `getHeader()` / `getHeaderLine()` – Pull HTTP headers using familiar CI naming. +- `getHeader()` / `getHeaderLine()` – Pull HTTP headers using familiar legacy naming. - `getFile()` – Returns the uploaded file object (if any) for the key. - `getRawInput()` – Gives the unparsed request body string. - `all()` – Aggregates query, form, and JSON payloads into a single array—handy when migrating legacy controllers. @@ -1140,7 +1140,7 @@ This document lists every API controller alphabetically with any inline route co - Purchase order header with supplier name - Array of line items with supply names and units (if supply_id maps to inventory_items) - Returns 404 if not found -- `POST /api/v1/purchase-orders` (`store`) – Create a new purchase order with line items. Supports both CodeIgniter-style form arrays and modern JSON format. Body parameters: +- `POST /api/v1/purchase-orders` (`store`) – Create a new purchase order with line items. Supports both legacy-style form arrays and modern JSON format. Body parameters: - `po_number` (string, required) – Purchase order number - `supplier_id` (integer, required) – Supplier ID - `order_date` (date, required) – Order date (Y-m-d format) @@ -1154,7 +1154,7 @@ This document lists every API controller alphabetically with any inline route co - `description` (string, optional) – Item description - `quantity` (integer, required) – Quantity ordered - `unit_cost` (float, required) – Unit cost - - **CodeIgniter-style form arrays:** + - **legacy-style form arrays:** - `item_supply_id[]` (array, required) – Array of supply IDs - `item_description[]` (array, optional) – Array of descriptions - `item_quantity[]` (array, required) – Array of quantities @@ -1183,7 +1183,7 @@ This document lists every API controller alphabetically with any inline route co - All endpoints require authentication (`auth:api` middleware) - Purchase orders support statuses: 'draft', 'ordered', 'received', 'canceled' - When receiving items, the system automatically updates inventory quantities and creates movement records -- The `store` method supports both legacy CodeIgniter form array format and modern JSON format for backward compatibility +- The `store` method supports both legacy form array format and modern JSON format for backward compatibility - Line items are validated to ensure at least one valid item with quantity > 0 - All financial calculations (subtotal, tax, total) are performed automatically - Database transactions ensure data consistency when creating POs or receiving items diff --git a/resources/docs/openapi.json b/resources/docs/openapi.json index 12e429f5..f07ce948 100644 --- a/resources/docs/openapi.json +++ b/resources/docs/openapi.json @@ -864,9 +864,9 @@ "path": "app/Http/Controllers/Api/CalendarController.php" }, { - "name": "CiRequestAdapter", - "class": "App\\Http\\Controllers\\Api\\Core\\CiRequestAdapter", - "path": "app/Http/Controllers/Api/Core/CiRequestAdapter.php" + "name": "request adapter", + "class": "App\\Http\\Controllers\\Api\\Core\\request adapter", + "path": "app/Http/Controllers/Api/Core/request adapter.php" }, { "name": "ClassController", diff --git a/routes/api.php b/routes/api.php index 156b5405..dce9fe1b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -125,12 +125,12 @@ use App\Http\Controllers\Api\ScannerController; use App\Http\Controllers\Api\Staff\TimeOffNotificationController; use App\Http\Controllers\Api\System\AccessDeniedController; use App\Http\Controllers\Api\Certificates\CertificateController; -// CodeIgniter also registers POST /api/login and POST /api/register (public, no /v1). +// Public auth aliases without the `/v1` prefix. Route::post('login', [AuthController::class, 'login']); Route::post('register', [RegisterController::class, 'store']); /* -| CodeIgniter POST /api/proofread (LanguageTool proxy; session or Bearer like CI auth filter). +| LanguageTool proxy for legacy and current clients. */ Route::post('proofread', [ProofreadController::class, 'check'])->middleware('teacher.portal.auth'); @@ -183,7 +183,7 @@ Route::permanentRedirect('attendance-comment-templates/list-data', '/api/v1/atte Route::permanentRedirect('administrator/attendance-templates', '/api/v1/administrator/attendance-templates'); Route::prefix('v1')->group(function () { - // Parity with CodeIgniter: POST /api/v1/login and POST /api/v1/register (public, no /auth prefix) + // Public auth aliases without the `/auth` prefix. Route::post('login', [AuthController::class, 'login']); Route::post('register', [RegisterController::class, 'store']); @@ -211,7 +211,7 @@ Route::prefix('v1')->group(function () { Route::post('contact', [SupportContactController::class, 'send']); /* - | Badge scan kiosk (public, throttled). Logs: authenticated staff (legacy CI RFIDController). + | Badge scan kiosk (public, throttled). Logs: authenticated staff. */ Route::post('badge_scan/scan', [BadgeScanController::class, 'scan']) ->middleware('throttle:120,1'); @@ -261,7 +261,7 @@ Route::prefix('v1')->group(function () { }); /* - | Print requests (CodeIgniter PrintRequests — teacher/admin JSON + uploads). + | Print requests for teacher/admin JSON and uploads. */ Route::middleware('auth:api')->prefix('print-requests')->group(function () { Route::middleware('print_requests.teacher')->group(function () { @@ -497,7 +497,7 @@ Route::prefix('v1')->group(function () { }); /* - | CodeIgniter View\AttendanceCommentTemplateController — legacy list/save/delete (auth:api). + | Legacy attendance template list/save/delete endpoints (auth:api). */ Route::middleware('auth:api')->prefix('attendance-templates')->group(function () { Route::get('/', [AttendanceCommentTemplateController::class, 'listData'])->name('api.v1.attendance-templates.legacy'); @@ -560,7 +560,7 @@ Route::prefix('v1')->group(function () { }); Route::get('teacher/absence-vacation', [TeacherController::class, 'absenceForm']); Route::post('teacher/absence-vacation', [TeacherController::class, 'submitAbsence']); - // Legacy CI paths: same handler as GET /api/v1/attendance/teacher/form + // Legacy paths: same handler as GET /api/v1/attendance/teacher/form Route::get('teacher/showupdate-attendance', [TeacherAttendanceApiController::class, 'form']); Route::get('teacher/showupdate_attendance', [TeacherAttendanceApiController::class, 'form']); // Legacy alias: same handler as GET /api/v1/scores/overview (teacher score dashboard) diff --git a/tests/Unit/ApiLoginSecurityServiceTest.php b/tests/Unit/ApiLoginSecurityServiceTest.php index cbf3c6c5..3b589e43 100644 --- a/tests/Unit/ApiLoginSecurityServiceTest.php +++ b/tests/Unit/ApiLoginSecurityServiceTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\TestCase; class ApiLoginSecurityServiceTest extends TestCase { - public function test_roles_object_map_matches_codeigniter_encoding(): void + public function test_roles_object_map_preserves_role_names(): void { $service = new ApiLoginSecurityService(); $map = $service->rolesToObjectMap(['Teacher', 'Admin']); @@ -27,14 +27,14 @@ class ApiLoginSecurityServiceTest extends TestCase $service = new ApiLoginSecurityService(); $user = $this->getMockBuilder(User::class) - ->onlyMethods(['roleNamesLikeCodeIgniter', 'teacherSessionContext']) + ->onlyMethods(['roleNames', 'teacherSessionContext']) ->getMock(); $user->id = 99; $user->firstname = 'Amina'; $user->lastname = 'Teacher'; - $user->method('roleNamesLikeCodeIgniter')->willReturn(['Teacher']); + $user->method('roleNames')->willReturn(['Teacher']); $user->method('teacherSessionContext')->willReturn([ 'class_section_id' => 42, 'class_section_name' => 'Grade 5 - A', @@ -56,14 +56,14 @@ class ApiLoginSecurityServiceTest extends TestCase $service = new ApiLoginSecurityService(); $user = $this->getMockBuilder(User::class) - ->onlyMethods(['roleNamesLikeCodeIgniter', 'teacherSessionContext']) + ->onlyMethods(['roleNames', 'teacherSessionContext']) ->getMock(); $user->id = 100; $user->firstname = 'Samir'; $user->lastname = 'TA'; - $user->method('roleNamesLikeCodeIgniter')->willReturn(['Teacher_Assistant']); + $user->method('roleNames')->willReturn(['Teacher_Assistant']); $user->method('teacherSessionContext')->willReturn([ 'class_section_id' => 17, 'class_section_name' => 'Grade 3 - B', diff --git a/tests/Unit/Services/Attendance/AttendanceTrackingService.php b/tests/Unit/Services/Attendance/AttendanceTrackingService.php index dffe27a1..4b7386a2 100644 --- a/tests/Unit/Services/Attendance/AttendanceTrackingService.php +++ b/tests/Unit/Services/Attendance/AttendanceTrackingService.php @@ -40,7 +40,7 @@ class AttendanceTrackingService public function getPendingViolations(?string $schoolYearParam = null, ?string $semesterParam = null): array { $schoolYear = filled($schoolYearParam) ? (string) $schoolYearParam : (string) $this->schoolYear; - $semester = filled($semesterParam) ? (string) $semesterParam : null; // disabled like CI4 + $semester = filled($semesterParam) ? (string) $semesterParam : null; // disabled like legacy $debugInfo = [ 'school_year_param' => $schoolYear, diff --git a/tools/ci-controller-to-laravel-mapping.csv b/tools/ci-controller-to-laravel-mapping.csv deleted file mode 100644 index 0d42051f..00000000 --- a/tools/ci-controller-to-laravel-mapping.csv +++ /dev/null @@ -1,106 +0,0 @@ -ci_relative_path,ci_basename,laravel_controller_paths_from_repo_root,migration_status,notes -AdminProgressController.php,AdminProgressController,app/Http/Controllers/Api/ClassProgress/ClassProgressController.php,mapped_renamed, -Admin/CompetitionWinnersController.php,CompetitionWinnersController,app/Http/Controllers/Api/Public/PublicWinnersController.php,mapped_renamed, -ApiDocsController.php,ApiDocsController,app/Http/Controllers/Api/Documentation/ApiDocsAdminController.php,mapped_renamed, -AuthController.php,AuthController,app/Http/Controllers/Api/Auth/AuthController.php,mapped_exact, -BaseController.php,BaseController,,framework_base,"CI framework base" -ClassProgressController.php,ClassProgressController,app/Http/Controllers/Api/ClassProgress/ClassProgressController.php,mapped_exact, -DocsController.php,DocsController,app/Http/Controllers/Api/Documentation/DocsCatalogController.php|app/Http/Controllers/Api/Documentation/ApiDocsAdminController.php,mapped_renamed,"Two CI files (root + View); docs split between catalog + admin" -ErrorController.php,ErrorController,app/Http/Controllers/Api/System/AccessDeniedController.php,mapped_renamed, -Home.php,Home,,stub_replaced,"CI welcome_message only; Laravel SPA + API" -InitializeRolesPermissions.php,InitializeRolesPermissions,,setup_script,"Use Laravel seeds/Artisan; not an HTTP controller" -ParentProgressController.php,ParentProgressController,app/Http/Controllers/Api/ClassProgress/ClassProgressController.php,mapped_renamed, -ParentReportCardController.php,ParentReportCardController,app/Http/Controllers/Api/Reports/ReportCardsController.php,mapped_renamed, -PrintRequests.php,PrintRequests,app/Http/Controllers/Api/PrintRequests/PrintRequestsController.php,mapped_renamed,"HTTP JSON API + PrintRequestsPortalService" -ProofreadController.php,ProofreadController,app/Http/Controllers/Api/Utilities/ProofreadController.php,mapped_exact, -TimeOffNotificationController.php,TimeOffNotificationController,app/Http/Controllers/Api/Staff/TimeOffNotificationController.php,mapped_exact, -View/AdministratorController.php,AdministratorController,app/Http/Controllers/Api/Administrator/AdministratorAbsenceController.php|app/Http/Controllers/Api/Administrator/AdministratorDashboardController.php|app/Http/Controllers/Api/Administrator/AdministratorEnrollmentController.php|app/Http/Controllers/Api/Administrator/AdministratorNotificationController.php|app/Http/Controllers/Api/Administrator/AdministratorTeacherSubmissionController.php|app/Http/Controllers/Api/Administrator/EmergencyContactController.php|app/Http/Controllers/Api/Administrator/TeacherClassAssignmentController.php,mapped_split,"Large CI controller split across Laravel admin APIs" -View/AssignmentController.php,AssignmentController,app/Http/Controllers/Api/Assignment/AssignmentApiController.php,mapped_renamed, -View/AttendanceCommentTemplateController.php,AttendanceCommentTemplateController,app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php,mapped_exact, -View/AttendanceController.php,AttendanceController,app/Http/Controllers/Api/Attendance/AdminAttendanceApiController.php|app/Http/Controllers/Api/Attendance/StaffAttendanceApiController.php|app/Http/Controllers/Api/Attendance/TeacherAttendanceApiController.php,mapped_split,"Attendance UI split by role in Laravel API" -View/AttendanceTrackingController.php,AttendanceTrackingController,app/Http/Controllers/Api/AttendanceTracking/AttendanceTrackingController.php,mapped_exact, -View/AuthorizedUsersController.php,AuthorizedUsersController,app/Http/Controllers/Api/Parents/AuthorizedUsersController.php,mapped_exact, -View/BadgesController.php,BadgesController,app/Http/Controllers/Api/Badges/BadgeController.php,mapped_renamed, -View/BroadcastEmailController.php,BroadcastEmailController,app/Http/Controllers/Api/Email/BroadcastEmailController.php,mapped_exact, -View/ClassController.php,ClassController,app/Http/Controllers/Api/Classes/ClassController.php,mapped_exact, -View/ClassPrepController.php,ClassPrepController,app/Http/Controllers/Api/ClassPreparation/ClassPreparationController.php,mapped_renamed, -View/ClassPreparationController.php,ClassPreparationController,app/Http/Controllers/Api/ClassPreparation/ClassPreparationController.php,mapped_exact, -View/CommunicationController.php,CommunicationController,app/Http/Controllers/Api/Communication/CommunicationController.php,mapped_exact, -View/CompetitionScoresController.php,CompetitionScoresController,app/Http/Controllers/Api/CompetitionScores/CompetitionScoresController.php,mapped_exact, -View/ConfigurationController.php,ConfigurationController,app/Http/Controllers/Api/Settings/ConfigurationAdminController.php,mapped_renamed, -View/ContactController.php,ContactController,app/Http/Controllers/Api/Support/ContactController.php,mapped_exact, -View/DashboardRedirectController.php,DashboardRedirectController,app/Http/Controllers/Api/System/DashboardRedirectController.php,mapped_exact, -View/DiscountController.php,DiscountController,app/Http/Controllers/Api/Discounts/DiscountController.php,mapped_exact, -View/DocsController.php,DocsController,app/Http/Controllers/Api/Documentation/DocsCatalogController.php|app/Http/Controllers/Api/Documentation/ApiDocsAdminController.php,mapped_renamed,"Two CI files (root + View); docs split between catalog + admin" -View/EmailController.php,EmailController,app/Http/Controllers/Api/Email/EmailController.php,mapped_exact, -View/EmailExtractorController.php,EmailExtractorController,app/Http/Controllers/Api/Email/EmailExtractorController.php,mapped_exact, -View/EmergencyContactController.php,EmergencyContactController,app/Http/Controllers/Api/Administrator/EmergencyContactController.php,mapped_exact, -View/EventController.php,EventController,app/Http/Controllers/Api/Settings/EventController.php,mapped_exact, -View/ExamDraftController.php,ExamDraftController,app/Http/Controllers/Api/Exams/ExamDraftController.php,mapped_exact, -View/ExpenseController.php,ExpenseController,app/Http/Controllers/Api/Expenses/ExpenseController.php,mapped_exact, -View/ExtraChargesController.php,ExtraChargesController,app/Http/Controllers/Api/ExtraCharges/ExtraChargesController.php,mapped_exact, -View/FamilyAdminController.php,FamilyAdminController,app/Http/Controllers/Api/Family/FamilyAdminController.php,mapped_exact, -View/FamilyController.php,FamilyController,app/Http/Controllers/Api/Family/FamilyController.php,mapped_exact, -View/FilesController.php,FilesController,app/Http/Controllers/Api/Reports/FilesController.php,mapped_exact, -View/FinalController.php,FinalController,app/Http/Controllers/Api/Scores/FinalController.php,mapped_exact, -View/FinancialController.php,FinancialController,app/Http/Controllers/Api/Finance/FinancialController.php,mapped_exact, -View/FlagController.php,FlagController,app/Http/Controllers/Api/Incidents/IncidentController.php,mapped_renamed, -View/FrontendController.php,FrontendController,app/Http/Controllers/Api/Frontend/FrontendController.php,mapped_exact, -View/GradingController.php,GradingController,app/Http/Controllers/Api/Grading/GradingController.php,mapped_exact, -View/HealthController.php,HealthController,app/Http/Controllers/Api/System/HealthController.php,mapped_exact, -View/HomeworkController.php,HomeworkController,app/Http/Controllers/Api/Scores/HomeworkController.php,mapped_exact, -View/HomeworkTrackingController.php,HomeworkTrackingController,app/Http/Controllers/Api/Grading/HomeworkTrackingController.php,mapped_exact, -View/InfoIconController.php,InfoIconController,app/Http/Controllers/Api/Frontend/InfoIconController.php,mapped_exact, -View/InventoryController.php,InventoryController,app/Http/Controllers/Api/Inventory/InventoryController.php,mapped_exact, -View/InvoiceController.php,InvoiceController,app/Http/Controllers/Api/Finance/InvoiceController.php,mapped_exact, -View/IpBanController.php,IpBanController,app/Http/Controllers/Api/Auth/IpBanController.php,mapped_exact, -View/LandingPageController.php,LandingPageController,app/Http/Controllers/Api/Frontend/LandingPageController.php,mapped_exact, -View/LateSlipLogsController.php,LateSlipLogsController,app/Http/Controllers/Api/Attendance/LateSlipLogsController.php,mapped_exact, -View/MessagesController.php,MessagesController,app/Http/Controllers/Api/Messaging/MessagesController.php,mapped_exact, -View/MidtermController.php,MidtermController,app/Http/Controllers/Api/Scores/MidtermController.php,mapped_exact, -View/NavBuilderController.php,NavBuilderController,app/Http/Controllers/Api/System/NavBuilderController.php,mapped_exact, -View/NotificationsController.php,NotificationsController,app/Http/Controllers/Api/Notifications/NotificationController.php,mapped_renamed, -View/PageController.php,PageController,app/Http/Controllers/Api/Frontend/PageController.php,mapped_exact, -View/ParentAttendanceReportController.php,ParentAttendanceReportController,app/Http/Controllers/Api/Parents/ParentAttendanceReportController.php,mapped_exact, -View/ParentController.php,ParentController,app/Http/Controllers/Api/Parents/ParentController.php,mapped_exact, -View/ParticipationController.php,ParticipationController,app/Http/Controllers/Api/Scores/ParticipationController.php,mapped_exact, -View/PaymentController.php,PaymentController,app/Http/Controllers/Api/Finance/PaymentController.php,mapped_exact, -View/PaymentNotificationController.php,PaymentNotificationController,app/Http/Controllers/Api/Finance/PaymentNotificationController.php,mapped_exact, -View/PaymentTransactionController.php,PaymentTransactionController,app/Http/Controllers/Api/Finance/PaymentTransactionController.php,mapped_exact, -View/PaypalTransactionsController.php,PaypalTransactionsController,app/Http/Controllers/Api/Finance/PaypalTransactionsController.php,mapped_exact, -View/PolicyController.php,PolicyController,app/Http/Controllers/Api/Settings/PolicyController.php,mapped_exact, -View/PreferencesController.php,PreferencesController,app/Http/Controllers/Api/Settings/PreferencesController.php,mapped_exact, -View/PrintRequests.php,PrintRequests,app/Http/Controllers/Api/PrintRequests/PrintRequestsController.php,mapped_renamed,"HTTP JSON API + PrintRequestsPortalService" -View/PrintablesBaseController.php,PrintablesBaseController,,framework_base,"Abstract printable base (CI)" -View/ProjectController.php,ProjectController,app/Http/Controllers/Api/Scores/ProjectController.php,mapped_exact, -View/PurchaseOrderController.php,PurchaseOrderController,app/Http/Controllers/Api/Finance/PurchaseOrderController.php,mapped_exact, -View/QuizController.php,QuizController,app/Http/Controllers/Api/Scores/QuizController.php,mapped_exact, -View/RFIDController.php,RFIDController,app/Http/Controllers/Api/BadgeScan/BadgeScanController.php|app/Http/Controllers/Api/Students/StudentController.php,mapped_split,"Scan + logs: BadgeScanController; assign student badge: StudentController.updateBadgeScan" -View/RefundController.php,RefundController,app/Http/Controllers/Api/Finance/RefundController.php,mapped_exact, -View/RegisterController.php,RegisterController,app/Http/Controllers/Api/Auth/RegisterController.php,mapped_exact, -View/ReimbursementController.php,ReimbursementController,app/Http/Controllers/Api/Finance/ReimbursementController.php,mapped_exact, -View/ReportCardsController.php,ReportCardsController,app/Http/Controllers/Api/Reports/ReportCardsController.php,mapped_exact, -View/RolePermissionController.php,RolePermissionController,app/Http/Controllers/Api/Auth/RolePermissionController.php,mapped_exact, -View/RoleSwitcherController.php,RoleSwitcherController,app/Http/Controllers/Api/Auth/RoleSwitcherController.php,mapped_exact, -View/SchoolCalendarController.php,SchoolCalendarController,app/Http/Controllers/Api/Settings/SchoolCalendarController.php,mapped_exact, -View/ScoreCommentController.php,ScoreCommentController,app/Http/Controllers/Api/Scores/ScoreCommentController.php,mapped_exact, -View/ScoreController.php,ScoreController,app/Http/Controllers/Api/Scores/ScoreController.php,mapped_exact, -View/ScorePredictor.php,ScorePredictor,app/Http/Controllers/Api/Scores/ScorePredictorController.php,mapped_renamed, -View/SendSMSController.php,SendSMSController,app/Http/Controllers/Api/Messaging/MessagesController.php,mapped_renamed, -View/SessionTimeoutController.php,SessionTimeoutController,app/Http/Controllers/Api/Auth/SessionTimeoutController.php,mapped_exact, -View/SettingsController.php,SettingsController,app/Http/Controllers/Api/Settings/SettingsController.php,mapped_exact, -View/SlipPrinterController.php,SlipPrinterController,app/Http/Controllers/Api/Reports/SlipPrinterController.php,mapped_exact, -View/StaffController.php,StaffController,app/Http/Controllers/Api/Staff/StaffController.php,mapped_exact, -View/StatsController.php,StatsController,app/Http/Controllers/Api/System/StatsController.php,mapped_exact, -View/StickersController.php,StickersController,app/Http/Controllers/Api/Reports/StickersController.php,mapped_exact, -View/StudentController.php,StudentController,app/Http/Controllers/Api/Students/StudentController.php,mapped_exact, -View/SubjectCurriculumController.php,SubjectCurriculumController,app/Http/Controllers/Api/Subjects/SubjectCurriculumController.php,mapped_exact, -View/SupplierController.php,SupplierController,app/Http/Controllers/Api/Inventory/SupplierController.php,mapped_exact, -View/SupplyCategoryController.php,SupplyCategoryController,app/Http/Controllers/Api/Inventory/SupplyCategoryController.php,mapped_exact, -View/SupportController.php,SupportController,app/Http/Controllers/Api/Support/SupportController.php,mapped_exact, -View/TeacherController.php,TeacherController,app/Http/Controllers/Api/Staff/TeacherController.php,mapped_exact, -View/TestDBController.php,TestDBController,app/Http/Controllers/Api/System/DatabaseHealthController.php|app/Http/Controllers/Api/System/HealthController.php,mapped_renamed, -View/UiController.php,UiController,app/Http/Controllers/Api/Ui/UiController.php,mapped_exact, -View/UserController.php,UserController,app/Http/Controllers/Api/Users/UserController.php,mapped_exact, -View/WhatsappController.php,WhatsappController,app/Http/Controllers/Api/Messaging/WhatsappController.php,mapped_exact, -WinnersController.php,WinnersController,app/Http/Controllers/Api/Public/PublicWinnersController.php,mapped_renamed, diff --git a/tools/generate-ci-controller-mapping.php b/tools/generate-ci-controller-mapping.php deleted file mode 100644 index 3633d18b..00000000 --- a/tools/generate-ci-controller-mapping.php +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env php - */ -$explicit = [ - 'AdminProgressController' => ['paths' => ['Api/ClassProgress/ClassProgressController.php'], 'notes' => ''], - 'ParentProgressController' => ['paths' => ['Api/ClassProgress/ClassProgressController.php'], 'notes' => ''], - 'ParentReportCardController' => ['paths' => ['Api/Reports/ReportCardsController.php'], 'notes' => ''], - 'ApiDocsController' => ['paths' => ['Api/Documentation/ApiDocsAdminController.php'], 'notes' => ''], - 'DocsController' => [ - 'paths' => ['Api/Documentation/DocsCatalogController.php', 'Api/Documentation/ApiDocsAdminController.php'], - 'notes' => 'Two CI files (root + View); docs split between catalog + admin', - ], - 'ConfigurationController' => ['paths' => ['Api/Settings/ConfigurationAdminController.php'], 'notes' => ''], - 'ClassPrepController' => ['paths' => ['Api/ClassPreparation/ClassPreparationController.php'], 'notes' => ''], - 'BadgesController' => ['paths' => ['Api/Badges/BadgeController.php'], 'notes' => ''], - 'NotificationsController' => ['paths' => ['Api/Notifications/NotificationController.php'], 'notes' => ''], - 'FlagController' => ['paths' => ['Api/Incidents/IncidentController.php'], 'notes' => ''], - 'SendSMSController' => ['paths' => ['Api/Messaging/MessagesController.php'], 'notes' => ''], - 'TestDBController' => [ - 'paths' => ['Api/System/DatabaseHealthController.php', 'Api/System/HealthController.php'], - 'notes' => '', - ], - 'ErrorController' => ['paths' => ['Api/System/AccessDeniedController.php'], 'notes' => ''], - 'ScorePredictor' => ['paths' => ['Api/Scores/ScorePredictorController.php'], 'notes' => ''], - 'CompetitionWinnersController' => ['paths' => ['Api/Public/PublicWinnersController.php'], 'notes' => ''], - 'WinnersController' => ['paths' => ['Api/Public/PublicWinnersController.php'], 'notes' => ''], - 'AssignmentController' => ['paths' => ['Api/Assignment/AssignmentApiController.php'], 'notes' => ''], - 'Home' => ['paths' => [], 'notes' => 'CI welcome_message only; Laravel SPA + API'], - 'BaseController' => ['paths' => [], 'notes' => 'CI framework base'], - 'PrintablesBaseController' => ['paths' => [], 'notes' => 'Abstract printable base (CI)'], - 'InitializeRolesPermissions' => ['paths' => [], 'notes' => 'Use Laravel seeds/Artisan; not an HTTP controller'], - 'PrintRequests' => [ - 'paths' => ['Api/PrintRequests/PrintRequestsController.php'], - 'notes' => 'HTTP JSON API + PrintRequestsPortalService', - ], - 'RFIDController' => [ - 'paths' => [ - 'Api/BadgeScan/BadgeScanController.php', - 'Api/Students/StudentController.php', - ], - 'notes' => 'Scan + logs: BadgeScanController; assign student badge: StudentController.updateBadgeScan', - ], - 'AdministratorController' => [ - 'paths' => [ - 'Api/Administrator/AdministratorAbsenceController.php', - 'Api/Administrator/AdministratorDashboardController.php', - 'Api/Administrator/AdministratorEnrollmentController.php', - 'Api/Administrator/AdministratorNotificationController.php', - 'Api/Administrator/AdministratorTeacherSubmissionController.php', - 'Api/Administrator/EmergencyContactController.php', - 'Api/Administrator/TeacherClassAssignmentController.php', - ], - 'notes' => 'Large CI controller split across Laravel admin APIs', - ], - 'AttendanceController' => [ - 'paths' => [ - 'Api/Attendance/AdminAttendanceApiController.php', - 'Api/Attendance/StaffAttendanceApiController.php', - 'Api/Attendance/TeacherAttendanceApiController.php', - ], - 'notes' => 'Attendance UI split by role in Laravel API', - ], -]; - -/** @return array basename => list of relative paths under Http/Controllers */ -function indexLaravelControllers(string $controllersDir): array -{ - $map = []; - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($controllersDir, FilesystemIterator::SKIP_DOTS) - ); - $prefixLen = strlen($controllersDir) + 1; - - foreach ($iterator as $fileInfo) { - /** @var SplFileInfo $fileInfo */ - if (! $fileInfo->isFile() || strcasecmp($fileInfo->getExtension(), 'php') !== 0) { - continue; - } - if ($fileInfo->getFilename() === 'Controller.php') { - continue; - } - $full = $fileInfo->getPathname(); - $rel = str_replace('\\', '/', substr($full, $prefixLen)); - $base = $fileInfo->getBasename('.php'); - if (! isset($map[$base])) { - $map[$base] = []; - } - $map[$base][] = $rel; - } - - return $map; -} - -/** @param string[] $paths */ -function joinRepoPaths(array $paths): string -{ - $prefixed = array_map( - static fn (string $p): string => 'app/Http/Controllers/'.$p, - array_filter($paths) - ); - - return implode('|', $prefixed); -} - -function resolvePaths(string $basename, array $explicit, array $lvFiles): array -{ - if (isset($explicit[$basename])) { - return $explicit[$basename]; - } - if (isset($lvFiles[$basename])) { - return ['paths' => $lvFiles[$basename], 'notes' => '']; - } - - return ['paths' => [], 'notes' => 'NOT FOUND IN Laravel app/Http/Controllers']; -} - -function migrationStatus(string $basename, array $resolved, array $explicit, array $lvFiles): string -{ - if (isset($explicit[$basename])) { - $e = $explicit[$basename]; - if ($basename === 'AdministratorController') { - return 'mapped_split'; - } - if ($basename === 'AttendanceController') { - return 'mapped_split'; - } - if ($basename === 'RFIDController') { - return 'mapped_split'; - } - if ($basename === 'Home') { - return 'stub_replaced'; - } - if (in_array($basename, ['BaseController', 'PrintablesBaseController'], true)) { - return 'framework_base'; - } - if ($basename === 'InitializeRolesPermissions') { - return 'setup_script'; - } - if ($e['paths'] === []) { - return 'no_laravel_controller'; - } - if ($basename === 'DocsController') { - return 'mapped_renamed'; - } - $renamed = [ - 'AdminProgressController', 'ParentProgressController', 'ParentReportCardController', 'ApiDocsController', - 'ConfigurationController', 'ClassPrepController', 'BadgesController', 'NotificationsController', - 'FlagController', 'SendSMSController', 'TestDBController', 'ErrorController', 'ScorePredictor', - 'CompetitionWinnersController', 'WinnersController', 'AssignmentController', 'PrintRequests', - ]; - if (in_array($basename, $renamed, true)) { - return 'mapped_renamed'; - } - - return 'mapped_explicit'; - } - - if (isset($lvFiles[$basename])) { - return count($lvFiles[$basename]) > 1 ? 'ambiguous_duplicate_in_laravel' : 'mapped_exact'; - } - - return 'unmapped'; -} - -$lvFiles = indexLaravelControllers($lvControllersDir); - -$ciFiles = []; -$ciIterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($ciRoot, FilesystemIterator::SKIP_DOTS) -); -$ciPrefixLen = strlen($ciRoot) + 1; - -foreach ($ciIterator as $fileInfo) { - if (! $fileInfo->isFile() || strcasecmp($fileInfo->getExtension(), 'php') !== 0) { - continue; - } - $full = $fileInfo->getPathname(); - $rel = str_replace('\\', '/', substr($full, $ciPrefixLen)); - $ciFiles[$full] = $rel; -} -ksort($ciFiles); - -$rows = []; -foreach ($ciFiles as $full => $relPath) { - $basename = basename($full, '.php'); - $resolved = resolvePaths($basename, $explicit, $lvFiles); - $pathStr = joinRepoPaths($resolved['paths']); - $notes = $resolved['notes']; - $status = migrationStatus($basename, $resolved, $explicit, $lvFiles); - - if ($status === 'unmapped') { - $pathStr = ''; - if ($notes === '') { - $notes = 'NOT FOUND IN Laravel app/Http/Controllers'; - } - } - - $rows[] = [$relPath, $basename, $pathStr, $status, $notes]; -} - -$dir = dirname($outputPath); -if (! is_dir($dir)) { - mkdir($dir, 0775, true); -} - -$fh = fopen($outputPath, 'wb'); -if ($fh === false) { - fwrite(STDERR, "Cannot write: {$outputPath}\n"); - exit(1); -} - -$headers = ['ci_relative_path', 'ci_basename', 'laravel_controller_paths_from_repo_root', 'migration_status', 'notes']; -fputcsv($fh, $headers); -foreach ($rows as $row) { - fputcsv($fh, $row); -} -fclose($fh); - -fwrite(STDOUT, "Wrote ".count($rows)." rows to {$outputPath}\n"); diff --git a/tools/sync_fillable_from_codeigniter.mjs b/tools/sync_fillable_from_codeigniter.mjs deleted file mode 100644 index f025d309..00000000 --- a/tools/sync_fillable_from_codeigniter.mjs +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Sync Laravel $fillable + $timestamps from CodeIgniter *Model.php (sibling app_codeigniter). - * Run: node tools/sync_fillable_from_codeigniter.mjs - */ -import { readdir, readFile, writeFile } from "node:fs/promises"; -import { join, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const laravelRoot = join(__dirname, ".."); -const workspaceRoot = join(laravelRoot, ".."); -const ciModelsDir = join(workspaceRoot, "app_codeigniter", "app", "Models"); -const laravelModelsDir = join(laravelRoot, "app", "Models"); - -const manualCiToLaravel = new Map([ - ["SettingsModel.php", "Setting.php"], -]); - -/** CI models that must not sync (legacy duplicate of another model). */ -const skipCiBasenames = new Set([ - "FlagModel.php", // table `flag`; Laravel uses CurrentFlagModel → current_flag -]); - -const skipLaravel = new Set([ - "Teacher.php", - "Communication.php", - "EventCharge.php", - "ClassModel.php", - "Calendar.php", -]); - -function ciFileToLaravel(ciBasename) { - if (manualCiToLaravel.has(ciBasename)) return manualCiToLaravel.get(ciBasename); - const m = ciBasename.match(/^(.+)Model\.php$/); - if (!m) return null; - const base = m[1]; - if (base === "Parent" || base === "Class") return `${base}Model.php`; - return `${base}.php`; -} - -/** Find array after `protected $allowedFields =` (flexible spacing). */ -function extractAllowedFieldsArrayLiteral(src) { - const m = src.match(/protected\s+\$allowedFields\s*=\s*/); - if (!m || m.index === undefined) return null; - let pos = m.index + m[0].length; - while (pos < src.length && /\s/.test(src[pos])) pos++; - if (pos >= src.length || src[pos] !== "[") return null; - const start = pos; - let depth = 0; - for (let i = pos; i < src.length; i++) { - const c = src[i]; - if (c === "[") depth++; - else if (c === "]") { - depth--; - if (depth === 0) return src.slice(start, i + 1); - } - } - return null; -} - -function parseQuotedStrings(arrayLiteral) { - const out = []; - const re = /'((?:\\'|[^'])*)'/g; - let m; - while ((m = re.exec(arrayLiteral))) out.push(m[1].replace(/\\'/g, "'")); - return out; -} - -function renderFillable(items) { - const lines = items.map((it) => { - const escaped = String(it).replace(/\\/g, "\\\\").replace(/'/g, "\\'"); - return ` '${escaped}',`; - }); - return `[\n${lines.join("\n")}\n ]`; -} - -function replaceFillable(lv, inner) { - const re = /protected\s+\$fillable\s*=\s*\[[\s\S]*?\];/m; - if (!re.test(lv)) return null; - return lv.replace(re, `protected $fillable = ${inner};`); -} - -function replaceTimestamps(lv, useTs) { - const line = useTs ? " public $timestamps = true;" : " public $timestamps = false;"; - // Normalize any leading whitespace so re-sync does not duplicate lines or pile up indent. - if (/^\s*public\s+\$timestamps\s*=\s*(true|false)\s*;/m.test(lv)) - return lv.replace(/^\s*public\s+\$timestamps\s*=\s*(true|false)\s*;/m, line); - return lv.replace(/(class\s+\w+[^\n]*\n\{)/, `$1\n${line}\n`); -} - -let updated = 0, - skipped = 0; -const missing = []; - -const files = await readdir(ciModelsDir).catch(() => []); -for (const ciBasename of files) { - if (!ciBasename.endsWith("Model.php")) continue; - if (skipCiBasenames.has(ciBasename)) { - console.error(`Skip CI (legacy): ${ciBasename}`); - skipped++; - continue; - } - const laravelBasename = ciFileToLaravel(ciBasename); - if (!laravelBasename) continue; - if (skipLaravel.has(laravelBasename)) { - console.error(`Skip (alias/base): ${laravelBasename}`); - skipped++; - continue; - } - - const lvPath = join(laravelModelsDir, laravelBasename); - try { - await readFile(lvPath, "utf8"); - } catch { - missing.push(`${laravelBasename} (from ${ciBasename})`); - continue; - } - - const ciSrc = await readFile(join(ciModelsDir, ciBasename), "utf8"); - let lvSrc = await readFile(lvPath, "utf8"); - - let useTs = true; - const tm = ciSrc.match(/protected\s+\$useTimestamps\s*=\s*(true|false)\s*;/); - if (tm) useTs = tm[1] === "true"; - - const allowedBody = extractAllowedFieldsArrayLiteral(ciSrc); - if (!allowedBody) { - console.error(`No allowedFields in ${ciBasename}`); - skipped++; - continue; - } - - const fields = parseQuotedStrings(allowedBody); - if (!fields.length) { - console.error(`Empty allowedFields in ${ciBasename}`); - skipped++; - continue; - } - - if (!/protected\s+\$fillable\s*=\s*\[/.test(lvSrc)) { - console.error(`No $fillable in ${laravelBasename}`); - skipped++; - continue; - } - - const rendered = renderFillable(fields); - let next = replaceFillable(lvSrc, rendered); - if (next == null) { - skipped++; - continue; - } - next = replaceTimestamps(next, useTs); - - if (next === lvSrc) { - console.error(`Unchanged: ${laravelBasename}`); - skipped++; - continue; - } - - await writeFile(lvPath, next, "utf8"); - console.log(`Updated ${laravelBasename}`); - updated++; -} - -console.log(`\nDone. Updated: ${updated}, skipped: ${skipped}`); -if (missing.length) { - console.error(`Missing (${missing.length}): ${missing.slice(0, 25).join(", ")}${missing.length > 25 ? "…" : ""}`); - process.exitCode = 2; -} diff --git a/tools/sync_fillable_from_codeigniter.php b/tools/sync_fillable_from_codeigniter.php deleted file mode 100644 index 28380c5d..00000000 --- a/tools/sync_fillable_from_codeigniter.php +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env php - Laravel model basename */ -$manualCiToLaravel = [ - 'FlagModel.php' => 'CurrentFlag.php', -]; - -/** Laravel files skipped (aliases / inheritance) */ -$skipLaravelBasenames = [ - 'Teacher.php', - 'Communication.php', - 'EventCharge.php', - 'ClassModel.php', - 'Calendar.php', -]; - -function ciFileToLaravelFile(string $ciBasename, array $manual): ?string -{ - if (isset($manual[$ciBasename])) { - return $manual[$ciBasename]; - } - if (! preg_match('/^(.+)Model\.php$/', $ciBasename, $m)) { - return null; - } - $base = $m[1]; - if ($base === 'Parent' || $base === 'Class') { - return $base.'Model.php'; - } - - return $base.'.php'; -} - -function extractFirstBracketArray(string $src, string $needle): ?string -{ - $pos = strpos($src, $needle); - if ($pos === false) { - return null; - } - $pos += strlen($needle); - $len = strlen($src); - while ($pos < $len && ctype_space($src[$pos])) { - $pos++; - } - if ($pos >= $len || $src[$pos] !== '[') { - return null; - } - $start = $pos; - $depth = 0; - for ($i = $pos; $i < $len; $i++) { - $c = $src[$i]; - if ($c === '[') { - $depth++; - } elseif ($c === ']') { - $depth--; - if ($depth === 0) { - return substr($src, $start, $i - $start + 1); - } - } - } - - return null; -} - -/** @return list */ -function parseQuotedStringsFromArrayLiteral(string $arrayLiteral): array -{ - $out = []; - if (! preg_match_all("/'((?:\\\\'|[^'])*)'/", $arrayLiteral, $m)) { - return $out; - } - foreach ($m[1] as $raw) { - $out[] = str_replace("\\'", "'", $raw); - } - - return $out; -} - -/** Render inner array only: [\n 'a',\n ] */ -function renderFillableArray(array $items): string -{ - $lines = []; - foreach ($items as $item) { - $escaped = addcslashes($item, "\\'"); - $lines[] = " '".$escaped."',"; - } - - return "[\n".implode("\n", $lines)."\n ]"; -} - -function replaceFillableBlock(string $laravel, string $newArrayLiteral): string -{ - $re = '/protected\s+\$fillable\s*=\s*\[[\s\S]*?\];/m'; - if (! preg_match($re, $laravel)) { - return $laravel; - } - - return preg_replace($re, 'protected $fillable = '.$newArrayLiteral.';', $laravel, 1); -} - -function replaceTimestampsLine(string $laravel, bool $useTimestamps): string -{ - $line = $useTimestamps - ? ' public $timestamps = true;' - : ' public $timestamps = false;'; - - if (preg_match('/public\s+\$timestamps\s*=\s*(true|false)\s*;/', $laravel)) { - return preg_replace( - '/public\s+\$timestamps\s*=\s*(true|false)\s*;/', - $line, - $laravel, - 1 - ); - } - - // Insert after class opening brace line (first line after "class X extends Y") - return preg_replace( - '/(class\s+\w+[^\n]*\n\{[\t ]*\n)/', - '$1'.$line.NL.NL, - $laravel, - 1 - ) ?: preg_replace('/(class\s+\w+[^\n]*\n\{)/', '$1'.NL.$line, $laravel, 1); -} - -$updated = 0; -$skipped = 0; -$missing = []; - -foreach (glob($ciModelsDir.DIRECTORY_SEPARATOR.'*Model.php') ?: [] as $ciPath) { - $ciBasename = basename($ciPath); - $laravelBasename = ciFileToLaravelFile($ciBasename, $manualCiToLaravel); - if ($laravelBasename === null) { - continue; - } - if (in_array($laravelBasename, $skipLaravelBasenames, true)) { - fwrite(STDERR, "Skip (alias/base): {$laravelBasename}".NL); - $skipped++; - - continue; - } - - $lvPath = $laravelModelsDir.DIRECTORY_SEPARATOR.$laravelBasename; - if (! is_file($lvPath)) { - $missing[] = $laravelBasename.' (from '.$ciBasename.')'; - - continue; - } - - $ciSrc = (string) file_get_contents($ciPath); - $lvSrc = (string) file_get_contents($lvPath); - - $useTs = true; - if (preg_match('/protected\s+\$useTimestamps\s*=\s*(true|false)\s*;/', $ciSrc, $tm)) { - $useTs = $tm[1] === 'true'; - } - - $allowedBody = extractFirstBracketArray($ciSrc, 'protected $allowedFields = '); - if ($allowedBody === null) { - fwrite(STDERR, "No allowedFields in {$ciBasename}".NL); - $skipped++; - - continue; - } - - $fields = parseQuotedStringsFromArrayLiteral($allowedBody); - if ($fields === []) { - fwrite(STDERR, "Empty allowedFields in {$ciBasename}".NL); - $skipped++; - - continue; - } - - if (! preg_match('/protected\s+\$fillable\s*=\s*\[/', $lvSrc)) { - fwrite(STDERR, "No \$fillable array in {$laravelBasename} — add manually".NL); - $skipped++; - - continue; - } - - $rendered = renderFillableArray($fields); - $newLv = replaceFillableBlock($lvSrc, $rendered); - $newLv = replaceTimestampsLine($newLv, $useTs); - - if ($newLv === $lvSrc) { - fwrite(STDERR, "Unchanged: {$laravelBasename}".NL); - $skipped++; - - continue; - } - - file_put_contents($lvPath, $newLv); - fwrite(STDOUT, "Updated {$laravelBasename}".NL); - $updated++; -} - -fwrite(STDOUT, NL.'Done. Updated: '.$updated.', skipped: '.$skipped.NL); -if ($missing !== []) { - fwrite(STDERR, 'Missing Laravel files ('.count($missing).'): '.implode(', ', array_slice($missing, 0, 20)).(count($missing) > 20 ? '…' : '').NL); - - exit(2); -}