2026-02-23 12:58:19 +01:00
|
|
|
<?php
|
|
|
|
|
|
2026-03-18 22:20:54 +01:00
|
|
|
namespace MintyPHP\Module\AddressBook\Service;
|
2026-02-23 12:58:19 +01:00
|
|
|
|
2026-03-13 11:31:33 +01:00
|
|
|
use MintyPHP\Repository\CustomField\TenantCustomFieldOptionRepository;
|
|
|
|
|
use MintyPHP\Service\Access\RoleService;
|
|
|
|
|
use MintyPHP\Service\CustomField\UserCustomFieldValueService;
|
|
|
|
|
use MintyPHP\Service\Org\DepartmentService;
|
|
|
|
|
use MintyPHP\Service\Tenant\TenantScopeService;
|
|
|
|
|
use MintyPHP\Service\Tenant\TenantService;
|
2026-02-23 12:58:19 +01:00
|
|
|
use MintyPHP\Service\User\UserAccountService;
|
|
|
|
|
use MintyPHP\Service\User\UserAssignmentService;
|
2026-03-13 11:31:33 +01:00
|
|
|
use MintyPHP\Service\User\UserAvatarService;
|
2026-02-23 12:58:19 +01:00
|
|
|
|
|
|
|
|
class AddressBookService
|
|
|
|
|
{
|
|
|
|
|
public function __construct(
|
|
|
|
|
private readonly UserAccountService $userAccountService,
|
|
|
|
|
private readonly UserAssignmentService $userAssignmentService,
|
2026-03-13 11:31:33 +01:00
|
|
|
private readonly TenantService $tenantService,
|
|
|
|
|
private readonly DepartmentService $departmentService,
|
|
|
|
|
private readonly RoleService $roleService,
|
|
|
|
|
private readonly TenantScopeService $scopeGateway,
|
|
|
|
|
private readonly UserCustomFieldValueService $customFieldValueService,
|
|
|
|
|
private readonly UserAvatarService $avatarService
|
2026-02-23 12:58:19 +01:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function buildIndexContext(int $currentUserId, array $query): array
|
|
|
|
|
{
|
|
|
|
|
$activeTenants = $this->splitCommaValues($query['tenants'] ?? '');
|
|
|
|
|
$activeRoles = $this->splitCommaValues($query['roles'] ?? '');
|
|
|
|
|
$activeDepartments = $this->splitCommaValues($query['departments'] ?? '');
|
|
|
|
|
|
2026-03-13 11:31:33 +01:00
|
|
|
$tenantIds = $this->normalizeIds($this->scopeGateway->getUserTenantIds($currentUserId));
|
|
|
|
|
$tenants = $this->tenantService->list();
|
2026-02-23 12:58:19 +01:00
|
|
|
if ($tenantIds) {
|
|
|
|
|
$allowedTenantMap = array_fill_keys($tenantIds, true);
|
|
|
|
|
$tenants = array_values(array_filter(
|
|
|
|
|
$tenants,
|
|
|
|
|
static function (array $tenant) use ($allowedTenantMap): bool {
|
|
|
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
|
|
|
return $tenantId > 0 && isset($allowedTenantMap[$tenantId]);
|
|
|
|
|
}
|
|
|
|
|
));
|
2026-03-13 11:31:33 +01:00
|
|
|
} elseif ($this->scopeGateway->isStrict()) {
|
2026-02-23 12:58:19 +01:00
|
|
|
$tenants = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tenantItems = array_map(
|
|
|
|
|
static fn (array $tenant): array => [
|
|
|
|
|
'id' => (string) ($tenant['uuid'] ?? ''),
|
|
|
|
|
'description' => (string) ($tenant['description'] ?? ''),
|
|
|
|
|
],
|
|
|
|
|
$tenants
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$departments = $tenantIds
|
2026-03-13 11:31:33 +01:00
|
|
|
? $this->departmentService->listByTenantIds($tenantIds)
|
|
|
|
|
: ($this->scopeGateway->isStrict() ? [] : $this->departmentService->list());
|
2026-02-23 12:58:19 +01:00
|
|
|
|
2026-03-13 11:31:33 +01:00
|
|
|
$roles = $this->roleService->listActive();
|
2026-02-23 12:58:19 +01:00
|
|
|
|
2026-03-18 22:20:54 +01:00
|
|
|
$customFieldFilterSpec = $this->customFieldValueService->extractCustomFieldFilterSpec($query, $currentUserId);
|
2026-02-23 12:58:19 +01:00
|
|
|
$customFieldFilterDefinitions = $this->normalizeDefinitions(
|
2026-03-18 22:20:54 +01:00
|
|
|
$customFieldFilterSpec['definitions']
|
2026-02-23 12:58:19 +01:00
|
|
|
);
|
2026-03-18 22:20:54 +01:00
|
|
|
$customFieldFilterQuery = $customFieldFilterSpec['query'];
|
2026-02-23 12:58:19 +01:00
|
|
|
|
|
|
|
|
$customFieldDefinitionIds = [];
|
|
|
|
|
foreach ($customFieldFilterDefinitions as $definition) {
|
|
|
|
|
$definitionId = (int) ($definition['id'] ?? 0);
|
|
|
|
|
if ($definitionId > 0) {
|
|
|
|
|
$customFieldDefinitionIds[] = $definitionId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$customFieldDefinitionIds = array_values(array_unique($customFieldDefinitionIds));
|
|
|
|
|
|
2026-03-13 11:31:33 +01:00
|
|
|
$customFieldOptions = TenantCustomFieldOptionRepository::listByDefinitionIds(
|
2026-02-23 12:58:19 +01:00
|
|
|
$customFieldDefinitionIds,
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$customFieldOptionsByDefinition = [];
|
|
|
|
|
foreach ($customFieldOptions as $option) {
|
|
|
|
|
$definitionId = (int) ($option['definition_id'] ?? 0);
|
|
|
|
|
if ($definitionId <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$customFieldOptionsByDefinition[$definitionId] ??= [];
|
|
|
|
|
$customFieldOptionsByDefinition[$definitionId][] = [
|
|
|
|
|
'id' => (string) ((int) ($option['id'] ?? 0)),
|
|
|
|
|
'description' => (string) ($option['label'] ?? ''),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tenantLabelById = [];
|
|
|
|
|
foreach ($tenants as $tenant) {
|
|
|
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
|
|
|
if ($tenantId <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$tenantLabelById[$tenantId] = (string) ($tenant['description'] ?? '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($customFieldFilterDefinitions as &$definition) {
|
|
|
|
|
$definitionId = (int) ($definition['id'] ?? 0);
|
|
|
|
|
$tenantId = (int) ($definition['tenant_id'] ?? 0);
|
|
|
|
|
$definition['options'] = $customFieldOptionsByDefinition[$definitionId] ?? [];
|
|
|
|
|
$definition['tenant_label'] = $tenantLabelById[$tenantId] ?? '';
|
|
|
|
|
}
|
|
|
|
|
unset($definition);
|
|
|
|
|
|
|
|
|
|
$tenantDepartmentMap = [];
|
2026-03-04 15:56:58 +01:00
|
|
|
$departmentMapByTenantId = [];
|
|
|
|
|
foreach ($departments as $department) {
|
|
|
|
|
$departmentId = (int) ($department['id'] ?? 0);
|
|
|
|
|
$departmentTenantId = (int) ($department['tenant_id'] ?? 0);
|
|
|
|
|
if ($departmentId <= 0 || $departmentTenantId <= 0) {
|
|
|
|
|
continue;
|
2026-02-23 12:58:19 +01:00
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
$departmentMapByTenantId[$departmentTenantId] ??= [];
|
|
|
|
|
$departmentMapByTenantId[$departmentTenantId][] = $departmentId;
|
|
|
|
|
}
|
2026-02-23 12:58:19 +01:00
|
|
|
|
2026-03-04 15:56:58 +01:00
|
|
|
foreach ($tenants as $tenant) {
|
|
|
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
|
|
|
$tenantUuid = (string) ($tenant['uuid'] ?? '');
|
|
|
|
|
if ($tenantId <= 0 || $tenantUuid === '') {
|
|
|
|
|
continue;
|
2026-02-23 12:58:19 +01:00
|
|
|
}
|
2026-03-04 15:56:58 +01:00
|
|
|
$tenantDepartmentMap[$tenantUuid] = array_map(
|
|
|
|
|
'strval',
|
|
|
|
|
$departmentMapByTenantId[$tenantId] ?? []
|
|
|
|
|
);
|
2026-02-23 12:58:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'activeTenants' => $activeTenants,
|
|
|
|
|
'activeRoles' => $activeRoles,
|
|
|
|
|
'activeDepartments' => $activeDepartments,
|
|
|
|
|
'tenantItems' => $tenantItems,
|
|
|
|
|
'departments' => $departments,
|
|
|
|
|
'roles' => $roles,
|
|
|
|
|
'customFieldFilterDefinitions' => $customFieldFilterDefinitions,
|
|
|
|
|
'customFieldFilterQuery' => $customFieldFilterQuery,
|
|
|
|
|
'tenantDepartmentMap' => $tenantDepartmentMap,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function buildGridResult(int $currentUserId, array $query): array
|
|
|
|
|
{
|
|
|
|
|
$limit = (int) ($query['limit'] ?? 10);
|
|
|
|
|
$offset = (int) ($query['offset'] ?? 0);
|
|
|
|
|
$search = trim((string) ($query['search'] ?? ''));
|
|
|
|
|
$order = (string) ($query['order'] ?? 'last_name');
|
|
|
|
|
$dir = (string) ($query['dir'] ?? 'asc');
|
|
|
|
|
$tenant = trim((string) ($query['tenant'] ?? ''));
|
|
|
|
|
$tenants = $query['tenants'] ?? '';
|
|
|
|
|
$departments = $query['departments'] ?? '';
|
|
|
|
|
$roles = $query['roles'] ?? '';
|
2026-03-18 22:20:54 +01:00
|
|
|
$customFieldFilterSpec = $this->customFieldValueService->extractCustomFieldFilterSpec($query, $currentUserId);
|
2026-02-23 12:58:19 +01:00
|
|
|
|
|
|
|
|
$result = $this->userAccountService->listPaged([
|
|
|
|
|
'limit' => $limit,
|
|
|
|
|
'offset' => $offset,
|
|
|
|
|
'search' => $search,
|
|
|
|
|
'order' => $order,
|
|
|
|
|
'dir' => $dir,
|
|
|
|
|
'tenant' => $tenant,
|
|
|
|
|
'tenants' => $tenants,
|
|
|
|
|
'departments' => $departments,
|
|
|
|
|
'roles' => $roles,
|
|
|
|
|
'customFieldFilterSpec' => $customFieldFilterSpec,
|
|
|
|
|
'tenantUserId' => $currentUserId,
|
|
|
|
|
'active' => 'active',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$rows = [];
|
|
|
|
|
foreach (($result['rows'] ?? []) as $row) {
|
|
|
|
|
$tenantList = $this->normalizeLabelList($row['tenant_labels'] ?? []);
|
|
|
|
|
$departmentList = $this->normalizeLabelList($row['department_labels'] ?? []);
|
|
|
|
|
$roleList = $this->normalizeLabelList($row['role_labels'] ?? []);
|
|
|
|
|
|
|
|
|
|
$uuid = (string) ($row['uuid'] ?? '');
|
|
|
|
|
$displayName = trim((string) ($row['display_name'] ?? ''));
|
|
|
|
|
if ($displayName === '') {
|
|
|
|
|
$displayName = trim((string) ($row['first_name'] ?? '') . ' ' . (string) ($row['last_name'] ?? ''));
|
|
|
|
|
}
|
|
|
|
|
if ($displayName === '') {
|
|
|
|
|
$displayName = (string) ($row['email'] ?? '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rows[] = [
|
|
|
|
|
'uuid' => $uuid,
|
|
|
|
|
'display_name' => $displayName,
|
|
|
|
|
'first_name' => (string) ($row['first_name'] ?? ''),
|
|
|
|
|
'last_name' => (string) ($row['last_name'] ?? ''),
|
|
|
|
|
'email' => (string) ($row['email'] ?? ''),
|
|
|
|
|
'phone' => (string) ($row['phone'] ?? ''),
|
|
|
|
|
'mobile' => (string) ($row['mobile'] ?? ''),
|
|
|
|
|
'short_dial' => (string) ($row['short_dial'] ?? ''),
|
|
|
|
|
'tenants' => $tenantList,
|
|
|
|
|
'departments' => $departmentList,
|
|
|
|
|
'roles' => $roleList,
|
2026-03-13 11:31:33 +01:00
|
|
|
'has_avatar' => $uuid !== '' && $this->avatarService->hasAvatar($uuid),
|
2026-02-23 12:58:19 +01:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'data' => $rows,
|
|
|
|
|
'total' => (int) ($result['total'] ?? 0),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function buildViewContext(int $currentUserId, string $uuid): array
|
|
|
|
|
{
|
|
|
|
|
$uuid = trim($uuid);
|
|
|
|
|
if ($uuid === '') {
|
|
|
|
|
return ['status' => 'invalid_uuid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $this->userAccountService->findByUuid($uuid);
|
|
|
|
|
if (!$user || !isset($user['id'])) {
|
|
|
|
|
return ['status' => 'not_found'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$targetUserId = (int) $user['id'];
|
|
|
|
|
if ($targetUserId <= 0) {
|
|
|
|
|
return ['status' => 'not_found'];
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 00:44:52 +01:00
|
|
|
// Users can always view their own profile; others require scope access.
|
2026-02-23 12:58:19 +01:00
|
|
|
if (
|
|
|
|
|
$currentUserId !== $targetUserId
|
2026-03-13 11:31:33 +01:00
|
|
|
&& !$this->scopeGateway->canAccess('users', $targetUserId, $currentUserId)
|
2026-02-23 12:58:19 +01:00
|
|
|
) {
|
|
|
|
|
return ['status' => 'forbidden'];
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 11:31:33 +01:00
|
|
|
$viewerTenantIds = $this->normalizeIds($this->scopeGateway->getUserTenantIds($currentUserId));
|
2026-02-23 12:58:19 +01:00
|
|
|
$assignments = $this->userAssignmentService->buildAssignmentsForUser($targetUserId);
|
|
|
|
|
|
|
|
|
|
$departmentMap = [];
|
|
|
|
|
foreach (($assignments['departments'] ?? []) as $department) {
|
|
|
|
|
if (empty($department['active'])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$tenantId = (int) ($department['tenant_id'] ?? 0);
|
|
|
|
|
$label = trim((string) ($department['description'] ?? ''));
|
2026-03-06 00:44:52 +01:00
|
|
|
// Only show departments in tenants the viewer also belongs to — prevents leaking org structure.
|
2026-02-23 12:58:19 +01:00
|
|
|
if ($tenantId <= 0 || $label === '' || !in_array($tenantId, $viewerTenantIds, true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$departmentMap[$tenantId] ??= [];
|
|
|
|
|
$departmentMap[$tenantId][] = $label;
|
|
|
|
|
}
|
|
|
|
|
foreach ($departmentMap as $tenantId => $labels) {
|
|
|
|
|
$labels = array_values(array_unique($labels));
|
|
|
|
|
natcasesort($labels);
|
|
|
|
|
$departmentMap[$tenantId] = array_values($labels);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$primaryTenantId = (int) ($user['primary_tenant_id'] ?? 0);
|
|
|
|
|
$tenantGroups = [];
|
|
|
|
|
foreach (($assignments['tenants'] ?? []) as $tenant) {
|
|
|
|
|
$tenantId = (int) ($tenant['id'] ?? 0);
|
|
|
|
|
$tenantLabel = trim((string) ($tenant['description'] ?? ''));
|
|
|
|
|
$status = strtolower(trim((string) ($tenant['status'] ?? '')));
|
|
|
|
|
if (
|
|
|
|
|
$tenantId <= 0
|
|
|
|
|
|| $tenantLabel === ''
|
|
|
|
|
|| $status !== 'active'
|
|
|
|
|
|| !in_array($tenantId, $viewerTenantIds, true)
|
|
|
|
|
) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$tenantGroups[] = [
|
|
|
|
|
'label' => $tenantLabel,
|
|
|
|
|
'is_primary' => $primaryTenantId > 0 && $tenantId === $primaryTenantId,
|
|
|
|
|
'departments' => $departmentMap[$tenantId] ?? [],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usort($tenantGroups, static fn (array $a, array $b): int => strnatcasecmp(
|
|
|
|
|
(string) $a['label'],
|
|
|
|
|
(string) $b['label']
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$roleLabels = [];
|
|
|
|
|
foreach (($assignments['roles'] ?? []) as $role) {
|
|
|
|
|
if (empty($role['active'])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$label = trim((string) ($role['description'] ?? ''));
|
|
|
|
|
if ($label !== '') {
|
|
|
|
|
$roleLabels[] = $label;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$roleLabels = array_values(array_unique($roleLabels));
|
|
|
|
|
natcasesort($roleLabels);
|
|
|
|
|
$roleLabels = array_values($roleLabels);
|
|
|
|
|
|
|
|
|
|
$avatarUuid = (string) ($user['uuid'] ?? '');
|
2026-03-13 11:31:33 +01:00
|
|
|
$user['has_avatar'] = $avatarUuid !== '' && $this->avatarService->hasAvatar($avatarUuid);
|
2026-02-23 12:58:19 +01:00
|
|
|
$user['tenant_groups'] = $tenantGroups;
|
|
|
|
|
$user['role_labels'] = $roleLabels;
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'status' => 'ok',
|
|
|
|
|
'user' => $user,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 12:24:06 +01:00
|
|
|
/**
|
|
|
|
|
* Transform custom field filter definitions into chip metadata for the grid UI.
|
|
|
|
|
*
|
|
|
|
|
* @param list<array<string, mixed>> $definitions
|
|
|
|
|
* @return list<array<string, mixed>>
|
|
|
|
|
*/
|
|
|
|
|
public function buildCustomFieldChipMeta(array $definitions): array
|
|
|
|
|
{
|
|
|
|
|
$chips = [];
|
|
|
|
|
foreach ($definitions as $definition) {
|
|
|
|
|
$uuid = strtolower(trim((string) ($definition['uuid'] ?? '')));
|
|
|
|
|
$type = strtolower(trim((string) ($definition['type'] ?? '')));
|
|
|
|
|
$label = trim((string) ($definition['label'] ?? ''));
|
|
|
|
|
$options = is_array($definition['options'] ?? null) ? $definition['options'] : [];
|
|
|
|
|
if ($uuid === '' || $type === '' || $label === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (in_array($type, ['select', 'boolean'], true)) {
|
|
|
|
|
$optionMap = gridOptionMapFromItems($options);
|
|
|
|
|
if ($type === 'boolean') {
|
|
|
|
|
$optionMap = ['1' => t('Yes'), '0' => t('No')];
|
|
|
|
|
}
|
|
|
|
|
$chips[] = [
|
|
|
|
|
'type' => 'scalar',
|
|
|
|
|
'param' => 'cf_' . $uuid,
|
|
|
|
|
'label' => $label,
|
|
|
|
|
'options' => $optionMap,
|
|
|
|
|
];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($type === 'multiselect') {
|
|
|
|
|
$chips[] = [
|
|
|
|
|
'type' => 'multi_csv',
|
|
|
|
|
'param' => 'cfm_' . $uuid,
|
|
|
|
|
'label' => $label,
|
|
|
|
|
'options' => gridOptionMapFromItems($options),
|
|
|
|
|
];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($type === 'date') {
|
|
|
|
|
$chips[] = [
|
|
|
|
|
'type' => 'date_range',
|
|
|
|
|
'label' => $label,
|
|
|
|
|
'from_param' => 'cfd_' . $uuid . '_from',
|
|
|
|
|
'to_param' => 'cfd_' . $uuid . '_to',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $chips;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-23 12:58:19 +01:00
|
|
|
private function splitCommaValues($value): array
|
|
|
|
|
{
|
|
|
|
|
return array_values(array_filter(
|
|
|
|
|
array_map('trim', explode(',', (string) $value)),
|
|
|
|
|
static fn (string $item): bool => $item !== ''
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function normalizeDefinitions($definitions): array
|
|
|
|
|
{
|
|
|
|
|
if (!is_array($definitions)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
return array_values(array_filter(
|
|
|
|
|
$definitions,
|
|
|
|
|
static fn ($definition): bool => is_array($definition)
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function normalizeIds(array $values): array
|
|
|
|
|
{
|
|
|
|
|
$ids = array_values(array_unique(array_map('intval', $values)));
|
|
|
|
|
return array_values(array_filter($ids, static fn (int $id): bool => $id > 0));
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 00:44:52 +01:00
|
|
|
// Accepts either a '||'-delimited DB aggregate string (from GROUP_CONCAT) or a plain array.
|
2026-02-23 12:58:19 +01:00
|
|
|
private function normalizeLabelList($value): array
|
|
|
|
|
{
|
|
|
|
|
if (is_string($value)) {
|
|
|
|
|
if ($value === '') {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
$items = explode('||', $value);
|
|
|
|
|
} elseif (is_array($value)) {
|
|
|
|
|
$items = $value;
|
|
|
|
|
} else {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array_values(array_filter(array_map(
|
|
|
|
|
static fn ($item): string => trim((string) $item),
|
|
|
|
|
$items
|
|
|
|
|
), static fn (string $item): bool => $item !== ''));
|
|
|
|
|
}
|
|
|
|
|
}
|