postgres database implementation

This commit is contained in:
2026-05-22 19:07:03 +02:00
parent dacc9ce2bc
commit e7f35d71e3
2 changed files with 146 additions and 97 deletions
+108 -88
View File
@@ -7,54 +7,72 @@ if (session_status() === PHP_SESSION_NONE) {
require_once __DIR__ . '/config.php';
function decode_firestore_value($value) {
if (!is_array($value)) return $value;
if (isset($value['stringValue'])) return $value['stringValue'];
if (isset($value['integerValue'])) return (int)$value['integerValue'];
if (isset($value['doubleValue'])) return (float)$value['doubleValue'];
if (isset($value['booleanValue'])) return (bool)$value['booleanValue'];
if (isset($value['nullValue'])) return null;
if (isset($value['arrayValue']['values'])) {
return array_map('decode_firestore_value', $value['arrayValue']['values']);
function init_db() {
$pdo = get_db_connection();
if (!$pdo) return;
try {
$pdo->exec("CREATE TABLE IF NOT EXISTS recipes (
slug VARCHAR(255) PRIMARY KEY,
data JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
$stmt = $pdo->query("SELECT COUNT(*) FROM recipes");
if ($stmt && $stmt->fetchColumn() == 0) {
$path = __DIR__ . '/data/recipes.json';
if (file_exists($path)) {
$json = file_get_contents($path);
$data = json_decode($json, true);
if (is_array($data) && count($data) > 0) {
$insert = $pdo->prepare("INSERT INTO recipes (slug, data) VALUES (:slug, :data)");
foreach ($data as $recipe) {
if (isset($recipe['slug'])) {
$insert->execute([
'slug' => $recipe['slug'],
'data' => json_encode($recipe, JSON_UNESCAPED_UNICODE)
]);
}
}
}
}
}
} catch (PDOException $e) {
error_log("DB Init Error: " . $e->getMessage());
}
if (isset($value['mapValue']['fields'])) {
return array_map('decode_firestore_value', $value['mapValue']['fields']);
}
return $value;
}
function decode_firestore_document(array $doc): array {
if (!isset($doc['fields'])) return [];
return array_map('decode_firestore_value', $doc['fields']);
}
function firestore_get(string $url): ?array {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || !$response) {
return null;
}
return json_decode($response, true);
}
function load_recipes_local(): array {
$path = __DIR__ . '/data/recipes.json';
if (!file_exists($path)) {
return [];
}
$json = file_get_contents($path);
$data = json_decode($json, true);
if (!is_array($data)) {
return [];
$pdo = get_db_connection();
$data = [];
if (!$pdo) {
$path = __DIR__ . '/data/recipes.json';
if (file_exists($path)) {
$json = file_get_contents($path);
$data = json_decode($json, true) ?: [];
}
} else {
static $initialized = false;
if (!$initialized) {
init_db();
$initialized = true;
}
try {
$stmt = $pdo->query("SELECT data FROM recipes");
if ($stmt) {
while ($row = $stmt->fetch()) {
$recipe = json_decode($row['data'], true);
if (is_array($recipe)) {
$data[] = $recipe;
}
}
}
} catch (PDOException $e) {
error_log("Failed to load recipes from DB: " . $e->getMessage());
}
}
foreach ($data as &$recipe) {
@@ -68,57 +86,59 @@ function load_recipes_local(): array {
}
function load_recipes(): array {
$config = get_firebase_config();
$projectId = $config['projectId'] ?? '';
if (empty($projectId)) {
return load_recipes_local();
}
$url = "https://firestore.googleapis.com/v1/projects/{$projectId}/databases/(default)/documents/recipes?pageSize=100";
$res = firestore_get($url);
if (!$res || !isset($res['documents'])) {
return load_recipes_local();
}
$recipes = [];
foreach ($res['documents'] as $doc) {
$decoded = decode_firestore_document($doc);
if (!empty($decoded)) {
if (!empty($decoded['hero']) && is_string($decoded['hero'])) {
$decoded['hero'] = normalize_asset_path($decoded['hero']);
}
$recipes[] = $decoded;
}
}
return $recipes;
return load_recipes_local();
}
function load_recipe_by_slug(string $slug): ?array {
$config = get_firebase_config();
$projectId = $config['projectId'] ?? '';
if (empty($projectId)) {
return find_recipe_by_slug(load_recipes_local(), $slug);
}
$url = "https://firestore.googleapis.com/v1/projects/{$projectId}/databases/(default)/documents/recipes/" . urlencode($slug);
$res = firestore_get($url);
if (!$res || isset($res['error'])) {
return find_recipe_by_slug(load_recipes_local(), $slug);
}
$decoded = decode_firestore_document($res);
if (!empty($decoded['hero']) && is_string($decoded['hero'])) {
$decoded['hero'] = normalize_asset_path($decoded['hero']);
}
return $decoded;
return find_recipe_by_slug(load_recipes_local(), $slug);
}
function save_recipes(array $recipes): bool {
$pdo = get_db_connection();
$path = __DIR__ . '/data/recipes.json';
$json = json_encode($recipes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
return (bool) file_put_contents($path, $json);
if (!$pdo) {
$json = json_encode($recipes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
return (bool) file_put_contents($path, $json);
}
try {
$pdo->beginTransaction();
$slugs = [];
$insert = $pdo->prepare("INSERT INTO recipes (slug, data) VALUES (:slug, :data) ON CONFLICT (slug) DO UPDATE SET data = EXCLUDED.data, updated_at = CURRENT_TIMESTAMP");
foreach ($recipes as $recipe) {
if (isset($recipe['slug'])) {
$slugs[] = $recipe['slug'];
$insert->execute([
'slug' => $recipe['slug'],
'data' => json_encode($recipe, JSON_UNESCAPED_UNICODE)
]);
}
}
if (!empty($slugs)) {
$placeholders = implode(',', array_fill(0, count($slugs), '?'));
$delete = $pdo->prepare("DELETE FROM recipes WHERE slug NOT IN ($placeholders)");
$delete->execute($slugs);
} else {
$pdo->exec("DELETE FROM recipes");
}
$pdo->commit();
// Also update local JSON as backup
$json = json_encode($recipes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
file_put_contents($path, $json);
return true;
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
error_log("Failed to save recipes to DB: " . $e->getMessage());
return false;
}
}
/**