336 lines
12 KiB
PHP
336 lines
12 KiB
PHP
<?php
|
|
|
|
namespace humhub\modules\animal_management\models\forms;
|
|
|
|
use humhub\modules\rescue_foundation\models\RescueFieldDefinition;
|
|
use Yii;
|
|
use yii\base\Model;
|
|
|
|
class FieldDefinitionSettingsForm extends Model
|
|
{
|
|
public const MODULE_ID = 'animal_management';
|
|
|
|
public array $rows = [];
|
|
|
|
public string $new_field_key = '';
|
|
public string $new_label = '';
|
|
public string $new_input_type = 'text';
|
|
public string $new_group_key = 'animal_profile';
|
|
public string $new_visibility = 'public';
|
|
public int $new_required = 0;
|
|
public int $new_sort_order = 500;
|
|
public string $new_options = '';
|
|
|
|
public function rules()
|
|
{
|
|
return [
|
|
[['rows'], 'safe'],
|
|
[['new_field_key', 'new_label', 'new_group_key', 'new_visibility', 'new_input_type', 'new_options'], 'string'],
|
|
[['new_required', 'new_sort_order'], 'integer'],
|
|
];
|
|
}
|
|
|
|
public static function inputTypeOptions(): array
|
|
{
|
|
return [
|
|
'text' => 'Text',
|
|
'textarea' => 'Textarea',
|
|
'boolean' => 'Boolean',
|
|
'select' => 'Select',
|
|
'number' => 'Number',
|
|
'date' => 'Date',
|
|
'datetime' => 'Date/Time',
|
|
];
|
|
}
|
|
|
|
public static function visibilityOptions(): array
|
|
{
|
|
return [
|
|
'public' => 'Public',
|
|
'private' => 'Private',
|
|
'internal' => 'Internal',
|
|
];
|
|
}
|
|
|
|
public function loadRows(): void
|
|
{
|
|
if (!$this->canUseFieldDefinition()) {
|
|
return;
|
|
}
|
|
|
|
$this->rows = [];
|
|
|
|
$definitions = RescueFieldDefinition::find()
|
|
->where(['module_id' => self::MODULE_ID])
|
|
->orderBy(['group_key' => SORT_ASC, 'sort_order' => SORT_ASC, 'id' => SORT_ASC])
|
|
->all();
|
|
|
|
foreach ($definitions as $definition) {
|
|
$this->rows[] = [
|
|
'id' => (int)$definition->id,
|
|
'field_key' => (string)$definition->field_key,
|
|
'label' => (string)$definition->label,
|
|
'group_key' => (string)$definition->group_key,
|
|
'input_type' => (string)$definition->input_type,
|
|
'required' => (int)$definition->required,
|
|
'is_core' => (int)$definition->is_core,
|
|
'is_active' => (int)$definition->is_active,
|
|
'visibility' => (string)$definition->visibility,
|
|
'sort_order' => (int)$definition->sort_order,
|
|
];
|
|
}
|
|
}
|
|
|
|
public function save(): bool
|
|
{
|
|
if (!$this->canUseFieldDefinition()) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Field definition storage is unavailable.'));
|
|
return false;
|
|
}
|
|
|
|
$this->clearValidationErrors();
|
|
|
|
$byId = [];
|
|
foreach (RescueFieldDefinition::find()->where(['module_id' => self::MODULE_ID])->all() as $definition) {
|
|
$byId[(int)$definition->id] = $definition;
|
|
}
|
|
|
|
$transaction = Yii::$app->db->beginTransaction();
|
|
|
|
try {
|
|
$now = date('Y-m-d H:i:s');
|
|
|
|
foreach ($this->rows as $index => $row) {
|
|
$id = (int)($row['id'] ?? 0);
|
|
if ($id === 0 || !isset($byId[$id])) {
|
|
continue;
|
|
}
|
|
|
|
$definition = $byId[$id];
|
|
$isCore = ((int)$definition->is_core) === 1;
|
|
$remove = !empty($row['remove']);
|
|
|
|
if ($remove && !$isCore) {
|
|
if (!$definition->delete()) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Could not remove field {field}.', [
|
|
'{field}' => (string)$definition->field_key,
|
|
]));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
$label = trim((string)($row['label'] ?? ''));
|
|
$groupKey = trim((string)($row['group_key'] ?? $definition->group_key));
|
|
$visibility = trim((string)($row['visibility'] ?? $definition->visibility));
|
|
$sortOrder = (int)($row['sort_order'] ?? $definition->sort_order);
|
|
$required = !empty($row['required']) ? 1 : 0;
|
|
$isActive = !empty($row['is_active']) ? 1 : 0;
|
|
$inputType = trim((string)($row['input_type'] ?? $definition->input_type));
|
|
$options = trim((string)$definition->options);
|
|
|
|
if ($label === '') {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Field label cannot be empty for {field}.', [
|
|
'{field}' => (string)$definition->field_key,
|
|
]));
|
|
continue;
|
|
}
|
|
|
|
if ($groupKey === '') {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Group key cannot be empty for {field}.', [
|
|
'{field}' => (string)$definition->field_key,
|
|
]));
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($visibility, self::visibilityOptions())) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Invalid visibility for {field}.', [
|
|
'{field}' => (string)$definition->field_key,
|
|
]));
|
|
continue;
|
|
}
|
|
|
|
if (!$isCore && !$this->validateSelectOptions($inputType, $options, (string)$definition->field_key)) {
|
|
continue;
|
|
}
|
|
|
|
$definition->label = $label;
|
|
$definition->group_key = $groupKey;
|
|
$definition->visibility = $visibility;
|
|
$definition->sort_order = $sortOrder;
|
|
$definition->required = $isCore ? (int)$definition->required : $required;
|
|
$definition->is_active = $isCore ? (int)$definition->is_active : $isActive;
|
|
$definition->updated_at = $now;
|
|
|
|
if (!$definition->save()) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Could not save field {field}.', [
|
|
'{field}' => (string)$definition->field_key,
|
|
]));
|
|
foreach ($definition->getFirstErrors() as $error) {
|
|
$this->addError('rows', $error);
|
|
}
|
|
}
|
|
|
|
$this->rows[$index]['is_core'] = (int)$definition->is_core;
|
|
}
|
|
|
|
if ($this->hasErrors()) {
|
|
$transaction->rollBack();
|
|
return false;
|
|
}
|
|
|
|
if (!$this->saveNewField($now)) {
|
|
$transaction->rollBack();
|
|
return false;
|
|
}
|
|
|
|
$transaction->commit();
|
|
return true;
|
|
} catch (\Throwable $e) {
|
|
$transaction->rollBack();
|
|
Yii::error($e, 'animal_management.field_definition_settings');
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Unexpected error while saving field settings.'));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function canUseFieldDefinition(): bool
|
|
{
|
|
if (!class_exists(RescueFieldDefinition::class)) {
|
|
return false;
|
|
}
|
|
|
|
return Yii::$app->db->schema->getTableSchema('rescue_field_definition', true) !== null;
|
|
}
|
|
|
|
private function saveNewField(string $now): bool
|
|
{
|
|
$newFieldKey = strtolower(trim($this->new_field_key));
|
|
$newLabel = trim($this->new_label);
|
|
|
|
if ($newFieldKey === '' && $newLabel === '') {
|
|
return true;
|
|
}
|
|
|
|
if ($newFieldKey === '' || $newLabel === '') {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Both new field key and label are required.'));
|
|
return false;
|
|
}
|
|
|
|
if (!preg_match('/^[a-z0-9_]+$/', $newFieldKey)) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'New field key must contain only lowercase letters, numbers, and underscores.'));
|
|
return false;
|
|
}
|
|
|
|
if (!array_key_exists($this->new_input_type, self::inputTypeOptions())) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Invalid input type for new field.'));
|
|
return false;
|
|
}
|
|
|
|
if (!array_key_exists($this->new_visibility, self::visibilityOptions())) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Invalid visibility for new field.'));
|
|
return false;
|
|
}
|
|
|
|
$groupKey = trim($this->new_group_key);
|
|
if ($groupKey === '') {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'New field group key is required.'));
|
|
return false;
|
|
}
|
|
|
|
$options = trim($this->new_options);
|
|
if (!$this->validateSelectOptions($this->new_input_type, $options, $newFieldKey)) {
|
|
return false;
|
|
}
|
|
|
|
$exists = RescueFieldDefinition::find()
|
|
->where(['module_id' => self::MODULE_ID, 'field_key' => $newFieldKey])
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Field key {key} already exists.', ['{key}' => $newFieldKey]));
|
|
return false;
|
|
}
|
|
|
|
$field = new RescueFieldDefinition();
|
|
$field->module_id = self::MODULE_ID;
|
|
$field->group_key = $groupKey;
|
|
$field->field_key = $newFieldKey;
|
|
$field->label = $newLabel;
|
|
$field->input_type = $this->new_input_type;
|
|
$field->required = $this->new_required ? 1 : 0;
|
|
$field->is_core = 0;
|
|
$field->is_active = 1;
|
|
$field->visibility = $this->new_visibility;
|
|
$field->sort_order = (int)$this->new_sort_order;
|
|
$field->options = $options !== '' ? $options : '{}';
|
|
$field->created_at = $now;
|
|
$field->updated_at = $now;
|
|
|
|
if (!$field->save()) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Could not create new field.'));
|
|
foreach ($field->getFirstErrors() as $error) {
|
|
$this->addError('rows', $error);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
$this->new_field_key = '';
|
|
$this->new_label = '';
|
|
$this->new_input_type = 'text';
|
|
$this->new_group_key = 'animal_profile';
|
|
$this->new_visibility = 'public';
|
|
$this->new_required = 0;
|
|
$this->new_sort_order = 500;
|
|
$this->new_options = '';
|
|
|
|
return true;
|
|
}
|
|
|
|
private function clearValidationErrors(): void
|
|
{
|
|
foreach (array_keys($this->errors) as $attribute) {
|
|
$this->clearErrors($attribute);
|
|
}
|
|
}
|
|
|
|
private function validateSelectOptions(string $inputType, string $options, string $fieldKey): bool
|
|
{
|
|
if ($inputType !== 'select') {
|
|
return true;
|
|
}
|
|
|
|
if ($options === '' || $options === '{}') {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Select field {field} requires options JSON or comma/newline list.', [
|
|
'{field}' => $fieldKey,
|
|
]));
|
|
return false;
|
|
}
|
|
|
|
$decoded = json_decode($options, true);
|
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
if (!is_array($decoded) || empty($decoded)) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Select field {field} options JSON must be a non-empty array/object.', [
|
|
'{field}' => $fieldKey,
|
|
]));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
$parts = preg_split('/[\r\n,]+/', $options);
|
|
$parts = array_filter(array_map(static function ($item) {
|
|
return trim((string)$item);
|
|
}, $parts));
|
|
|
|
if (empty($parts)) {
|
|
$this->addError('rows', Yii::t('AnimalManagementModule.base', 'Select field {field} requires at least one option.', [
|
|
'{field}' => $fieldKey,
|
|
]));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|