2026-09-30 00:49:53 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 家庭:数据空间的根,所有名册条目都挂在它下面。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
|
|
namespace App;
|
|
|
|
|
|
|
|
|
|
|
|
final class Family
|
|
|
|
|
|
{
|
2026-10-18 20:14:50 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 应用自己的口号(用户 2026-10-18 明确指定,别再改)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 🔴 用户原话:「我们应用的口号或者标语是:**一家人用岁月写成的一本书**。
|
|
|
|
|
|
* 这也是我们"家书"这个名字的由来。所以你不要再乱换这个标语了,
|
|
|
|
|
|
* 只要是想出现标语或者副标题的地方,就用这样一句话。」
|
|
|
|
|
|
*
|
|
|
|
|
|
* ⚠️ 它与 `families.motto`(用户可在网页端「系统设置」里自己写一句)
|
|
|
|
|
|
* 是**两件事**:这里这句是"谁都没写时的默认值",
|
|
|
|
|
|
* 用户写了自己的就用他自己的 —— 规则见 `slogan()`。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public const DEFAULT_SLOGAN = '一家人用岁月写成的一本书';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 「我」这一端该显示的那句话(标语 / 副标题)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 优先级(与网页端 `templates/home.php` 的 `heroSlogan` **同一套规则**):
|
|
|
|
|
|
* ① **我自己的** `persons.slogan`(在「我的资料」里填的那一句);
|
|
|
|
|
|
* ② 家庭的 `families.motto`(网页端「系统设置」里那句,用户可自定义);
|
|
|
|
|
|
* ③ 应用默认口号 `DEFAULT_SLOGAN`。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 🔴 为什么不是"简单取 motto":用户 2026-10-18 说得很清楚 ——
|
|
|
|
|
|
* 「如果这个用户自己有标语,他可以写一句自己的标语;……如果他没有标语,
|
|
|
|
|
|
* 就用我们给他的这个标语。」
|
|
|
|
|
|
* 个人 → 家庭 → 默认,正是这三层;网页端首页的 `heroSlogan` 已经是这个逻辑,
|
|
|
|
|
|
* 端上照抄同一套,两边才不会出现"网页一句、手机另一句"。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $familyId 家庭 id
|
|
|
|
|
|
* @param int|null $personId 当前登录者(null = 不取个人那句,直接走家庭/默认)
|
|
|
|
|
|
* @return string 永远非空(兜底是 DEFAULT_SLOGAN)
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function slogan(int $familyId, ?int $personId = null): string
|
|
|
|
|
|
{
|
|
|
|
|
|
if ($personId !== null && $personId > 0) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
$st = Database::get()->prepare('SELECT slogan FROM persons WHERE id = ? AND family_id = ?');
|
|
|
|
|
|
$st->execute([$personId, $familyId]);
|
|
|
|
|
|
$mine = trim((string)($st->fetchColumn() ?: ''));
|
|
|
|
|
|
if ($mine !== '') {
|
|
|
|
|
|
return $mine;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (\Throwable $ex) {
|
|
|
|
|
|
// 老库还没这一列 / 读不到都不该挡路:往下走家庭那句
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
$f = self::find($familyId);
|
|
|
|
|
|
$motto = trim((string)($f['motto'] ?? ''));
|
|
|
|
|
|
if ($motto !== '') {
|
|
|
|
|
|
return $motto;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (\Throwable $ex) {
|
|
|
|
|
|
// 同上:读不到就用默认
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return self::DEFAULT_SLOGAN;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-30 00:49:53 +08:00
|
|
|
|
public static function find(int $id): ?array
|
|
|
|
|
|
{
|
|
|
|
|
|
$st = Database::get()->prepare('SELECT * FROM families WHERE id = ?');
|
|
|
|
|
|
$st->execute([$id]);
|
|
|
|
|
|
$row = $st->fetch();
|
|
|
|
|
|
return $row ?: null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static function create(string $name, string $motto = ''): int
|
|
|
|
|
|
{
|
|
|
|
|
|
$st = Database::get()->prepare('INSERT INTO families (name, motto) VALUES (?, ?)');
|
|
|
|
|
|
$st->execute([$name, $motto !== '' ? $motto : null]);
|
|
|
|
|
|
return (int)Database::get()->lastInsertId();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static function update(int $id, string $name, string $motto = ''): void
|
|
|
|
|
|
{
|
|
|
|
|
|
$st = Database::get()->prepare('UPDATE families SET name = ?, motto = ? WHERE id = ?');
|
|
|
|
|
|
$st->execute([$name, $motto !== '' ? $motto : null, $id]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static function countMembers(int $familyId): int
|
|
|
|
|
|
{
|
|
|
|
|
|
$st = Database::get()->prepare(
|
|
|
|
|
|
"SELECT COUNT(*) FROM persons WHERE family_id = ? AND kind = 'member'"
|
|
|
|
|
|
);
|
|
|
|
|
|
$st->execute([$familyId]);
|
|
|
|
|
|
return (int)$st->fetchColumn();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|