Initial import of animal_management module
This commit is contained in:
348
models/forms/AnimalProgressUpdateForm.php
Normal file
348
models/forms/AnimalProgressUpdateForm.php
Normal file
@@ -0,0 +1,348 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user