347 lines
12 KiB
PHP
347 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\AnimalMedicalVisit;
|
|
use humhub\modules\animal_management\models\AnimalMedicalVisitFieldValue;
|
|
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 AnimalMedicalVisitForm extends Model
|
|
{
|
|
public const MODULE_ID = 'animal_management';
|
|
|
|
public Animal $animal;
|
|
|
|
public string $visit_at = '';
|
|
public string $provider_name = '';
|
|
public string $notes = '';
|
|
public string $recommendations = '';
|
|
public bool $post_to_space_feed = false;
|
|
public bool $post_to_animal_feed = true;
|
|
public array $customFields = [];
|
|
public ?AnimalMedicalVisit $medicalVisit = null;
|
|
|
|
private array $customFieldDefinitions = [];
|
|
|
|
public function init()
|
|
{
|
|
parent::init();
|
|
$this->bootstrapCustomFieldDefinitions();
|
|
}
|
|
|
|
public function rules()
|
|
{
|
|
return [
|
|
[['visit_at', 'provider_name', 'notes', 'recommendations'], 'safe'],
|
|
[['provider_name'], 'string', 'max' => 190],
|
|
[['notes', 'recommendations'], 'string'],
|
|
[['post_to_space_feed', 'post_to_animal_feed'], 'boolean'],
|
|
[['customFields'], 'safe'],
|
|
[['customFields'], 'validateCustomFieldValues'],
|
|
];
|
|
}
|
|
|
|
public function save(): bool
|
|
{
|
|
if (!$this->validate()) {
|
|
return false;
|
|
}
|
|
|
|
$record = $this->medicalVisit instanceof AnimalMedicalVisit
|
|
? $this->medicalVisit
|
|
: new AnimalMedicalVisit();
|
|
|
|
$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->visit_at = $this->normalizeDateTime($this->visit_at);
|
|
$record->provider_name = trim($this->provider_name);
|
|
$record->notes = trim($this->notes);
|
|
$record->recommendations = trim($this->recommendations);
|
|
if ($record->hasAttribute('post_to_space_feed') && $record->hasAttribute('post_to_animal_feed')) {
|
|
$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->medicalVisit = $record;
|
|
|
|
if (!$this->saveCustomFieldValues($record)) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->post_to_space_feed) {
|
|
AnimalStreamPublisherService::publishMedicalVisit($this->animal, $record);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function setMedicalVisit(AnimalMedicalVisit $medicalVisit): void
|
|
{
|
|
$this->medicalVisit = $medicalVisit;
|
|
$this->visit_at = (string)$medicalVisit->visit_at;
|
|
$this->provider_name = (string)$medicalVisit->provider_name;
|
|
$this->notes = (string)$medicalVisit->notes;
|
|
$this->recommendations = (string)$medicalVisit->recommendations;
|
|
if ($medicalVisit->hasAttribute('post_to_space_feed') && $medicalVisit->hasAttribute('post_to_animal_feed')) {
|
|
$this->post_to_space_feed = ((int)$medicalVisit->post_to_space_feed) === 1;
|
|
$this->post_to_animal_feed = ((int)$medicalVisit->post_to_animal_feed) === 1;
|
|
} else {
|
|
$this->post_to_space_feed = false;
|
|
$this->post_to_animal_feed = true;
|
|
}
|
|
|
|
$this->loadCustomFieldValues($medicalVisit);
|
|
}
|
|
|
|
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_medical_visit', '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(AnimalMedicalVisit $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 = AnimalMedicalVisitFieldValue::findOne([
|
|
'medical_visit_id' => (int)$record->id,
|
|
'field_definition_id' => $fieldDefinitionId,
|
|
]);
|
|
|
|
if ($this->isCustomValueEmpty($valueText, $inputType)) {
|
|
if ($valueRecord instanceof AnimalMedicalVisitFieldValue) {
|
|
$valueRecord->delete();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!$valueRecord instanceof AnimalMedicalVisitFieldValue) {
|
|
$valueRecord = new AnimalMedicalVisitFieldValue();
|
|
$valueRecord->medical_visit_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 medical 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(AnimalMedicalVisit $record): void
|
|
{
|
|
foreach ($this->customFieldDefinitions as $fieldKey => $definition) {
|
|
$value = AnimalMedicalVisitFieldValue::findOne([
|
|
'medical_visit_id' => (int)$record->id,
|
|
'field_definition_id' => (int)$definition['id'],
|
|
]);
|
|
|
|
if (!$value instanceof AnimalMedicalVisitFieldValue) {
|
|
continue;
|
|
}
|
|
|
|
$this->customFields[$fieldKey] = (string)$value->value_text;
|
|
}
|
|
}
|
|
}
|