/*
* SymfonyFormDataBuilder
* Utility to create and update FormData for Symfony-style form field names.
*
* Usage:
* const builder = new SymfonyFormDataBuilder('reviewer_settings');
* builder.setTextArrayField('candidateDeclineCommentsAsArray', ['a','b']);
* const fd = builder.getFormData();
*/
class SymfonyFormDataBuilder {
/**
* @param {string} formPrefix - optional form prefix used by Symfony form (e.g. "reviewer_settings").
*/
constructor(formPrefix = '') {
this.formPrefix = formPrefix || '';
this.formData = null; // lazy-created FormData
}
// internal: build full field name with prefix
_prefixedName(name) {
if (!this.formPrefix) return name;
return `${this.formPrefix}[${name}]`;
}
// Create new FormData (replaces existing)
create() {
this.formData = new FormData();
return this.formData;
}
// Ensure FormData exists
ensure() {
if (!this.formData) this.create();
return this.formData;
}
// Completely clear current FormData
clear() {
this.formData = new FormData();
return this;
}
// Get current FormData (may be null)
getFormData() {
return this.formData;
}
// Set scalar field (overwrites existing key)
setField(name, value) {
this.ensure();
this.formData.set(this._prefixedName(name), value);
return this;
}
// Append scalar field (allows duplicate keys)
appendField(name, value) {
this.ensure();
this.formData.append(this._prefixedName(name), value);
return this;
}
// Remove all keys that belong to array field `name`, e.g. form[name][0], form[name][1]...
_removeArrayKeys(name) {
if (!this.formData) return;
// Remove any key that equals baseName or starts with baseName[ (handles different renderings)
const baseName = this._prefixedName(name); // e.g. reviewer_settings[candidateDeclineCommentsAsArray]
const prefix = baseName + '[';
const newFd = new FormData();
for (let pair of this.formData.entries()) {
const key = pair[0];
const value = pair[1];
if (key === baseName || key.indexOf(prefix) === 0) {
// skip array keys for this name
continue;
}
newFd.append(key, value);
}
this.formData = newFd;
}
removeField(name) {
if (!this.formData) return this;
const fullName = this._prefixedName(name);
const newFd = new FormData();
for (let pair of this.formData.entries()) {
const key = pair[0];
const value = pair[1];
if (key === fullName) {
// skip this key
continue;
}
newFd.append(key, value);
}
this.formData = newFd;
return this;
}
/**
* Установить поле-массив текстовых строк для Symfony-формы.
* Будет создана (если необходимо) FormData, существующие ключи этого массива удалены и
* добавлены новые элементы в форме `prefix[name][]`.
*
* @param {string} name - имя поля, например 'candidateDeclineCommentsAsArray'
* @param {string[]} textArray - массив строк
* @returns {SymfonyFormDataBuilder}
*/
setTextArrayField(name, textArray) {
if (!Array.isArray(textArray)) {
throw new Error('textArray must be an Array of strings');
}
this.ensure();
// remove old keys related to this array
this._removeArrayKeys(name);
const baseName = this._prefixedName(name); // e.g. reviewer_settings[candidateDeclineCommentsAsArray]
// Symfony обычно ожидает индексы 0..N-1. Добавляем поля именно в таком формате.
// Используем формат с пустыми скобками (e.g. name[]), который PHP/Symfony распарсят как массив.
const arrayKey = `${baseName}[]`;
for (let i = 0; i < textArray.length; i++) {
const value = textArray[i] == null ? '' : String(textArray[i]);
this.formData.append(arrayKey, value);
}
// Для безопасности: если массив пустой, добавим пустой ключ без индекса — Symfony может корректно обработать.
// if (textArray.length === 0) {
// // ensure there's at least an empty value so Symfony treats it as an empty array
// const arrayKeyEmpty = `${baseName}[]`;
// this.formData.append(arrayKeyEmpty, '');
// }
// NOTE: removed DOM synchronization. This builder now only manipulates FormData,
// and does not add/remove hidden inputs in the HTML form. The caller may
// submit FormData via fetch/XHR or use native form rendering to handle inputs.
return this;
}
// Debug helper: convert FormData to plain object (arrays for duplicate keys)
toObject() {
if (!this.formData) return {};
const out = {};
for (let pair of this.formData.entries()) {
const key = pair[0];
const value = pair[1];
if (out.hasOwnProperty(key)) {
if (!Array.isArray(out[key])) out[key] = [out[key]];
out[key].push(value);
} else {
out[key] = value;
}
}
return out;
}
}