349 lines
12 KiB
PHP
349 lines
12 KiB
PHP
<?php
|
|
|
|
namespace humhub\modules\animal_management\models\forms;
|
|
|
|
use humhub\modules\animal_management\models\Animal;
|
|
use humhub\modules\animal_management\models\AnimalProgressUpdate;
|
|
use humhub\modules\animal_management\models\AnimalProgressUpdateFieldValue;
|
|
use humhub\modules\animal_management\services\AnimalStreamPublisherService;
|
|
use humhub\modules\rescue_foundation\models\RescueFieldDefinition;
|
|
use Yii;
|
|
use yii\base\Model;
|
|
use yii\helpers\Json;
|
|
|
|
class AnimalProgressUpdateForm extends Model
|
|
{
|
|
public const MODULE_ID = 'animal_management';
|
|
|
|
public Animal $animal;
|
|
|
|
public string $update_at = '';
|
|
public string $weight = '';
|
|
public string $vitals = '';
|
|
public string $behavior_notes = '';
|
|
public string $meal_plan_changes = '';
|
|
public string $housing_changes = '';
|
|
public string $medical_concerns = '';
|
|
public bool $post_to_space_feed = false;
|
|
public bool $post_to_animal_feed = true;
|
|
public array $customFields = [];
|
|
public ?AnimalProgressUpdate $progressUpdate = null;
|
|
|
|
private array $customFieldDefinitions = [];
|
|
|
|
public function init()
|
|
{
|
|
parent::init();
|
|
$this->bootstrapCustomFieldDefinitions();
|
|
}
|
|
|
|
public function rules()
|
|
{
|
|
return [
|
|
[['update_at', 'weight', 'vitals', 'behavior_notes', 'meal_plan_changes', 'housing_changes', 'medical_concerns'], 'safe'],
|
|
[['vitals', 'behavior_notes', 'meal_plan_changes', 'housing_changes', 'medical_concerns'], 'string'],
|
|
[['weight'], 'string', 'max' => 32],
|
|
[['post_to_space_feed', 'post_to_animal_feed'], 'boolean'],
|
|
[['customFields'], 'safe'],
|
|
[['customFields'], 'validateCustomFieldValues'],
|
|
];
|
|
}
|
|
|
|
public function save(): bool
|
|
{
|
|
if (!$this->validate()) {
|
|
return false;
|
|
}
|
|
|
|
$record = $this->progressUpdate instanceof AnimalProgressUpdate
|
|
? $this->progressUpdate
|
|
: new AnimalProgressUpdate();
|
|
|
|
$isNew = $record->getIsNewRecord();
|
|
|
|
if ($isNew) {
|
|
$record->animal_id = (int)$this->animal->id;
|
|
$record->created_by = Yii::$app->user->isGuest ? null : (int)Yii::$app->user->id;
|
|
}
|
|
|
|
$record->update_at = $this->normalizeDateTime($this->update_at);
|
|
$record->weight = trim($this->weight);
|
|
$record->vitals = trim($this->vitals);
|
|
$record->behavior_notes = trim($this->behavior_notes);
|
|
$record->meal_plan_changes = trim($this->meal_plan_changes);
|
|
$record->housing_changes = trim($this->housing_changes);
|
|
$record->medical_concerns = trim($this->medical_concerns);
|
|
$record->post_to_space_feed = $this->post_to_space_feed ? 1 : 0;
|
|
$record->post_to_animal_feed = $this->post_to_animal_feed ? 1 : 0;
|
|
|
|
if (!$record->save()) {
|
|
$this->addErrors($record->getErrors());
|
|
return false;
|
|
}
|
|
|
|
$this->progressUpdate = $record;
|
|
|
|
if (!$this->saveCustomFieldValues($record)) {
|
|
return false;
|
|
}
|
|
|
|
if ($record->post_to_space_feed) {
|
|
AnimalStreamPublisherService::publishProgressUpdate($this->animal, $record);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function setProgressUpdate(AnimalProgressUpdate $progressUpdate): void
|
|
{
|
|
$this->progressUpdate = $progressUpdate;
|
|
$this->update_at = (string)$progressUpdate->update_at;
|
|
$this->weight = (string)$progressUpdate->weight;
|
|
$this->vitals = (string)$progressUpdate->vitals;
|
|
$this->behavior_notes = (string)$progressUpdate->behavior_notes;
|
|
$this->meal_plan_changes = (string)$progressUpdate->meal_plan_changes;
|
|
$this->housing_changes = (string)$progressUpdate->housing_changes;
|
|
$this->medical_concerns = (string)$progressUpdate->medical_concerns;
|
|
$this->post_to_space_feed = ((int)$progressUpdate->post_to_space_feed) === 1;
|
|
$this->post_to_animal_feed = ((int)$progressUpdate->post_to_animal_feed) === 1;
|
|
|
|
$this->loadCustomFieldValues($progressUpdate);
|
|
}
|
|
|
|
public function getCustomFieldDefinitions(): array
|
|
{
|
|
return $this->customFieldDefinitions;
|
|
}
|
|
|
|
public function getCustomFieldSelectOptions(string $fieldKey): array
|
|
{
|
|
if (!isset($this->customFieldDefinitions[$fieldKey])) {
|
|
return [];
|
|
}
|
|
|
|
return $this->parseSelectOptions((string)$this->customFieldDefinitions[$fieldKey]['options']);
|
|
}
|
|
|
|
public function validateCustomFieldValues(string $attribute): void
|
|
{
|
|
foreach ($this->customFieldDefinitions as $fieldKey => $definition) {
|
|
$value = $this->customFields[$fieldKey] ?? null;
|
|
$label = (string)$definition['label'];
|
|
$inputType = (string)$definition['input_type'];
|
|
$required = ((int)$definition['required']) === 1;
|
|
|
|
if ($required && $this->isCustomValueEmpty($value, $inputType)) {
|
|
$this->addError($attribute, Yii::t('AnimalManagementModule.base', '{field} is required.', ['{field}' => $label]));
|
|
continue;
|
|
}
|
|
|
|
if ($this->isCustomValueEmpty($value, $inputType)) {
|
|
continue;
|
|
}
|
|
|
|
$normalized = is_array($value) ? '' : trim((string)$value);
|
|
|
|
if ($inputType === 'number' && !is_numeric($normalized)) {
|
|
$this->addError($attribute, Yii::t('AnimalManagementModule.base', '{field} must be a valid number.', ['{field}' => $label]));
|
|
continue;
|
|
}
|
|
|
|
if (($inputType === 'date' || $inputType === 'datetime') && strtotime($normalized) === false) {
|
|
$this->addError($attribute, Yii::t('AnimalManagementModule.base', '{field} must be a valid date/time.', ['{field}' => $label]));
|
|
continue;
|
|
}
|
|
|
|
if ($inputType === 'select') {
|
|
$options = $this->getCustomFieldSelectOptions($fieldKey);
|
|
if (!empty($options) && !array_key_exists($normalized, $options)) {
|
|
$this->addError($attribute, Yii::t('AnimalManagementModule.base', 'Invalid selection for {field}.', ['{field}' => $label]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function normalizeDateTime(string $value): string
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return date('Y-m-d H:i:s');
|
|
}
|
|
|
|
$timestamp = strtotime($value);
|
|
if ($timestamp === false) {
|
|
return date('Y-m-d H:i:s');
|
|
}
|
|
|
|
return date('Y-m-d H:i:s', $timestamp);
|
|
}
|
|
|
|
private function bootstrapCustomFieldDefinitions(): void
|
|
{
|
|
if (!class_exists(RescueFieldDefinition::class)) {
|
|
return;
|
|
}
|
|
|
|
if (Yii::$app->db->schema->getTableSchema('rescue_field_definition', true) === null) {
|
|
return;
|
|
}
|
|
|
|
$definitions = RescueFieldDefinition::find()
|
|
->where(['module_id' => self::MODULE_ID, 'group_key' => 'animal_progress_update', 'is_active' => 1])
|
|
->orderBy(['sort_order' => SORT_ASC, 'id' => SORT_ASC])
|
|
->all();
|
|
|
|
foreach ($definitions as $definition) {
|
|
$fieldKey = (string)$definition->field_key;
|
|
$this->customFieldDefinitions[$fieldKey] = [
|
|
'id' => (int)$definition->id,
|
|
'field_key' => $fieldKey,
|
|
'label' => trim((string)$definition->label) !== '' ? (string)$definition->label : $fieldKey,
|
|
'input_type' => (string)$definition->input_type,
|
|
'required' => (int)$definition->required,
|
|
'visibility' => (string)$definition->visibility,
|
|
'options' => (string)$definition->options,
|
|
];
|
|
}
|
|
}
|
|
|
|
private function saveCustomFieldValues(AnimalProgressUpdate $record): bool
|
|
{
|
|
foreach ($this->customFieldDefinitions as $fieldKey => $definition) {
|
|
$fieldDefinitionId = (int)$definition['id'];
|
|
if ($fieldDefinitionId === 0) {
|
|
continue;
|
|
}
|
|
|
|
$inputType = (string)$definition['input_type'];
|
|
$rawValue = $this->customFields[$fieldKey] ?? null;
|
|
$valueText = $this->normalizeCustomValue($rawValue, $inputType);
|
|
|
|
$valueRecord = AnimalProgressUpdateFieldValue::findOne([
|
|
'progress_update_id' => (int)$record->id,
|
|
'field_definition_id' => $fieldDefinitionId,
|
|
]);
|
|
|
|
if ($this->isCustomValueEmpty($valueText, $inputType)) {
|
|
if ($valueRecord instanceof AnimalProgressUpdateFieldValue) {
|
|
$valueRecord->delete();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!$valueRecord instanceof AnimalProgressUpdateFieldValue) {
|
|
$valueRecord = new AnimalProgressUpdateFieldValue();
|
|
$valueRecord->progress_update_id = (int)$record->id;
|
|
$valueRecord->field_definition_id = $fieldDefinitionId;
|
|
}
|
|
|
|
$valueRecord->value_text = $valueText;
|
|
$valueRecord->value_json = null;
|
|
|
|
if (!$valueRecord->save()) {
|
|
$this->addError('customFields', Yii::t('AnimalManagementModule.base', 'Could not save custom progress field {field}.', [
|
|
'{field}' => (string)$definition['label'],
|
|
]));
|
|
foreach ($valueRecord->getFirstErrors() as $error) {
|
|
$this->addError('customFields', $error);
|
|
}
|
|
}
|
|
}
|
|
|
|
return !$this->hasErrors('customFields');
|
|
}
|
|
|
|
private function normalizeCustomValue($value, string $inputType): ?string
|
|
{
|
|
if ($inputType === 'boolean') {
|
|
return !empty($value) ? '1' : '0';
|
|
}
|
|
|
|
if (is_array($value)) {
|
|
return Json::encode($value);
|
|
}
|
|
|
|
return trim((string)$value);
|
|
}
|
|
|
|
private function isCustomValueEmpty($value, string $inputType): bool
|
|
{
|
|
if ($inputType === 'boolean') {
|
|
return false;
|
|
}
|
|
|
|
if (is_array($value)) {
|
|
return empty($value);
|
|
}
|
|
|
|
return trim((string)$value) === '';
|
|
}
|
|
|
|
private function parseSelectOptions(string $options): array
|
|
{
|
|
$options = trim($options);
|
|
if ($options === '') {
|
|
return [];
|
|
}
|
|
|
|
$decoded = null;
|
|
try {
|
|
$decoded = Json::decode($options, true);
|
|
} catch (\Throwable $e) {
|
|
$decoded = null;
|
|
}
|
|
|
|
if (is_array($decoded)) {
|
|
$result = [];
|
|
if (array_values($decoded) === $decoded) {
|
|
foreach ($decoded as $item) {
|
|
$item = (string)$item;
|
|
if ($item === '') {
|
|
continue;
|
|
}
|
|
$result[$item] = $item;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
foreach ($decoded as $key => $value) {
|
|
$key = (string)$key;
|
|
if ($key === '') {
|
|
continue;
|
|
}
|
|
$result[$key] = (string)$value;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
$result = [];
|
|
foreach (preg_split('/[\r\n,]+/', $options) as $item) {
|
|
$item = trim((string)$item);
|
|
if ($item === '') {
|
|
continue;
|
|
}
|
|
|
|
$result[$item] = $item;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function loadCustomFieldValues(AnimalProgressUpdate $record): void
|
|
{
|
|
foreach ($this->customFieldDefinitions as $fieldKey => $definition) {
|
|
$value = AnimalProgressUpdateFieldValue::findOne([
|
|
'progress_update_id' => (int)$record->id,
|
|
'field_definition_id' => (int)$definition['id'],
|
|
]);
|
|
|
|
if (!$value instanceof AnimalProgressUpdateFieldValue) {
|
|
continue;
|
|
}
|
|
|
|
$this->customFields[$fieldKey] = (string)$value->value_text;
|
|
}
|
|
}
|
|
}
|