Безголове надсилання форми за допомогою WordPress REST API

Вихідний вузол: 806492

Якщо ви створюєте сайт на WordPress, вам потрібна вагома причина НЕ щоб вибрати плагін форми WordPress. Вони зручні та пропонують безліч налаштувань, створення яких з нуля займе багато зусиль. Вони відтворюють HTML, перевіряють дані, зберігають подання та забезпечують інтеграцію зі сторонніми службами.

Але припустімо, що ми плануємо використовувати WordPress як безголовий CMS. У цьому випадку ми будемо в основному взаємодіяти з REST API (або GraphQL). Інтерфейсна частина стає повністю нашою відповідальністю, і ми більше не можемо покладатися на плагіни форм, щоб виконувати важку роботу в цій області. Тепер ми на місці водія, коли справа доходить до передньої частини.

Форми були вирішеною проблемою, але тепер ми повинні вирішити, що з ними робити. У нас є кілька варіантів:

  • Чи використовуємо ми власний спеціальний API, якщо він є? Якщо ні, і ми не хочемо його створювати, ми можемо скористатися послугою. Тут багато хороші постачальники статичних форм, і постійно з’являються нові.
  • Чи можемо ми продовжувати використовувати плагін WordPress, який ми вже використовуємо, і використовувати його перевірку, зберігання та інтеграцію?

Найпопулярніший плагін безкоштовної форми, Contact Form 7, має кінцеву точку REST API для подання, а також відомий платний плагін, гравітаційні форми, Серед інших.

З технічної точки зору немає реальної різниці між надсиланням даних форми в кінцеву точку, надану службою, або плагіном WordPress. Отже, ми повинні приймати рішення за різними критеріями. Ціна очевидна; після цього доступність інсталяції WordPress і його REST API. Надсилання до кінцевої точки передбачає, що вона завжди доступна для всіх. Це вже зрозуміло, коли йдеться про послуги, оскільки ми платимо за їх доступність. Деякі налаштування можуть обмежувати доступ WordPress лише до процесів редагування та створення. Ще одна річ, яку слід розглянути, це те, де ви хочете зберігати дані, зокрема у спосіб, який відповідає правилам GPDR.

Коли мова заходить про функції, окрім подання, плагіни форм WordPress важко порівняти. Вони мають свою екосистему, додатки, здатні генерувати звіти, PDF-файли, доступну інтеграцію з інформаційними бюлетенями та платіжними службами. Небагато послуг пропонують стільки в одному пакеті.

Навіть якщо ми використовуємо WordPress у «традиційний» спосіб із інтерфейсом на основі теми WordPress, використання REST API плагіна форми може мати сенс у багатьох випадках. Наприклад, якщо ми розробляємо тему з використанням CSS-фреймворку, що має першочергове значення для утиліт, стилізація візуалізованої форми з фіксованою розміткою, структурованою за умовами класів, подібними до BEM, залишає кислий присмак у роті будь-якого розробника.

Мета цієї статті — представити кінцеві точки надсилання двох плагінів форм WordPress і показати спосіб відтворення типової поведінки, пов’язаної з формами, до якої ми звикли виходити з коробки. Загалом, надаючи форму, ми маємо справу з двома основними проблемами. Один – це подання самих даних, а інший – надання значущого зворотного зв’язку користувачеві.

Отже, почнемо з цього.

Кінцеві точки

Надсилання даних є більш простою частиною. Обидві кінцеві точки очікують a POST запит, а динамічна частина URL-адреси – це ідентифікатор форми.

Contact Form 7 REST API стає доступним одразу після активації плагіна, і виглядає він так:

https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback

Якщо ми працюємо з Gravity Forms, кінцева точка приймає таку форму:

https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions

REST API Gravity Forms вимкнено за замовчуванням. Щоб увімкнути його, нам потрібно перейти до налаштувань плагіна, потім на сторінку REST API і позначити опцію «Увімкнути доступ до API». Немає необхідності створювати ключ API, оскільки кінцева точка надсилання форми його не вимагає.

Тіло запиту

У нашому прикладі форми є п’ять полів із такими правилами:

  • обов’язкове текстове поле
  • обов’язкове поле електронної пошти
  • обов’язкове поле дати, яке приймає дати до 4 жовтня 1957 року
  • додаткове текстове поле
  • обов’язковий прапорець

Для запитів контактної форми 7 body ключі, ми повинні визначити їх за допомогою синтаксис тегів форми:

{ "somebodys-name": "Marian Kenney", "any-email": "[email protected]", "before-space-age": "1922-03-11", "optional-message": "", "fake-terms": "1"
}

Gravity Forms очікує ключі в іншому форматі. Ми повинні використовувати автоматично згенерований інкрементний ідентифікатор поля з input_ префікс. Ідентифікатор видно під час редагування поля.

{ "input_1": "Marian Kenney", "input_2": "[email protected]", "input_3": "1922-03-11", "input_4": "", "input_5_1": "1"
}

Подача даних

Ми можемо заощадити собі багато роботи, якщо використовуємо очікувані ключі для вводу. name атрибути. В іншому випадку нам доведеться зіставити назви вхідних даних із ключами.

Зібравши все разом, ми отримаємо таку структуру HTML для контактної форми 7:

<form action="https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback" method="post"> <label for="somebodys-name">Somebody's name</label> <input id="somebodys-name" type="text" name="somebodys-name"> <!-- Other input elements --> <button type="submit">Submit</button>
</form>

У випадку Gravity Forms нам потрібно лише перемкнути action і name атрибути:

<form action="https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions" method="post"> <label for="input_1">Somebody's name</label> <input id="input_1" type="text" name="input_1"> <!-- Other input elements --> <button type="submit">Submit</button>
</form>

Оскільки вся необхідна інформація доступна в HTML, ми готові надіслати запит. Один із способів зробити це – використовувати FormData у поєднанні з fetch:

const formSubmissionHandler = (event) => { event.preventDefault(); const formElement = event.target, { action, method } = formElement, body = new FormData(formElement); fetch(action, { method, body }) .then((response) => response.json()) .then((response) => { // Determine if the submission is not valid if (isFormSubmissionError(response)) { // Handle the case when there are validation errors } // Handle the happy path }) .catch((error) => { // Handle the case when there's a problem with the request });
}; const formElement = document.querySelector("form"); formElement.addEventListener("submit", formSubmissionHandler);

Ми можемо надіслати подання з незначними зусиллями, але користувальницький досвід, м’яко кажучи, нижчий. Ми повинні надати користувачам якомога більше вказівок, щоб успішно подати форму. Принаймні це означає, що нам потрібно:

  • показати глобальну помилку або повідомлення про успіх,
  • додати вбудовані повідомлення про помилку перевірки поля та можливі вказівки, а також
  • привертайте увагу до частин, які вимагають уваги, за допомогою спеціальних занять.

Перевірка поля

Окрім використання вбудованої перевірки форм HTML, ми можемо використовувати JavaScript для додаткової перевірки на стороні клієнта та/або скористатися перевагами перевірки на стороні сервера.

Що стосується перевірки на стороні сервера, як Contact Form 7, так і Gravity Forms пропонують це з коробки та повертають повідомлення про помилку перевірки як частину відповіді. Це зручно, оскільки ми можемо контролювати правила перевірки від адміністратора WordPress.

Для більш складних правил перевірки, як-от перевірка умовного поля, може бути доцільно покладатися лише на серверну сторону, оскільки синхронізація зовнішньої перевірки JavaScript із налаштуваннями плагінів може стати проблемою обслуговування.

Якщо ми використовуємо лише перевірку на стороні сервера, завдання зводиться до аналізу відповіді, вилучення відповідних даних і маніпулювання DOM, як-от вставка елементів і перемикання імен класів.

Відповідні повідомлення

Відповідь, коли виникає помилка перевірки для контактної форми 7, виглядає так:

{ "into": "#", "status": "validation_failed", "message": "One or more fields have an error. Please check and try again.", "posted_data_hash": "", "invalid_fields": [ { "into": "span.wpcf7-form-control-wrap.somebodys-name", "message": "The field is required.", "idref": null, "error_id": "-ve-somebodys-name" }, { "into": "span.wpcf7-form-control-wrap.any-email", "message": "The field is required.", "idref": null, "error_id": "-ve-any-email" }, { "into": "span.wpcf7-form-control-wrap.before-space-age", "message": "The field is required.", "idref": null, "error_id": "-ve-before-space-age" }, { "into": "span.wpcf7-form-control-wrap.fake-terms", "message": "You must accept the terms and conditions before sending your message.", "idref": null, "error_id": "-ve-fake-terms" } ]
}

Після успішного надсилання відповідь виглядає так:

{ "into": "#", "status": "mail_sent", "message": "Thank you for your message. It has been sent.", "posted_data_hash": "d52f9f9de995287195409fe6dcde0c50"
}

Порівняно з цим, відповідь Gravity Forms на помилку перевірки більш компактна:

{ "is_valid": false, "validation_messages": { "1": "This field is required.", "2": "This field is required.", "3": "This field is required.", "5": "This field is required." }, "page_number": 1, "source_page_number": 1
}

Але відповідь на успішне подання більша:

{ "is_valid": true, "page_number": 0, "source_page_number": 1, "confirmation_message": "<div id='gform_confirmation_wrapper_1' class='gform_confirmation_wrapper '><div id='gform_confirmation_message_1' class='gform_confirmation_message_1 gform_confirmation_message'>Thanks for contacting us! We will get in touch with you shortly.</div></div>", "confirmation_type": "message"
}

Хоча обидва містять потрібну нам інформацію, вони не дотримуються загальної конвенції та мають свої особливості. Наприклад, повідомлення про підтвердження в Gravity Forms містить HTML, а ключі повідомлення про підтвердження не мають input_ prefix — префікс, який потрібен, коли ми надсилаємо запит. З іншого боку, помилки перевірки в контактній формі 7 містять інформацію, яка стосується лише їх зовнішньої реалізації. Ключі полів не можна використовувати одразу; їх треба витягти.

У такій ситуації замість того, щоб працювати з відповіддю, яку ми отримуємо, краще придумати бажаний, ідеальний формат. Отримавши це, ми зможемо знайти способи перетворити оригінальну відповідь на те, що вважаємо за потрібне. Якщо ми об’єднаємо найкраще з двох сценаріїв і видалимо нерелевантні частини для нашого сценарію використання, то вийде щось на зразок цього:

{ "isSuccess": false, "message": "One or more fields have an error. Please check and try again.", "validationError": { "somebodys-name": "This field is required.", "any-email": "This field is required.", "input_3": "This field is required.", "input_5": "This field is required." }
}

І після успішного подання ми встановлюємо isSuccess до true і повертає порожній об’єкт помилки перевірки:

{ "isSuccess": true, "message": "Thanks for contacting us! We will get in touch with you shortly.", "validationError": {}
}

Тепер справа за тим, щоб перетворити те, що ми маємо, на те, що нам потрібно. Код для нормалізації відповіді Contact Forms 7 такий:

const normalizeContactForm7Response = (response) => { // The other possible statuses are different kind of errors const isSuccess = response.status === 'mail_sent'; // A message is provided for all statuses const message = response.message; const validationError = isSuccess ? {} : // We transform an array of objects into an object Object.fromEntries( response.invalid_fields.map((error) => { // Extracts the part after "cf7-form-control-wrap" const key = /cf7[-a-z]*.(.*)/.exec(error.into)[1]; return [key, error.message]; }) ); return { isSuccess, message, validationError, };
};

Код для нормалізації відповіді Gravity Forms виглядає так:

const normalizeGravityFormsResponse = (response) => { // Provided already as a boolean in the response const isSuccess = response.is_valid; const message = isSuccess ? // Comes wrapped in a HTML and we likely don't need that stripHtml(response.confirmation_message) : // No general error message, so we set a fallback 'There was a problem with your submission.'; const validationError = isSuccess ? {} : // We replace the keys with the prefixed version; // this way the request and response matches Object.fromEntries( Object.entries( response.validation_messages ).map(([key, value]) => [`input_${key}`, value]) ); return { isSuccess, message, validationError, };
};

Нам досі не вистачає способу відображення помилок перевірки, повідомлень про успіх і перемикання класів. Однак у нас є зручний спосіб доступу до потрібних нам даних, і ми усунули всі невідповідності у відповідях за допомогою легкої абстракції. Коли він зібраний, він готовий до розміщення в існуючій кодовій базі, або ми можемо продовжити створення на його основі.

Є багато способів впоратися з частиною, що залишилася. Те, що має сенс, залежатиме від проекту. У ситуаціях, коли ми в основному повинні реагувати на зміни стану, декларативна та реактивна бібліотеки можуть дуже допомогти. Alpine.js був висвітлено тут на CSS-Tricks, і він ідеально підходить як для демонстрацій, так і для використання на робочих сайтах. Майже без будь-яких змін ми можемо повторно використовувати код із попереднього прикладу. Нам потрібно лише додати відповідні директиви в потрібних місцях.

Підводячи підсумок

Зіставлення інтерфейсу, яке надають плагіни форм WordPress, можна зробити з відносною легкістю для простих, простих форм — і таким чином, щоб можна було повторно використовувати від проекту до проекту. Ми навіть можемо зробити це таким чином, щоб ми могли перемикати плагін, не впливаючи на зовнішню частину.

Звичайно, потрібен час і зусилля, щоб створити багатосторінкову форму, попередній перегляд завантажених зображень або інші розширені функції, які ми зазвичай вставляємо прямо в плагін, але чим унікальніші вимоги, яким ми маємо відповідати, тим більше має сенс використовувати кінцеву точку подання, оскільки нам не потрібно працювати з даною інтерфейсною реалізацією, яка намагається вирішити багато проблем, але ніколи не конкретну, яку ми хочемо.

Використання WordPress як безголової CMS для доступу до REST API плагіна форми для досягнення кінцевих точок подання, безсумнівно, стане більш широко використовуваною практикою. Це те, що варто досліджувати та мати на увазі. У майбутньому я не здивуюся, якщо побачу плагіни форм WordPress, призначені в основному для роботи в безголовому контексті, як цей. Я можу уявити плагін, у якому зовнішня рендеринг є додатковою функцією, яка не є невід’ємною частиною його ядра. Які наслідки це матиме та чи зможе це мати комерційний успіх, ще належить дослідити, але це захоплюючий простір для спостереження за розвитком.

Джерело: https://css-tricks.com/headless-form-submission-with-the-wordpress-rest-api/

Часова мітка:

Більше від CSS-хитрощі