Рейтинг:2

buildForm: как отличить перезагрузку страницы от обратного вызова ajax?

флаг br

Я создаю пользовательскую форму в Drupal 9, в которой есть обратные вызовы ajax.

Вовремя билдформ Мне нужно загрузить некоторые дополнительные данные через остаточный вызов внешней службы, которую затем я помещаю внутрь private_tempstore переменная.

Я бы хотел избежать вызова остальной конечной точки во время обратных вызовов ajax и полагаться на сохраненную переменную.

Однако я не могу найти способ различить случай «загрузить страницу» и «случай ajax». Является ли это возможным?

я обнаружил этот ответ это, кажется, обычно работает:

// Пример только для краткости, внедряем сервис request_stack и вызываем 
// getCurrentRequest(), чтобы получить объект запроса, если это возможно.
$запрос = \Drupal::request();
$is_ajax = $request->isXmlHttpRequest();

Но я хотел бы знать, есть ли какой-нибудь помощник\решение, использующее API формы.

флаг ru
ИМХО никакая логика формы никогда не должна основываться на загрузке страницы по сравнению с Ajax. Учитывая, что BigPipe ядра может загружать что угодно с помощью Ajax (или нет) в зависимости от размещения блоков, среды, конфигурации и т. д., это кажется гарантированным путем к фиаско.
Giuseppe avatar
флаг br
@Hudri, это большое соображение, которого я не осознавал, спасибо. Однако есть ли другой способ решить этот вопрос? Я имею в виду, я бы хотел избежать внешнего вызова rest для каждого обратного вызова ajax формы.
флаг ru
TBH Я не совсем понимаю причину вопроса. `buildForm` имеет доступ к `FormStateInterface $form_state`, что не так с `$form_state`?
Giuseppe avatar
флаг br
@Hudri Я попытаюсь перефразировать: во время первого запуска `buildForm` данные, которые мне нужны, получаются из остальной конечной точки, а не внутри `$ form_state`. Затем я сохраняю эти данные в `tempstore` (но я также мог бы использовать `$form_state->setTemporaryValue()`. первый случай и последующие.
флаг ru
Я думаю, вы не видите леса за всеми деревьями :-) Псевдокод `function buildForm() { if (!$form_state->get('some_helper_var') { $tempStore = load_external_stuff(); $form_state-> set('some_helper_var', TRUE); } $form['field_foo']['default_value'] = $form_state->get('field_foo') ?? $tempStore->get('foo')); }`
Giuseppe avatar
флаг br
@Hudri, да, я не видел этого решения :facepalm: Однако теперь, когда я пытаюсь это сделать, `buildForm` вызывается дважды во время обратного вызова ajax - по крайней мере, во время отладки. В первый раз значения состояния формы пусты, поэтому «внешние вещи» все еще загружаются каждый раз, поэтому на самом деле это не работает :-(
Рейтинг:5
флаг cn

Начиная с Drupal 8 объект формы, созданный с помощью buildForm(), не сохраняется между запросом, отображающим форму, и первым запросом Ajax. Так что будьте готовы к тому, что buildForm() будет вызвана снова и должна дать точно такой же результат. Когда вы получаете данные из $form_state, это не те данные, которые вы ожидаете от первого buildForm(), потому что они никогда не кэшируются. Добавляя сложности, визуализированный результат первой сборки кэшируется, поэтому то, что вы сохраняете в первой сборке в другом месте, например в tempStore, может быть устаревшим в запросе Ajax. Единственные данные, которые работают должным образом, — это значения формы, которые можно скрыть, если вы хотите, чтобы они были в отправленной форме и не были видны пользователю.

TLDR: buildForm() вызывается чаще, чем вы думаете, и вам не следует помещать в него код, выполнение которого обходится дорого. Рефакторинг внешнего API-вызова службы с надлежащим кэшированием, чтобы не имело значения, как часто он вызывается. Аннулируйте кеш службы так же, как и отображаемую форму, чтобы ни в одной из них не было устаревших данных.

Giuseppe avatar
флаг br
1. спасибо за пояснение, хотя оно мне не очень понятно. Например. Я не понимаю, как данные, хранящиеся в tempStore, могут устареть, если $form_state не кэшируется, и какое отношение к этому имеет кешированный результат рендеринга. Есть ли более подробная документация о том, как это работает, чтобы лучше понять?
Giuseppe avatar
флаг br
2.«Обнулите кеш службы так же, как и отображаемую форму, чтобы ни в одной из них не было устаревших данных». Как это сделать? Что должно быть «триггером» для аннулирования служебного кеша? Я имею в виду, что я хотел бы сделать это при перезагрузке страницы, должен ли я использовать `KernelEvents::REQUEST` для поиска этого конкретного маршрута? А что касается кеша формы, означает ли это, что я должен просто сделать форму вообще не кешируемой?
4uk4 avatar
флаг cn
1. Основная проблема в том, что вы пытаетесь использовать buildform() вне области видимости. При одних и тех же входных данных он должен давать один и тот же результат столько раз, сколько Drupal вызывает этот метод. Только если $form_state содержит отправленные и обработанные значения формы или элемент запуска, вы можете перестроить другую форму $form и сохранить данные.
4uk4 avatar
флаг cn
2. Добавьте кэш-бункер в службу и кэшируйте с теми же метаданными кэша, которые вы прикрепили к обработанной $form.
4uk4 avatar
флаг cn
Если крайне важно, чтобы вы использовали ту же версию данных внешнего API, вы можете добавить строку версии в качестве скрытого элемента формы.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.