recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
@@ -0,0 +1,251 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
use CodeIgniter\Database\BaseConnection;
class FamiliesBootstrapSeeder extends Seeder
{
/** @var BaseConnection */
protected $db;
// Map your legacy `parents` table/columns
protected array $legacy = [
'table' => 'parents', // legacy table
'firstparent_id' => 'firstparent_id', // users.id (primary parent)
'second_user_id' => 'secondparent_id', // users.id of 2nd parent if present (may be 0)
'email' => 'secondparent_email',
'firstname' => 'secondparent_firstname',
'lastname' => 'secondparent_lastname',
];
public function run()
{
$this->db = \Config\Database::connect();
// While tuning, you can comment these two lines to avoid full rollback
$this->db->transStart();
[$familiesCreated, $studentsLinked, $primGuardiansLinked] = $this->bootstrapByPrimaryParents();
[$createdUsers, $secGuardiansLinked, $skippedLegacy, $legacyTotal] = $this->importLegacySecondParents();
$this->db->transComplete();
echo PHP_EOL;
echo "Families created: {$familiesCreated}\n";
echo "Students linked: {$studentsLinked}\n";
echo "Primary guardians: {$primGuardiansLinked}\n";
echo "Legacy rows scanned: {$legacyTotal}\n";
echo "Stub users created: {$createdUsers}\n";
echo "Secondary guardians: {$secGuardiansLinked}\n";
echo "Legacy rows skipped: {$skippedLegacy}\n";
echo PHP_EOL;
}
/** Create/ensure one family per primary parent (students.parent_id), link students, add primary guardian. */
protected function bootstrapByPrimaryParents(): array
{
$familiesCreated = 0; $studentsLinked = 0; $primGuardiansLinked = 0;
$primaryParents = $this->db->query(
"SELECT DISTINCT parent_id FROM students WHERE parent_id IS NOT NULL"
)->getResultArray();
foreach ($primaryParents as $row) {
$primaryUserId = (int) $row['parent_id'];
if ($primaryUserId <= 0) { continue; }
$familyId = $this->ensureFamilyForPrimaryParent($primaryUserId, $familiesCreated);
// link all kids of this primary parent
$kids = $this->db->query("SELECT id FROM students WHERE parent_id = ?", [$primaryUserId])->getResultArray();
foreach ($kids as $s) {
$studentsLinked += $this->attachStudentToFamily((int)$s['id'], $familyId);
}
// ensure primary guardian link
$primGuardiansLinked += $this->attachGuardianUser($familyId, $primaryUserId, 'primary', 1, 1, 0);
}
return [$familiesCreated, $studentsLinked, $primGuardiansLinked];
}
/** Import second parents from legacy `parents` table (by email or secondparent_id). */
protected function importLegacySecondParents(): array
{
$L = $this->legacy;
$createdUsers = 0; $linked = 0; $skipped = 0; $total = 0;
if (! $this->db->tableExists($L['table'])) {
echo "[LEGACY] Table '{$L['table']}' not found; skipping.\n";
return [0,0,0,0];
}
$fields = array_map('strtolower', array_column($this->db->getFieldData($L['table']), 'name'));
foreach (['firstparent_id','email','firstname','lastname','second_user_id'] as $k) {
if (! array_key_exists($k, $L)) continue;
$col = strtolower($L[$k]);
if ($col && ! in_array($col, $fields, true)) {
echo "[LEGACY] Missing column {$L[$k]} in {$L['table']}; skipping import.\n";
return [0,0,0,0];
}
}
$qb = $this->db->table($L['table']);
$qb->select("{$L['firstparent_id']} AS firstparent_id", false)
->select("{$L['second_user_id']} AS secondparent_id", false)
->select("{$L['email']} AS email", false)
->select("{$L['firstname']} AS firstname", false)
->select("{$L['lastname']} AS lastname", false)
->where("{$L['email']} IS NOT NULL", null, false)
->where("{$L['email']} !=", '');
$q = $qb->get();
if ($q === false) {
$err = $this->db->error();
echo "[LEGACY] Query failed: {$err['code']} {$err['message']}\n";
return [0,0,0,0];
}
$rows = $q->getResultArray();
$total = count($rows);
foreach ($rows as $r) {
$primaryUserId = (int)($r['firstparent_id'] ?? 0);
$email = trim((string)($r['email'] ?? ''));
$first = trim((string)($r['firstname'] ?? ''));
$last = trim((string)($r['lastname'] ?? ''));
$secondUserId = (int)($r['secondparent_id'] ?? 0);
if ($primaryUserId <= 0 || $email === '') { $skipped++; continue; }
// students for this primary parent
$students = $this->db->query("SELECT id FROM students WHERE parent_id = ?", [$primaryUserId])->getResultArray();
if (!$students) { $skipped++; continue; }
// resolve/create the second parent user
$userRow = null;
if ($secondUserId > 0) {
$userRow = $this->db->table('users')->select('id, firstname, lastname')->where('id', $secondUserId)->get()->getRowArray();
}
if (!$userRow) {
$userRow = $this->db->table('users')->select('id, firstname, lastname')->where('email', $email)->get()->getRowArray();
}
if (!$userRow) {
// STUB USER with safe defaults for your NOT NULL columns
$this->db->table('users')->insert([
'firstname' => $first ?: '(Parent)',
'lastname' => $last ?: '(Unspecified)',
'email' => $email,
'cellphone' => '',
'address_street' => '',
'apt' => null,
'city' => '',
'state' => '',
'zip' => '',
'accept_school_policy' => 0,
'is_verified' => 0,
'status' => 'Active',
'is_suspended' => 0,
'failed_attempts' => 0,
'password' => password_hash(bin2hex(random_bytes(8)), PASSWORD_BCRYPT),
'created_at' => utc_now(),
'updated_at' => utc_now(),
'token' => null,
'account_id' => null,
'user_type' => 'primary',
'semester' => '',
'school_year' => null,
'rfid_tag' => null,
'last_failed_at' => null,
]);
if ($this->db->affectedRows() === 0) {
$err = $this->db->error();
echo "[ERROR] users insert failed for {$email}: {$err['code']} {$err['message']}\n";
$skipped++; continue;
}
$secondUserId = (int)$this->db->insertID();
$createdUsers++;
$userRow = ['id' => $secondUserId, 'firstname' => $first, 'lastname' => $last];
} else {
$secondUserId = (int)$userRow['id'];
if (empty($userRow['firstname']) || empty($userRow['lastname'])) {
$this->db->table('users')->where('id', $secondUserId)->update([
'firstname' => $userRow['firstname'] ?: ($first ?: '(Parent)'),
'lastname' => $userRow['lastname'] ?: ($last ?: '(Unspecified)'),
]);
}
}
// link second parent to each students family
foreach ($students as $s) {
$studentId = (int)$s['id'];
$familyId = $this->familyIdFromStudentPrimary($studentId);
if (!$familyId) {
$familyId = $this->ensureFamilyForPrimaryParent($primaryUserId, $dummy = 0);
$this->attachStudentToFamily($studentId, $familyId);
}
$linked += $this->attachGuardianUser($familyId, $secondUserId, 'secondary', 0, 1, 0);
}
}
return [$createdUsers, $linked, $skipped, $total];
}
// ---------- Helpers ----------
/** Ensure there is exactly one family per primary parent user. */
protected function ensureFamilyForPrimaryParent(int $primaryUserId, int &$createdCounter): int
{
$code = 'FAM-' . $primaryUserId;
$row = $this->db->query("SELECT id FROM families WHERE family_code = ?", [$code])->getRowArray();
if ($row) return (int)$row['id'];
$this->db->table('families')->insert([
'family_code' => $code,
'household_name' => 'Family of User ' . $primaryUserId,
'is_active' => 1,
]);
$createdCounter++;
return (int)$this->db->insertID();
}
/** Idempotent link student → family. */
protected function attachStudentToFamily(int $studentId, int $familyId): int
{
$this->db->query(
"INSERT IGNORE INTO family_students (family_id, student_id, is_primary_home) VALUES (?, ?, 1)",
[$familyId, $studentId]
);
return $this->db->affectedRows();
}
/** Idempotent link guardian user → family. */
protected function attachGuardianUser(int $familyId, int $userId, string $relation, int $isPrimary, int $receiveEmails, int $receiveSms): int
{
$this->db->query(
"INSERT IGNORE INTO family_guardians (family_id, user_id, relation, is_primary, receive_emails, receive_sms) VALUES (?, ?, ?, ?, ?, ?)",
[$familyId, $userId, $relation, $isPrimary, $receiveEmails, $receiveSms]
);
return $this->db->affectedRows();
}
/** Canonical family for a student based on parent_id → FAM-{parent_id}, fallback to any linked family. */
protected function familyIdFromStudentPrimary(int $studentId): ?int
{
$row = $this->db->query("SELECT parent_id FROM students WHERE id = ?", [$studentId])->getRowArray();
if (!$row || empty($row['parent_id'])) return null;
$code = 'FAM-' . (int)$row['parent_id'];
$fam = $this->db->query("SELECT id FROM families WHERE family_code = ?", [$code])->getRowArray();
if ($fam) return (int)$fam['id'];
$fam2 = $this->db->query(
"SELECT family_id FROM family_students WHERE student_id = ? ORDER BY is_primary_home DESC LIMIT 1",
[$studentId]
)->getRowArray();
return $fam2 ? (int)$fam2['family_id'] : null;
}
}