reconstruction of the project
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
<?php namespace App\Models;
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\BaseModel;
|
||||
|
||||
class WhatsappGroupMembership extends BaseModel
|
||||
{
|
||||
protected $table = 'whatsapp_group_memberships';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'class_section_id',
|
||||
'school_year',
|
||||
@@ -18,40 +21,126 @@ class WhatsappGroupMembership extends BaseModel
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
public $timestamps = true; // created_at, updated_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 (CI-compatible behavior)
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetch all membership rows for the given sections and term.
|
||||
* Returns an associative index ["<sid>:<type>:<id>" => row].
|
||||
* Returns an associative index ["<sid>:<type>:<id>" => row(array)].
|
||||
*/
|
||||
public function getBySectionsAndTerm(array $sectionIds, string $year, string $sem): array
|
||||
public static function getBySectionsAndTerm(array $sectionIds, string $year, string $sem): array
|
||||
{
|
||||
if (empty($sectionIds)) return [];
|
||||
$ids = array_values(array_unique(array_filter(array_map('intval', $sectionIds))));
|
||||
if (empty($ids)) return [];
|
||||
|
||||
$rows = $this->asArray()
|
||||
->whereIn('class_section_id', array_values(array_unique(array_map('intval', $sectionIds))))
|
||||
->where('school_year', trim($year))
|
||||
->where('semester', trim($sem))
|
||||
->findAll();
|
||||
$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 $r) {
|
||||
$k = sprintf('%d:%s:%d', (int)$r['class_section_id'], (string)$r['subject_type'], (int)$r['subject_id']);
|
||||
$out[$k] = $r;
|
||||
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 (parent) and class/term.
|
||||
* Upsert membership row for a specific subject and class/term.
|
||||
* Returns the row ID.
|
||||
*
|
||||
* Matches CI 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 function upsertMembership(int $classSectionId, string $year, string $sem, string $type, int $subjectId, bool $isMember, ?int $verifiedBy = null): int
|
||||
{
|
||||
$type = $type === 'second' ? 'second' : 'primary';
|
||||
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' => trim($year),
|
||||
'semester' => trim($sem),
|
||||
'school_year' => $year,
|
||||
'semester' => $sem,
|
||||
'subject_type' => $type,
|
||||
'subject_id' => $subjectId,
|
||||
'is_member' => $isMember ? 1 : 0,
|
||||
@@ -59,23 +148,69 @@ class WhatsappGroupMembership extends BaseModel
|
||||
|
||||
if ($verifiedBy) {
|
||||
$payload['verified_by'] = $verifiedBy;
|
||||
$payload['verified_at'] = utc_now();
|
||||
$payload['verified_at'] = now();
|
||||
}
|
||||
|
||||
$existing = $this->asArray()
|
||||
->where('class_section_id', $payload['class_section_id'])
|
||||
->where('school_year', $payload['school_year'])
|
||||
->where('semester', $payload['semester'])
|
||||
->where('subject_type', $payload['subject_type'])
|
||||
->where('subject_id', $payload['subject_id'])
|
||||
// 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 CI 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) {
|
||||
$this->update((int)$existing['id'], $payload);
|
||||
return (int)$existing['id'];
|
||||
// Keep semester from the existing row if present (CI behavior).
|
||||
if (!empty($existing->semester)) {
|
||||
$payload['semester'] = (string) $existing->semester;
|
||||
}
|
||||
|
||||
$existing->fill($payload);
|
||||
$existing->save();
|
||||
|
||||
return (int) $existing->id;
|
||||
}
|
||||
|
||||
return (int) $this->insert($payload, true);
|
||||
// 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'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user