2026-08-20-01-add-category-id.php 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. <?php
  2. declare(strict_types=1);
  3. // Example migration for a project with JSON files: adds a new field to
  4. // every existing record.
  5. //
  6. // Idempotent: already-updated records stay unchanged, so the migration can
  7. // safely run again after a partial failure.
  8. return function (array $context): void {
  9. $file = $context["app_root"] . "/data/products.json";
  10. if (!is_file($file)) {
  11. return;
  12. }
  13. $data = json_decode((string) file_get_contents($file), true);
  14. if (!is_array($data)) {
  15. throw new RuntimeException("products.json is not readable or not valid JSON.");
  16. }
  17. $changed = false;
  18. foreach ($data as $index => $product) {
  19. if (is_array($product) && !array_key_exists("category_id", $product)) {
  20. $data[$index]["category_id"] = null;
  21. $changed = true;
  22. }
  23. }
  24. if (!$changed) {
  25. return;
  26. }
  27. $json = json_encode(
  28. $data,
  29. JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
  30. );
  31. if ($json === false) {
  32. throw new RuntimeException("products.json could not be encoded.");
  33. }
  34. // Write atomically, so an interruption never leaves a half-written file.
  35. $tmpFile = $file . ".tmp";
  36. if (file_put_contents($tmpFile, $json, LOCK_EX) === false || !rename($tmpFile, $file)) {
  37. @unlink($tmpFile);
  38. throw new RuntimeException("products.json could not be written.");
  39. }
  40. };