Files
animal_management/widgets/SearchAnimalProfilesBlock.php
2026-04-04 13:13:00 -04:00

307 lines
11 KiB
PHP

<?php
namespace humhub\modules\animal_management\widgets;
use humhub\modules\animal_management\models\Animal;
use humhub\modules\animal_management\models\AnimalFieldValue;
use humhub\modules\animal_management\models\AnimalGalleryItem;
use humhub\modules\animal_management\models\AnimalMedicalVisit;
use humhub\modules\animal_management\models\forms\DisplaySettingsForm;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\rescue_foundation\models\RescueFieldDefinition;
use Yii;
use yii\base\Widget;
class SearchAnimalProfilesBlock extends Widget
{
public ContentContainerActiveRecord $contentContainer;
public int $limit = 10;
public function run()
{
if (!$this->contentContainer->moduleManager->isEnabled('animal_management')) {
return '<div class="well well-sm" style="margin-bottom:0;">'
. Yii::t('AnimalManagementModule.base', 'Animal profiles are not enabled for this rescue.')
. '</div>';
}
$queryValue = trim((string)Yii::$app->request->get('q', ''));
$showAll = ((int)Yii::$app->request->get('animalFeedAll', 0)) === 1;
$countParam = (int)Yii::$app->request->get('animalFeedCount', $this->limit);
if ($countParam < $this->limit) {
$countParam = $this->limit;
}
if ($countParam > 200) {
$countParam = 200;
}
$query = Animal::find()->where(['contentcontainer_id' => $this->contentContainer->contentcontainer_id]);
if ($queryValue !== '') {
$query->andWhere([
'or',
['like', 'animal_uid', $queryValue],
['like', 'name', $queryValue],
['like', 'species', $queryValue],
]);
}
$totalCount = (int)$query->count();
$displayCount = $showAll ? $totalCount : min($countParam, $totalCount);
$settings = Yii::$app->getModule('animal_management')->settings->contentContainer($this->contentContainer);
$heading = trim((string)$settings->get('searchBlockHeading', DisplaySettingsForm::DEFAULT_SEARCH_BLOCK_HEADING));
if ($heading === '') {
$heading = DisplaySettingsForm::DEFAULT_SEARCH_BLOCK_HEADING;
}
$tileFieldsRaw = $settings->get('tileFields', json_encode(DisplaySettingsForm::DEFAULT_TILE_FIELDS));
$tileFields = $this->normalizeDisplayFields($tileFieldsRaw, DisplaySettingsForm::DEFAULT_TILE_FIELDS);
$animals = $query
->orderBy(['updated_at' => SORT_DESC, 'id' => SORT_DESC])
->limit($displayCount)
->all();
$animalIds = array_map(static function (Animal $animal): int {
return (int)$animal->id;
}, $animals);
$latestMedicalVisitByAnimal = $this->resolveLatestMedicalVisits($animalIds);
$animalImageUrls = $this->resolveAnimalImageUrls($animalIds, ['profile_image_url', 'profile_image', 'photo_url', 'image_url', 'photo'], false);
$tileFieldOverrides = $this->resolveDisplayFieldOverrides($animalIds, 'tile_display_fields');
$hasMore = !$showAll && $displayCount < $totalCount;
$nextCount = min($displayCount + $this->limit, $totalCount);
$viewFile = $this->getViewPath() . '/searchAnimalProfilesBlock.php';
if (!is_file($viewFile)) {
return '<div class="well well-sm" style="margin-bottom:0;">'
. Yii::t('AnimalManagementModule.base', 'Animal search is temporarily unavailable.')
. '</div>';
}
return $this->render('searchAnimalProfilesBlock', [
'animals' => $animals,
'contentContainer' => $this->contentContainer,
'queryValue' => $queryValue,
'heading' => $heading,
'tileFields' => $tileFields,
'tileFieldOverrides' => $tileFieldOverrides,
'latestMedicalVisitByAnimal' => $latestMedicalVisitByAnimal,
'animalImageUrls' => $animalImageUrls,
'totalCount' => $totalCount,
'displayCount' => $displayCount,
'hasMore' => $hasMore,
'nextCount' => $nextCount,
'showAll' => $showAll,
]);
}
private function normalizeDisplayFields($raw, array $default): array
{
if (is_string($raw)) {
$decoded = json_decode($raw, true);
if (!is_array($decoded)) {
return $default;
}
$raw = $decoded;
}
if (!is_array($raw)) {
return $default;
}
$allowed = array_keys(DisplaySettingsForm::fieldOptions());
$normalized = [];
foreach ($raw as $field) {
$field = trim((string)$field);
if ($field === '' || !in_array($field, $allowed, true)) {
continue;
}
if (!in_array($field, $normalized, true)) {
$normalized[] = $field;
}
}
return !empty($normalized) ? $normalized : $default;
}
private function resolveLatestMedicalVisits(array $animalIds): array
{
$animalIds = array_values(array_unique(array_map('intval', $animalIds)));
if (empty($animalIds)) {
return [];
}
$visits = AnimalMedicalVisit::find()
->where(['animal_id' => $animalIds])
->orderBy(['animal_id' => SORT_ASC, 'visit_at' => SORT_DESC, 'id' => SORT_DESC])
->all();
$result = [];
foreach ($visits as $visit) {
$animalId = (int)$visit->animal_id;
if (!isset($result[$animalId])) {
$result[$animalId] = $visit;
}
}
return $result;
}
private function resolveAnimalImageUrls(array $animalIds, array $imageFieldOrder = [], bool $allowGalleryFallback = false): array
{
$animalIds = array_values(array_unique(array_map('intval', $animalIds)));
if (empty($animalIds)) {
return [];
}
if (!class_exists(RescueFieldDefinition::class)
|| Yii::$app->db->schema->getTableSchema('rescue_field_definition', true) === null
|| Yii::$app->db->schema->getTableSchema('rescue_animal_field_value', true) === null
) {
return [];
}
if (empty($imageFieldOrder)) {
$imageFieldOrder = ['cover_image_url', 'profile_image_url', 'photo_url', 'image_url', 'profile_image', 'photo'];
}
$definitions = RescueFieldDefinition::find()
->select(['id', 'field_key'])
->where([
'module_id' => 'animal_management',
'group_key' => 'animal_profile',
'field_key' => $imageFieldOrder,
'is_active' => 1,
])
->all();
if (empty($definitions)) {
return [];
}
$definitionPriority = [];
foreach ($definitions as $definition) {
$priority = array_search((string)$definition->field_key, $imageFieldOrder, true);
$definitionPriority[(int)$definition->id] = $priority === false ? 999 : (int)$priority;
}
if (empty($definitionPriority)) {
return [];
}
$valueRows = AnimalFieldValue::find()
->where(['animal_id' => $animalIds, 'field_definition_id' => array_keys($definitionPriority)])
->all();
$imageUrls = [];
$chosenPriorityByAnimal = [];
foreach ($valueRows as $valueRow) {
$animalId = (int)$valueRow->animal_id;
$valueText = trim((string)$valueRow->value_text);
if ($valueText === '') {
continue;
}
$priority = $definitionPriority[(int)$valueRow->field_definition_id] ?? 999;
if (!isset($chosenPriorityByAnimal[$animalId]) || $priority < $chosenPriorityByAnimal[$animalId]) {
$chosenPriorityByAnimal[$animalId] = $priority;
$imageUrls[$animalId] = $valueText;
}
}
$missingAnimalIds = [];
foreach ($animalIds as $animalId) {
if (!isset($imageUrls[$animalId])) {
$missingAnimalIds[] = (int)$animalId;
}
}
if ($allowGalleryFallback && !empty($missingAnimalIds) && Yii::$app->db->schema->getTableSchema('rescue_animal_gallery_item', true) !== null) {
$galleryItems = AnimalGalleryItem::find()
->where(['animal_id' => $missingAnimalIds])
->orderBy(['animal_id' => SORT_ASC, 'id' => SORT_DESC])
->all();
foreach ($galleryItems as $galleryItem) {
$animalId = (int)$galleryItem->animal_id;
if (isset($imageUrls[$animalId])) {
continue;
}
$url = trim((string)$galleryItem->getImageUrl());
if ($url === '') {
continue;
}
$imageUrls[$animalId] = $url;
}
}
return $imageUrls;
}
private function resolveDisplayFieldOverrides(array $animalIds, string $fieldKey): array
{
$animalIds = array_values(array_unique(array_map('intval', $animalIds)));
if (empty($animalIds)) {
return [];
}
if (!class_exists(RescueFieldDefinition::class)
|| Yii::$app->db->schema->getTableSchema('rescue_field_definition', true) === null
|| Yii::$app->db->schema->getTableSchema('rescue_animal_field_value', true) === null
) {
return [];
}
$definition = RescueFieldDefinition::findOne([
'module_id' => 'animal_management',
'group_key' => 'animal_profile',
'field_key' => $fieldKey,
]);
if (!$definition instanceof RescueFieldDefinition) {
return [];
}
$rows = AnimalFieldValue::find()
->where(['animal_id' => $animalIds, 'field_definition_id' => (int)$definition->id])
->all();
$allowed = array_keys(DisplaySettingsForm::fieldOptions());
$result = [];
foreach ($rows as $row) {
$raw = trim((string)$row->value_text);
if ($raw === '') {
continue;
}
$decoded = json_decode($raw, true);
if (!is_array($decoded)) {
$decoded = array_map('trim', explode(',', $raw));
}
$normalized = [];
foreach ($decoded as $field) {
$field = trim((string)$field);
if ($field === '' || !in_array($field, $allowed, true)) {
continue;
}
if (!in_array($field, $normalized, true)) {
$normalized[] = $field;
}
}
if (!empty($normalized)) {
$result[(int)$row->animal_id] = $normalized;
}
}
return $result;
}
}