false, 'error' => '不能把自己设成自己的父母。']; } if (self::wouldCycle($familyId, $parentId, $childId)) { return ['ok' => false, 'error' => '这样会让两个人互为祖辈,关系说不通,已拒绝。']; } // 同一位父母只能有一个「父亲」和一个「母亲」位 $db = Database::get(); $st = $db->prepare( 'DELETE FROM relations WHERE family_id = ? AND to_id = ? AND kind = "parent" AND from_role = ?' ); $st->execute([$familyId, $childId, $role]); $st = $db->prepare( 'INSERT INTO relations (family_id, from_id, to_id, kind, from_role) VALUES (?,?,?,"parent",?)' ); $st->execute([$familyId, $parentId, $childId, $role]); return ['ok' => true, 'error' => '']; } /** 互为配偶(对称,只存一条;若已存在反向的记录则视为重复) */ public static function addSpouse(int $familyId, int $a, int $b): array { if ($a === $b) { return ['ok' => false, 'error' => '不能把自己设成自己的配偶。']; } $db = Database::get(); $st = $db->prepare( 'SELECT id FROM relations WHERE family_id = ? AND kind = "spouse" AND ((from_id = ? AND to_id = ?) OR (from_id = ? AND to_id = ?))' ); $st->execute([$familyId, $a, $b, $b, $a]); if ($st->fetchColumn()) { return ['ok' => true, 'error' => '']; } $st = $db->prepare( 'INSERT INTO relations (family_id, from_id, to_id, kind) VALUES (?,?,?,"spouse")' ); $st->execute([$familyId, $a, $b]); return ['ok' => true, 'error' => '']; } /** 解除某条关系(按 id) */ public static function remove(int $familyId, int $id): void { Database::get()->prepare('DELETE FROM relations WHERE id = ? AND family_id = ?') ->execute([$id, $familyId]); } /** 解除两人之间的某种关系 */ public static function detach(int $familyId, int $personId, string $kind, string $role = ''): void { $sql = 'DELETE FROM relations WHERE family_id = ? AND to_id = ? AND kind = ? AND from_role = ?'; Database::get()->prepare($sql)->execute([$familyId, $personId, $kind, $role]); } /** 某人身上挂着的全部关系(详情页编辑用) */ public static function forPerson(int $familyId, int $personId): array { $st = Database::get()->prepare( 'SELECT r.*, pf.name AS from_name, pt.name AS to_name FROM relations r JOIN persons pf ON pf.id = r.from_id JOIN persons pt ON pt.id = r.to_id WHERE r.family_id = ? AND (r.from_id = ? OR r.to_id = ?) ORDER BY r.kind, r.id' ); $st->execute([$familyId, $personId, $personId]); return $st->fetchAll(); } /** * 防环:若把 $maybeAncestor 挂到 $node 之上,会不会绕回自己? * 做法是顺着 parent 边从 $maybeAncestor 往上走,看能否走到 $node。 */ private static function wouldCycle(int $familyId, int $maybeAncestor, int $node): bool { $st = Database::get()->prepare( 'SELECT from_id FROM relations WHERE family_id = ? AND to_id = ? AND kind = "parent"' ); $stack = [$maybeAncestor]; $seen = []; while ($stack) { $cur = array_pop($stack); if ($cur === $node) { return true; } if (isset($seen[$cur])) { continue; } $seen[$cur] = true; $st->execute([$familyId, $cur]); foreach ($st->fetchAll(\PDO::FETCH_COLUMN) as $up) { $stack[] = (int)$up; } } return false; } /** 全家的关系条数(设置页/统计用) */ public static function count(int $familyId): int { $st = Database::get()->prepare('SELECT COUNT(*) FROM relations WHERE family_id = ?'); $st->execute([$familyId]); return (int)$st->fetchColumn(); } }