Одноразовий скрипт часто сприймається як щось менш важливе за звичайний код застосунку.
Його не планують підтримувати роками. Він може бути потрібен лише для одного імпорту, міграції контенту або виправлення даних.
Але якщо цей скрипт змінює production базу, наслідки його помилки цілком production: дублікати, неправильні зв’язки, зламані переклади, оприлюднені внутрішні дані або контент, який уже бачать користувачі.
Тому одноразовий імпортер варто оцінювати не за тривалістю його життя, а за тим, які дані він може змінити.
Перша проста перевірка - не створити той самий матеріал двічі
if ( Post::withTrashed() ->where( 'slug', $slug, ) ->exists()) { throw new RuntimeException( 'Post already exists: '.$slug );}Такий захист виглядає майже надто простим, але він закриває дуже реальний сценарій: скрипт запустили повторно, бо не помітили результат першого запуску.
Перевірка через withTrashed() додатково захищає від дублювання запису, який був м’яко видалений, але все ще займає той самий ідентифікатор URL (slug).
Категорію краще перевірити до створення запису
$category = Category::query() ->where( 'slug', $categorySlug, ) ->where( 'status', true, ) ->first(); if ($category === null) { throw new RuntimeException( 'Active category was not found.' );}Якщо імпортер покладається на назву категорії без перевірки, помилка може проявитися далеко не там, де її легко помітити.
Краще зупинити виконання одразу, ніж створити матеріал без потрібного зв’язку або прив’язати його не туди.
Особливо корисно перевіряти не лише існування категорії, а й те, що вона активна.
Публічний контент не повинен випадково містити внутрішні службові ключі
if ( preg_match( '/\b(?:ABC|OPS|APP)-\d+\b/', $payload, ) === 1) { throw new RuntimeException( 'Internal issue key found.' );}Під час підготовки матеріалу легко залишити посилання на внутрішню задачу, службову назву або інший фрагмент, який був корисний під час роботи, але не повинен потрапити в публічний текст.
Коротка перевірка всього сформованого вмісту перед записом дешевша за ручний пошук після публікації.
Двомовний контент краще будувати парами
$add( $heading( 'Український заголовок', 'same-anchor', ), $heading( 'English Heading', 'same-anchor', ),);Коли українські та англійські блоки створюються незалежними масивами, легко додати один блок лише в одну мову або змінити порядок в одній версії.
Побудова парами робить структуру явною: одна позиція завжди має український і англійський варіант того самого типу.
if ( $uk['type'] !== $en['type']) { throw new RuntimeException( 'UK/EN block type mismatch.' );}Якщо на одній позиції українська версія має заголовок, а англійська - звичайний текст, це вже не переклад однієї структури.
Краще зупинити імпорт одразу, ніж отримати дві версії сторінки, які поступово розходяться.
Якорі заголовків теж мають збігатися
if ( $anchors['uk'] !== $anchors['en']) { throw new RuntimeException( 'UK/EN heading anchors mismatch.' );}Навіть якщо заголовки перекладені правильно, різні якорі можуть ламати зміст сторінки, прямі посилання на розділи або навігацію.
Це ще один приклад перевірки, яка майже нічого не коштує, але захищає структуру матеріалу.
Імпортер має знати, які типи блоків йому дозволено створювати
$allowed = [ 'heading', 'rich_text', 'code', 'notice', 'quote',]; foreach ($blocks as $block) { if ( ! in_array( $block['type'], $allowed, true, ) ) {Такий список працює як маленька межа довіри.
Якщо в підготовленому наборі раптом з’явився блок, якого цей імпортер не повинен створювати, краще зупинитися й розібратися, ніж мовчки записати його в базу.
Перевірка синтаксису до запуску - найдешевша страховка
php -l import-script.phpphp -l не перевіряє бізнес-правила і не знає, чи правильний запис буде створено.
Але він відсікає найпростішу категорію проблем ще до підключення до застосунку: синтаксичні помилки, незакриті дужки, випадково пошкоджений файл.
Для скрипта, який запускається вручну на сервері, це дуже дешева й дуже корисна перевірка.
Чистий синтаксис ще не означає безпечний імпорт
Це важлива межа: перевірка синтаксису відповідає лише на запитання "чи може PHP прочитати цей файл?".
Вона не знає, чи існує категорія, чи дублюється матеріал, чи збігаються переклади, чи потрапили у текст внутрішні дані.
Саме тому технічна перевірка синтаксису і перевірки даних доповнюють одна одну, а не замінюють.
Запис краще виконувати в транзакції
$postId = DB::transaction( function (): int { $post = Post::create([ // ... ]); syncTranslations($post); syncBlocks($post); return $post->id; });Якщо створення основного запису пройшло, а синхронізація перекладів або блоків упала, без транзакції можна залишити напівготові дані.
Транзакція не виправляє неправильну логіку, але зменшує кількість частково виконаних імпортів.
Після запису варто перевірити те, що реально збереглося
Ще одна корисна звичка - завершувати скрипт не просто повідомленням "готово", а перевіркою створеного запису.
Наприклад, повторно прочитати матеріал із бази, порахувати блоки обох мов, перевірити їхні типи й тільки після цього повернути явний результат успіху.
Це перетворює скрипт із команди "спробувати записати" на маленьку процедуру "записати й перевірити".
result: OKstatus: draftcategory: devopsuk_blocks: 24en_blocks: 24same_types: trueguards: duplicate: passed category: passed internal_keys: passed anchors: passedХороший імпортер краще зупиниться зайвий раз, ніж мовчки запише підозрілі дані
Для одноразового імпорту зупинка зазвичай дешевша за неправильний запис.
Якщо категорія не знайдена, тип блоку невідомий або структура перекладів розійшлася, краще отримати помилку й виправити скрипт.
Мовчазне "спробуємо якось продовжити" у таких сценаріях часто лише переносить проблему з коду в production дані.
Мій мінімальний набір перевірок для одноразового імпортера
[ ] Перевірити синтаксис до виконання[ ] Перевірити відсутність дубліката[ ] Перевірити зовнішні зв'язки[ ] Перевірити дозволені типи блоків[ ] Перевірити пари українських та англійських блоків[ ] Перевірити однакові якорі[ ] Перевірити заборонені внутрішні позначення[ ] Виконати запис у транзакції[ ] Повторно прочитати й перевірити збережений результат[ ] Повернути явний результат успіхуОдноразовість не робить код безпечним
Одноразовий скрипт може прожити п’ять хвилин і більше ніколи не запускатися.
Але дані, які він створив, можуть залишитися в системі роками.
Саме тому маленькі перевірки перед записом мають таку високу цінність: вони дешеві, локальні й зупиняють помилки до того, як ті перетворяться на проблему production даних.
Якщо скрипт має право змінювати production дані, він уже заслуговує на ставлення як до production коду.
Одноразовий скрипт не перестає бути production лише тому, що його планують запустити один раз.



