| @copyright 2007-2026 (c) AVE.cms | @link https://ave-cms.ru | @version 3.3 */ namespace App\Content\Fields; defined('BASEPATH') || die('Direct access to this location is not allowed.'); use App\Helpers\Str; use App\Helpers\Json; /** * Форма настроек поля из его settingsSchema() — конструктор рубрики. * * render() строит HTML-контролы (имена rubric_field_settings[]), * collect() симметрично нормализует POST обратно в массив настроек для JSON. * Дескриптор схемы: ['key','type','label','options'?,'default'?,'multiple'?]. * Поддержаны type: text|int|number|bool|select|list|map|rubric. */ class FieldSettingsForm { /** Есть ли у типа настраиваемые параметры/правила. */ public static function has($type) { return !empty(self::schema($type)) || !empty(self::validationSchemaOf($type)); } public static function render($type, array $settings = array()) { $schema = self::schema($type); $validation = self::validationSchemaOf($type); if (empty($schema) && empty($validation)) { return ''; } $html = ''; if (!empty($schema)) { $html .= '
'; foreach ($schema as $desc) { $html .= self::field($desc, $settings); } $html .= '
'; } if (!empty($validation)) { $html .= '
Валидация
' . '
'; foreach ($validation as $desc) { $html .= self::field($desc, $settings); } $html .= '
'; } return $html; } protected static function field(array $desc, array $settings) { $key = isset($desc['key']) ? (string) $desc['key'] : ''; if ($key === '') { return ''; } $value = array_key_exists($key, $settings) ? $settings[$key] : (isset($desc['default']) ? $desc['default'] : ''); // Числовые правила (мин/макс, длина, количество) — половина ряда, встают // парами в одну линию; булев тумблер и текст — на всю ширину. $col = (isset($desc['type']) && in_array($desc['type'], array('int', 'number'), true)) ? 'col-6' : 'col-12'; $hint = (isset($desc['hint']) && $desc['hint'] !== '') ? '' . self::e($desc['hint']) . '' : ''; $tag = isset($desc['type']) && $desc['type'] === 'map' ? 'div' : 'label'; return '<' . $tag . ' class="field ' . $col . '">' . self::e(isset($desc['label']) ? $desc['label'] : $key) . '' . self::control($desc, $key, $value) . $hint . ''; } /** POST rubric_field_settings[...] + тип → нормализованный массив настроек. */ public static function collect($type, $raw) { $raw = is_array($raw) ? $raw : array(); $descriptors = array_merge(self::schema($type), self::validationSchemaOf($type)); $out = array(); foreach ($descriptors as $desc) { $key = isset($desc['key']) ? (string) $desc['key'] : ''; if ($key === '') { continue; } $kind = isset($desc['type']) ? (string) $desc['type'] : 'text'; $value = array_key_exists($key, $raw) ? $raw[$key] : null; $out[$key] = self::normalize($kind, $value); } // Пустые необязательные правила не храним: значения по умолчанию // определяет schema самого native-типа. foreach ($out as $k => $v) { if ($v === '' || $v === array() || $v === null || $v === false) { unset($out[$k]); } } return $out; } /** Ключи настроек и правил, которыми владеет конкретный тип поля. */ public static function keys($type) { $keys = array(); foreach (array_merge(self::schema($type), self::validationSchemaOf($type)) as $desc) { $key = isset($desc['key']) ? (string) $desc['key'] : ''; if ($key !== '') { $keys[] = $key; } } return array_values(array_unique($keys)); } /** Готовый JSON для колонки rubric_field_settings (или '' если пусто). */ public static function toJson($type, $raw) { $settings = self::collect($type, $raw); return empty($settings) ? '' : Json::encode($settings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } protected static function schema($type) { $field = FieldRegistry::get((string) $type); return $field ? (array) $field->settingsSchema() : array(); } protected static function validationSchemaOf($type) { $field = FieldRegistry::get((string) $type); return $field ? (array) $field->validationSchema() : array(); } protected static function control(array $desc, $key, $value) { $kind = isset($desc['type']) ? (string) $desc['type'] : 'text'; $name = 'rubric_field_settings[' . self::attr($key) . ']'; switch ($kind) { case 'int': case 'number': return ''; case 'bool': $checked = (!empty($value) && $value !== '0') ? ' checked' : ''; return '' . ''; case 'select': $options = isset($desc['options']) && is_array($desc['options']) ? $desc['options'] : array(); $html = ''; case 'list': $lines = is_array($value) ? $value : preg_split('/\r\n|\r|\n/', (string) $value); return ''; case 'map': $rows = self::mapRows($value); $lines = array(); $html = '
' . '' . '
'; foreach ($rows as $row) { $lines[] = $row['key'] . '=' . $row['label']; $html .= self::mapRowControl($row['key'], $row['label']); } if (empty($rows)) { $html .= self::mapRowControl('', ''); } return $html . '
' . '' . '
'; case 'rubric': $val = is_array($value) ? implode(',', $value) : (string) $value; return ''; default: return ''; } } protected static function mapRows($value) { $rows = array(); if (is_array($value)) { foreach ($value as $key => $label) { if (is_array($label)) { $mapKey = isset($label['value']) ? $label['value'] : (isset($label['key']) ? $label['key'] : $key); $mapLabel = isset($label['label']) ? $label['label'] : (isset($label['title']) ? $label['title'] : ''); } else { $mapKey = $key; $mapLabel = $label; } if ((string) $mapKey !== '' || (string) $mapLabel !== '') { $rows[] = array('key' => (string) $mapKey, 'label' => (string) $mapLabel); } } return $rows; } foreach (preg_split('/\r\n|\r|\n/', (string) $value) as $line) { $line = trim((string) $line); if ($line === '') { continue; } if (strpos($line, '=') !== false) { list($key, $label) = explode('=', $line, 2); } else { $key = $line; $label = $line; } $rows[] = array('key' => trim((string) $key), 'label' => trim((string) $label)); } return $rows; } protected static function mapRowControl($key, $label) { $displayLabel = preg_replace('/\|\s*$/u', '', (string) $label); return '
' . '' . '' . '' . '
'; } protected static function normalize($kind, $value) { switch ($kind) { case 'int': case 'number': return ($value === '' || $value === null) ? '' : (0 + $value); case 'bool': return (!empty($value) && $value !== '0'); case 'list': $lines = is_array($value) ? $value : preg_split('/\r\n|\r|\n/', (string) $value); $out = array(); foreach ((array) $lines as $line) { $line = trim((string) $line); if ($line !== '') { $out[] = $line; } } return array_values(array_unique($out)); case 'map': $lines = is_array($value) ? $value : preg_split('/\r\n|\r|\n/', (string) $value); $out = array(); foreach ((array) $lines as $index => $line) { if (is_array($line)) { $key = isset($line['value']) ? $line['value'] : (isset($line['key']) ? $line['key'] : $index); $label = isset($line['label']) ? $line['label'] : (isset($line['title']) ? $line['title'] : ''); if ((string) $key !== '' && (string) $label !== '') { $out[] = array('value' => (string) $key, 'label' => trim((string) $label)); } continue; } $line = trim((string) $line); if ($line === '') { continue; } if (strpos($line, '=') !== false) { list($k, $label) = explode('=', $line, 2); $k = trim($k); $label = trim($label); } else { $k = $line; $label = $line; } if ($k !== '' && $label !== '') { $out[] = array('value' => $k, 'label' => $label); } } return $out; case 'rubric': $ids = array(); foreach (is_array($value) ? $value : explode(',', (string) $value) as $r) { $r = (int) trim((string) $r); if ($r > 0) { $ids[] = $r; } } return array_values(array_unique($ids)); default: return trim((string) $value); } } protected static function e($value) { return Str::escape((string) $value); } protected static function attr($value) { return Str::escape((string) $value); } }