'integer', 'role_id' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; /* ============================================================ * Relationships * ============================================================ */ public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); } public function role(): BelongsTo { return $this->belongsTo(Role::class, 'role_id'); } /* ============================================================ * Scopes * ============================================================ */ public function scopeForUser(Builder $q, int $userId): Builder { return $q->where('user_id', $userId); } public function scopeForRole(Builder $q, int $roleId): Builder { return $q->where('role_id', $roleId); } public function scopeForSchoolYear(Builder $q, ?string $schoolYear): Builder { if ($schoolYear === null || trim($schoolYear) === '') return $q; return $q->where('school_year', $schoolYear); } /* ============================================================ * CI-compatible methods (converted) * ============================================================ */ /** * CI: getRolesByUserId($userId) * Returns: [ ['role_name' => 'parent'], ['role_name' => 'teacher'] ] */ public static function getRolesByUserId(int $userId): array { return static::query() ->selectRaw('roles.name as role_name') ->join('roles', 'roles.id', '=', 'user_roles.role_id') ->where('user_roles.user_id', $userId) ->whereNull('user_roles.deleted_at') ->orderBy('roles.priority', 'asc') ->orderBy('roles.name', 'asc') ->get() ->map(fn ($r) => ['role_name' => (string) $r->role_name]) ->all(); } /** * CI: updateOrInsertRole($userId, $roleId) * Idempotent (does nothing if already exists and not soft-deleted). * If the row exists but is soft-deleted, restores it. */ public static function updateOrInsertRole(int $userId, int $roleId, ?int $editorId = null): bool { $editorId = $editorId ?? (auth()->id() ?? null); $existing = static::withTrashed() ->where('user_id', $userId) ->where('role_id', $roleId) ->first(); if ($existing) { if ($existing->trashed()) { $existing->restore(); } // keep updated_by fresh if ($editorId !== null) { $existing->updated_by = $editorId; $existing->save(); } return true; } static::query()->create([ 'user_id' => $userId, 'role_id' => $roleId, 'updated_by' => $editorId, ]); return true; } /* ============================================================ * Replacement for the CI "create()" method (query only) * ============================================================ * In Laravel, view rendering belongs in Controllers, not Models. * This returns the user list that your CI method was building. * * CI logic: * users joined to roles through user_roles, excluding parent/teacher, * groupBy users.id to avoid duplicates. */ public static function usersExcludingRoles(array $excludedRoleNames = ['parent', 'teacher']): array { $excluded = array_values(array_unique(array_filter(array_map('strval', $excludedRoleNames)))); $excludedLower = array_map('strtolower', $excluded); $placeholders = implode(',', array_fill(0, count($excludedLower), '?')); return DB::table('users') ->select('users.*') ->join('user_roles', 'user_roles.user_id', '=', 'users.id') ->join('roles', 'roles.id', '=', 'user_roles.role_id') ->whereNull('user_roles.deleted_at') ->whereRaw("LOWER(roles.name) NOT IN ($placeholders)", $excludedLower) ->groupBy('users.id') ->orderBy('users.lastname', 'asc') ->orderBy('users.firstname', 'asc') ->get() ->map(fn ($r) => (array) $r) ->all(); } /* ============================================================ * Optional validation helper * ============================================================ */ public static function rules(bool $updating = false): array { $req = $updating ? 'sometimes' : 'required'; return [ 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], 'role_id' => [$req, 'integer', 'min:1', 'exists:roles,id'], 'semester' => ['nullable', 'string', 'max:20'], 'school_year'=> ['nullable', 'string', 'max:20'], 'updated_by' => ['nullable', 'integer'], ]; } }