Files
alrahma_sunday_school_api/app/Models/WhatsappGroupMembership.php
T
2026-06-09 02:32:58 -04:00

215 lines
6.9 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use App\Models\BaseModel;
class WhatsappGroupMembership extends BaseModel
{
protected $table = 'whatsapp_group_memberships';
protected $fillable = [
'class_section_id',
'school_year',
'semester',
'subject_type',
'primary',
'second',
'subject_id',
'is_member',
'verified_by',
'verified_at',
];
public $timestamps = true; // created_at, updated_at
protected $casts = [
'class_section_id' => 'integer',
'subject_id' => 'integer',
'is_member' => 'boolean',
'verified_by' => 'integer',
'verified_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/* ============================================================
* Constants
* ============================================================
*/
public const TYPE_PRIMARY = 'primary';
public const TYPE_SECOND = 'second';
public static function normType(string $type): string
{
return strtolower(trim($type)) === self::TYPE_SECOND ? self::TYPE_SECOND : self::TYPE_PRIMARY;
}
/* ============================================================
* Scopes
* ============================================================
*/
public function scopeForSections(Builder $q, array $sectionIds): Builder
{
$ids = array_values(array_unique(array_filter(array_map('intval', $sectionIds))));
return empty($ids) ? $q->whereRaw('1=0') : $q->whereIn('class_section_id', $ids);
}
public function scopeForYear(Builder $q, string $year): Builder
{
return $q->where('school_year', trim($year));
}
public function scopeForSemester(Builder $q, string $sem): Builder
{
$sem = trim($sem);
return $sem === '' ? $q : $q->where('semester', $sem);
}
public function scopeForSubject(Builder $q, string $type, int $subjectId): Builder
{
return $q->where('subject_type', static::normType($type))
->where('subject_id', $subjectId);
}
/* ============================================================
* Methods (legacy-compatible behavior)
* ============================================================
*/
/**
* Fetch all membership rows for the given sections and term.
* Returns an associative index ["<sid>:<type>:<id>" => row(array)].
*/
public static function getBySectionsAndTerm(array $sectionIds, string $year, string $sem): array
{
$ids = array_values(array_unique(array_filter(array_map('intval', $sectionIds))));
if (empty($ids)) return [];
$year = trim($year);
$sem = trim($sem);
$q = static::query()
->forSections($ids)
->forYear($year);
if ($sem !== '') {
$q->where('semester', $sem);
}
// Prefer most recently updated membership when duplicates exist.
$rows = $q->orderByDesc('updated_at')
->orderByDesc('id')
->get();
$out = [];
foreach ($rows as $m) {
$k = sprintf('%d:%s:%d', (int)$m->class_section_id, (string)$m->subject_type, (int)$m->subject_id);
if (!isset($out[$k])) {
$out[$k] = $m->toArray();
}
}
return $out;
}
/**
* Upsert membership row for a specific subject and class/term.
* Returns the row ID.
*
* 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.
*/
public static function upsertMembership(
int $classSectionId,
string $year,
string $sem,
string $type,
int $subjectId,
bool $isMember,
?int $verifiedBy = null
): int {
$type = static::normType($type);
$year = trim($year);
$sem = trim($sem);
$payload = [
'class_section_id' => $classSectionId,
'school_year' => $year,
'semester' => $sem,
'subject_type' => $type,
'subject_id' => $subjectId,
'is_member' => $isMember ? 1 : 0,
];
if ($verifiedBy) {
$payload['verified_by'] = $verifiedBy;
$payload['verified_at'] = now();
}
// If semester is provided, we can do a clean updateOrCreate.
if ($sem !== '') {
$row = static::query()->updateOrCreate(
[
'class_section_id' => $classSectionId,
'school_year' => $year,
'semester' => $sem,
'subject_type' => $type,
'subject_id' => $subjectId,
],
$payload
);
return (int) $row->id;
}
// 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)
->where('subject_type', $type)
->where('subject_id', $subjectId)
->orderByDesc('updated_at')
->orderByDesc('id')
->first();
if ($existing) {
// Keep semester from the existing row if present (legacy behavior).
if (!empty($existing->semester)) {
$payload['semester'] = (string) $existing->semester;
}
$existing->fill($payload);
$existing->save();
return (int) $existing->id;
}
// No existing row => create a new one (semester remains blank)
$created = static::query()->create($payload);
return (int) $created->id;
}
/* ============================================================
* Optional validation helper
* ============================================================
*/
public static function rules(bool $updating = false): array
{
$req = $updating ? 'sometimes' : 'required';
return [
'class_section_id' => [$req, 'integer', 'min:1'],
'school_year' => [$req, 'string', 'max:20'],
'semester' => ['nullable', 'string', 'max:20'],
'subject_type' => [$req, 'string', 'in:' . implode(',', [self::TYPE_PRIMARY, self::TYPE_SECOND])],
'subject_id' => [$req, 'integer', 'min:1'],
'is_member' => ['nullable', 'boolean'],
'verified_by' => ['nullable', 'integer'],
'verified_at' => ['nullable', 'date'],
];
}
}