add family logic
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Families;
|
||||
|
||||
use App\Models\Family;
|
||||
use App\Models\FamilyGuardian;
|
||||
use App\Models\FamilyStudent;
|
||||
use App\Models\Student;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class FamilyMutationService
|
||||
{
|
||||
public function bootstrapFamilies(): array
|
||||
{
|
||||
$this->ensureTablesExist(['families', 'family_students', 'family_guardians']);
|
||||
|
||||
return DB::transaction(function () {
|
||||
$primaryParents = DB::table('students')
|
||||
->select('parent_id')
|
||||
->whereNotNull('parent_id')
|
||||
->distinct()
|
||||
->get()
|
||||
->map(fn ($r) => (array) $r)
|
||||
->all();
|
||||
|
||||
$created = 0;
|
||||
$linkedStudents = 0;
|
||||
$linkedGuardians = 0;
|
||||
|
||||
foreach ($primaryParents as $row) {
|
||||
$primaryUserId = (int) ($row['parent_id'] ?? 0);
|
||||
if ($primaryUserId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$familyId = $this->ensureFamilyForPrimaryParent($primaryUserId, $created);
|
||||
|
||||
$students = DB::table('students')
|
||||
->select('id')
|
||||
->where('parent_id', $primaryUserId)
|
||||
->get()
|
||||
->map(fn ($r) => (array) $r)
|
||||
->all();
|
||||
|
||||
foreach ($students as $s) {
|
||||
$linkedStudents += $this->attachStudentToFamily((int) ($s['id'] ?? 0), $familyId);
|
||||
}
|
||||
|
||||
$linkedGuardians += $this->attachGuardianUser($familyId, $primaryUserId, 'primary', true, 1, 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'families_created' => $created,
|
||||
'students_linked' => $linkedStudents,
|
||||
'guardians_linked' => $linkedGuardians,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function attachSecondByUser(int $studentId, int $userId, string $relation = 'secondary'): array
|
||||
{
|
||||
if ($studentId <= 0 || $userId <= 0) {
|
||||
return ['ok' => false, 'message' => 'student_id and user_id required'];
|
||||
}
|
||||
|
||||
$familyId = $this->familyIdFromStudentPrimary($studentId);
|
||||
if (!$familyId) {
|
||||
return ['ok' => false, 'message' => 'No primary family found for this student'];
|
||||
}
|
||||
|
||||
$rows = $this->attachGuardianUser($familyId, $userId, $relation, false, 1, 0);
|
||||
|
||||
return ['ok' => true, 'attached' => $rows, 'family_id' => $familyId];
|
||||
}
|
||||
|
||||
public function attachSecondByEmail(int $studentId, string $email, ?string $firstname, ?string $lastname, string $relation = 'secondary'): array
|
||||
{
|
||||
if ($studentId <= 0 || $email === '') {
|
||||
return ['ok' => false, 'message' => 'student_id and email required'];
|
||||
}
|
||||
|
||||
$familyId = $this->familyIdFromStudentPrimary($studentId);
|
||||
if (!$familyId) {
|
||||
return ['ok' => false, 'message' => 'No primary family found for this student'];
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($email, $firstname, $lastname, $relation, $familyId) {
|
||||
$user = User::query()->where('email', $email)->first();
|
||||
if (!$user) {
|
||||
$user = User::query()->create([
|
||||
'firstname' => (string) ($firstname ?? ''),
|
||||
'lastname' => (string) ($lastname ?? ''),
|
||||
'email' => $email,
|
||||
'status' => 'Active',
|
||||
'user_type' => 'secondary',
|
||||
'cellphone' => '',
|
||||
'address_street' => '',
|
||||
'city' => '',
|
||||
'state' => '',
|
||||
'zip' => '',
|
||||
'accept_school_policy' => 1,
|
||||
'password' => bcrypt('temporary'),
|
||||
'semester' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
$rows = $this->attachGuardianUser($familyId, (int) $user->id, $relation, false, 1, 0);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'attached' => $rows,
|
||||
'family_id' => $familyId,
|
||||
'user_id' => (int) $user->id,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function setPrimaryHome(int $familyId, int $studentId, bool $isPrimaryHome): void
|
||||
{
|
||||
FamilyStudent::query()
|
||||
->where('family_id', $familyId)
|
||||
->where('student_id', $studentId)
|
||||
->update(['is_primary_home' => $isPrimaryHome ? 1 : 0]);
|
||||
}
|
||||
|
||||
public function setGuardianFlags(int $familyId, int $userId, array $flags): void
|
||||
{
|
||||
$data = [];
|
||||
foreach (['receive_emails', 'is_primary', 'receive_sms', 'relation'] as $key) {
|
||||
if (array_key_exists($key, $flags)) {
|
||||
$value = $flags[$key];
|
||||
$data[$key] = in_array($key, ['receive_emails', 'is_primary', 'receive_sms'], true)
|
||||
? (int) $value
|
||||
: (string) $value;
|
||||
}
|
||||
}
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FamilyGuardian::query()
|
||||
->where('family_id', $familyId)
|
||||
->where('user_id', $userId)
|
||||
->update($data);
|
||||
}
|
||||
|
||||
public function unlinkGuardian(int $familyId, int $userId): void
|
||||
{
|
||||
FamilyGuardian::query()
|
||||
->where('family_id', $familyId)
|
||||
->where('user_id', $userId)
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function unlinkStudent(int $familyId, int $studentId): void
|
||||
{
|
||||
FamilyStudent::query()
|
||||
->where('family_id', $familyId)
|
||||
->where('student_id', $studentId)
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function importSecondParentsFromLegacy(): array
|
||||
{
|
||||
$legacyTable = 'parents';
|
||||
$this->ensureTablesExist(['families', 'family_students', 'family_guardians']);
|
||||
if (!Schema::hasTable($legacyTable)) {
|
||||
return ['ok' => false, 'message' => "Legacy table '{$legacyTable}' not found"];
|
||||
}
|
||||
|
||||
$rows = DB::table($legacyTable)
|
||||
->selectRaw('firstparent_id AS first_id, secondparent_id AS second_id, secondparent_email AS email, secondparent_firstname AS firstname, secondparent_lastname AS lastname')
|
||||
->where(function ($q) {
|
||||
$q->where('secondparent_id', '!=', 0)
|
||||
->orWhere('secondparent_email', '!=', '');
|
||||
})
|
||||
->get()
|
||||
->map(fn ($r) => (array) $r)
|
||||
->all();
|
||||
|
||||
$createdUsers = 0;
|
||||
$linked = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
$firstId = (int) ($r['first_id'] ?? 0);
|
||||
$secondId = (int) ($r['second_id'] ?? 0);
|
||||
$email = trim((string) ($r['email'] ?? ''));
|
||||
|
||||
if ($firstId <= 0 || ($secondId <= 0 && $email === '')) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$tmp = 0;
|
||||
$familyId = $this->ensureFamilyForPrimaryParent($firstId, $tmp);
|
||||
|
||||
$userId = 0;
|
||||
if ($secondId > 0) {
|
||||
$userId = $secondId;
|
||||
} else {
|
||||
$user = User::query()->where('email', $email)->first();
|
||||
if (!$user) {
|
||||
$user = User::query()->create([
|
||||
'firstname' => (string) ($r['firstname'] ?? ''),
|
||||
'lastname' => (string) ($r['lastname'] ?? ''),
|
||||
'email' => $email,
|
||||
'status' => 'Active',
|
||||
'user_type' => 'secondary',
|
||||
'cellphone' => '',
|
||||
'address_street' => '',
|
||||
'city' => '',
|
||||
'state' => '',
|
||||
'zip' => '',
|
||||
'accept_school_policy' => 1,
|
||||
'password' => bcrypt('temporary'),
|
||||
'semester' => '',
|
||||
]);
|
||||
$createdUsers++;
|
||||
}
|
||||
$userId = (int) $user->id;
|
||||
}
|
||||
|
||||
$students = DB::table('students')
|
||||
->select('id')
|
||||
->where('parent_id', $firstId)
|
||||
->get()
|
||||
->map(fn ($r) => (array) $r)
|
||||
->all();
|
||||
foreach ($students as $sr) {
|
||||
$sid = (int) ($sr['id'] ?? 0);
|
||||
if ($sid > 0) {
|
||||
$this->attachStudentToFamily($sid, $familyId);
|
||||
}
|
||||
}
|
||||
|
||||
$linked += $this->attachGuardianUser($familyId, $userId, 'guardian', false, 1, 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'created_users' => $createdUsers,
|
||||
'guardians_linked' => $linked,
|
||||
'skipped' => $skipped,
|
||||
'total_source' => count($rows),
|
||||
];
|
||||
}
|
||||
|
||||
private function ensureFamilyForPrimaryParent(int $primaryUserId, int &$createdCounter): int
|
||||
{
|
||||
$code = 'FAM-' . $primaryUserId;
|
||||
$row = Family::query()->where('family_code', $code)->first();
|
||||
if ($row) {
|
||||
return (int) $row->id;
|
||||
}
|
||||
|
||||
$family = Family::query()->create([
|
||||
'family_code' => $code,
|
||||
'household_name' => 'Family of User ' . $primaryUserId,
|
||||
'is_active' => 1,
|
||||
]);
|
||||
$createdCounter++;
|
||||
|
||||
return (int) $family->id;
|
||||
}
|
||||
|
||||
private function attachStudentToFamily(int $studentId, int $familyId): int
|
||||
{
|
||||
if ($studentId <= 0 || $familyId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DB::table('family_students')->insertOrIgnore([
|
||||
'family_id' => $familyId,
|
||||
'student_id' => $studentId,
|
||||
'is_primary_home' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function attachGuardianUser(
|
||||
int $familyId,
|
||||
int $userId,
|
||||
string $relation = 'primary',
|
||||
bool $isPrimary = false,
|
||||
int $receiveEmails = 1,
|
||||
int $receiveSms = 0
|
||||
): int {
|
||||
if ($familyId <= 0 || $userId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DB::table('family_guardians')->insertOrIgnore([
|
||||
'family_id' => $familyId,
|
||||
'user_id' => $userId,
|
||||
'relation' => $relation,
|
||||
'is_primary' => $isPrimary ? 1 : 0,
|
||||
'receive_emails' => $receiveEmails,
|
||||
'receive_sms' => $receiveSms,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function familyIdFromStudentPrimary(int $studentId): ?int
|
||||
{
|
||||
$student = Student::query()->select('parent_id')->find($studentId);
|
||||
if (!$student || empty($student->parent_id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$code = 'FAM-' . (int) $student->parent_id;
|
||||
$row = Family::query()->select('id')->where('family_code', $code)->first();
|
||||
if ($row) {
|
||||
return (int) $row->id;
|
||||
}
|
||||
|
||||
$fallback = DB::table('family_students')
|
||||
->select('family_id')
|
||||
->where('student_id', $studentId)
|
||||
->orderByDesc('is_primary_home')
|
||||
->first();
|
||||
|
||||
return $fallback?->family_id ? (int) $fallback->family_id : null;
|
||||
}
|
||||
|
||||
private function ensureTablesExist(array $tables): void
|
||||
{
|
||||
foreach ($tables as $table) {
|
||||
if (!Schema::hasTable($table)) {
|
||||
Log::warning('Family tables missing', ['table' => $table]);
|
||||
throw new \RuntimeException("Missing required table '{$table}'. Run migrations.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user