['Required']]); } $userId = (int) $user['id']; $email = (string) $user['email']; // Ids owned by the user. $stmt = $pdo->prepare('SELECT idhome FROM home WHERE iduser = ?'); $stmt->execute([$userId]); $homeIds = $stmt->fetchAll(PDO::FETCH_COLUMN); $stmt = $pdo->prepare('SELECT owner_id FROM property_owners WHERE user_id = ?'); $stmt->execute([$userId]); $ownerIds = $stmt->fetchAll(PDO::FETCH_COLUMN); // Collect files before the rows disappear; they are unlinked after a successful commit. $files = []; if ($homeIds || $ownerIds) { $where = []; $params = []; if ($homeIds) { $where[] = 'idhome IN (' . implode(',', array_fill(0, count($homeIds), '?')) . ')'; $params = array_merge($params, $homeIds); } if ($ownerIds) { $where[] = 'owner_id IN (' . implode(',', array_fill(0, count($ownerIds), '?')) . ')'; $params = array_merge($params, $ownerIds); } $stmt = $pdo->prepare('SELECT filename, entity_type, idhome FROM doc_storage WHERE ' . implode(' OR ', $where)); $stmt->execute($params); foreach ($stmt->fetchAll() as $f) { $isPerson = ($f['entity_type'] ?? '') === 'person' || empty($f['idhome']); $files[] = ($isPerson ? $config['persondocs_dir'] : $config['homedocs_dir']) . '/' . basename((string) $f['filename']); } } $in = fn (array $ids) => implode(',', array_fill(0, count($ids), '?')); $pdo->beginTransaction(); try { // Sharing granted by the user, received by the user, or addressed to their e-mail. $pdo->prepare('DELETE FROM home_sharing WHERE iduser = ? OR idshareduser = ? OR shared_email = ?') ->execute([$userId, $userId, $email]); if ($homeIds) { $pdo->prepare('DELETE FROM doc_storage WHERE idhome IN (' . $in($homeIds) . ')')->execute($homeIds); $pdo->prepare('DELETE FROM home_owners WHERE home_id IN (' . $in($homeIds) . ')')->execute($homeIds); $pdo->prepare('DELETE FROM home_sharing WHERE idhome IN (' . $in($homeIds) . ')')->execute($homeIds); } if ($ownerIds) { $pdo->prepare('DELETE FROM doc_storage WHERE owner_id IN (' . $in($ownerIds) . ')')->execute($ownerIds); $pdo->prepare('DELETE FROM home_owners WHERE owner_id IN (' . $in($ownerIds) . ')')->execute($ownerIds); } $pdo->prepare('DELETE FROM home WHERE iduser = ?')->execute([$userId]); $pdo->prepare('DELETE FROM property_owners WHERE user_id = ?')->execute([$userId]); // Credentials and sessions. $pdo->prepare('DELETE FROM api_tokens WHERE user_id = ?')->execute([$userId]); $pdo->prepare('DELETE FROM auth_sessions WHERE user_id = ?')->execute([$userId]); $pdo->prepare('DELETE FROM auth_personal_access_tokens WHERE tokenable_id = ?')->execute([$userId]); $pdo->prepare('DELETE FROM auth_password_resets WHERE email = ?')->execute([$email]); // Cascades auth_social_logins, auth_user_activity, auth_announcements. $pdo->prepare('DELETE FROM auth_users WHERE id = ?')->execute([$userId]); $pdo->commit(); } catch (Throwable $e) { $pdo->rollBack(); json_error(500, 'Account deletion failed'); } // Files last: an orphaned file is harmless, a lost file after a rollback is not. foreach ($files as $path) { if (is_file($path)) { @unlink($path); } } json_ok();