Rapport-Laravel/app/Http/Controllers/MetriccampaignController.php

722 lines
29 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\LambdaRequest;
class MetriccampaignController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->_service = 'metric';
$this->_module = 'campaign_view';
$this->_viewname = 'metriccampaign';
$this->_title = 'Campaign management';
$this->_base_url = '/metriccampaign';
}
protected function get_list_fields()
{
return [
'actions' => ['title' => 'Actions', 'buttons' => ['edit', 'delete']],
'name' => ['title' => 'Name', 'type' => 'text'],
'roles' => ['title' => 'Roles', 'type' => 'text'],
'sort_order' => ['title' => 'Sort Order', 'type' => 'number'],
'teams_rank' => ['title' => 'Teams Rank', 'type' => 'text'],
'status' => ['title' => 'Status', 'type' => 'select'],
];
}
public function list(Request $request)
{
$response = LambdaRequest::signedrequest('get', $this->_service, $this->_module.'/get_all');
if (isset($response->statusCode) && (int) $response->statusCode === 401) {
return redirect('/unauthorised');
}
$roleMap = $this->getRoleNameMap();
$items = $response->data->items ?? [];
foreach ($items as &$item) {
$item->roles = $this->formatAssignedRoles($item->campaign_visible_roles ?? ($item->roles ?? ''), $roleMap);
}
unset($item);
return $this->view('list')->with([
'title' => $this->_title,
'fields' => $this->get_list_fields(),
'base_url' => $this->_base_url,
'items' => $items,
'buttons' => $this->get_list_buttons(),
]);
}
protected function get_form_fields($type, $values)
{
$roles = $this->getlambda('get', 'ums', 'roles', 'dropdown');
$roles_dd = [];
foreach ($roles->data->items as $role) {
$roles_dd[$role->id] = $role->name;
}
$metrics = $this->getlambda('get', 'metric', 'metric', 'get_all');
$metrics_dd = [];
foreach ($metrics->data->items as $metric) {
$metrics_dd[$metric->id] = $metric->name;
}
$intervalValues = [
'yearlycy' => 'Calender Year',
'yearlyfy' => 'Financial Year',
'monthly' => 'Monthly',
'weekly' => 'Weekly',
'daily' => 'Daily',
];
$teamsRankValues = [
'l1' => 'L1 ',
'l2' => 'L2 ',
'l3' => 'L3 ',
'l4' => 'L4 ',
];
$userAttrs = $this->getlambda('get', 'ums', 'userattribute', 'get_all');
$attrs_dd = [];
$userattr_dd = [];
$attrs_meta = [];
foreach (($userAttrs->data->items ?? []) as $attr) {
if (($attr->status ?? 'active') === 'disabled') {
continue;
}
$code = $attr->code ?? '';
if ($code === '') {
continue;
}
$options = [];
foreach (($attr->options ?? []) as $opt) {
$opt = (array) $opt;
$optValue = (string) ($opt['value'] ?? '');
if ($optValue === '') {
continue;
}
$options[$optValue] = (string) ($opt['display'] ?? $optValue);
}
$attrs_dd[$code] = ($attr->attribute ?? $code) . ' (' . $code . ')';
$userattr_dd[$code] = $attr->attribute ?? $code;
$attrs_meta[$code] = [
'code' => $code,
'attribute' => $attr->attribute ?? $code,
'type' => $attr->type ?? 'text',
'options' => $options,
];
}
return [
'name' => ['title' => 'Name', 'type' => 'text', 'required' => true, 'fullwidth' => true],
'sort_order' => [
'title' => 'Sort Order',
'type' => 'number',
'required' => false,
'placeholder' => 'Lower number shows first',
],
'validity_start' => ['title' => 'Validity Start', 'type' => 'date', 'required' => true],
'validity_end' => ['title' => 'Validity End', 'type' => 'date', 'required' => true],
'campaign_visible_roles' => ['title' => 'Campaign Visible Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'reporting_roles' => ['title' => 'Reporting Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'teams_rank' => ['title' => 'Teams Rank', 'type' => 'select', 'required' => true, 'values' => $teamsRankValues],
'intervals' => ['title' => 'Intervals', 'type' => 'multiselect', 'required' => true, 'values' => $intervalValues],
'ranking_attributes' => [
'title' => 'User Attribute Filters',
'type' => 'ranking_attributes',
'required' => false,
'fullwidth' => true,
'attributes' => $attrs_dd,
'attributes_meta' => $attrs_meta,
],
'bg_image' => ['title' => 'Background image', 'type' => 'image', 'required' => false],
'icon' => ['title' => 'Icon', 'type' => 'image', 'required' => false],
'showOverallRank' => [
'title' => 'Show Overall Rank',
'type' => 'select',
'required' => false,
'values' => ['yes' => 'Yes', 'no' => 'No'],
],
'overallRank_title' => [
'title' => 'Overall Rank Title',
'type' => 'text',
'required' => false,
'placeholder' => 'My Overall Rank',
],
'showManagerOverallRank' => [
'title' => 'Show Manager Overall Rank',
'type' => 'select',
'required' => false,
'values' => ['yes' => 'Yes', 'no' => 'No'],
],
'managerOverallRank_title' => [
'title' => 'Manager Overall Rank Title',
'type' => 'text',
'required' => false,
'placeholder' => 'View Individual Rank',
],
'overallRank_color1' => [
'title' => 'My Rank - Gradient color (dark)',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A e.g. 255-66-33-1',
],
'overallRank_color2' => [
'title' => 'My Rank - Gradient color (light)',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A e.g. 255-66-33-1',
],
'showRankInTeam' => [
'title' => 'Show Rank in Team',
'type' => 'select',
'required' => false,
'values' => ['yes' => 'Yes', 'no' => 'No'],
],
'rankInTeam_title' => [
'title' => 'Rank in Team Title',
'type' => 'text',
'required' => false,
'placeholder' => 'Rank in Team',
],
'rankInTeam_color1' => [
'title' => 'Rank in Team - Gradient color (dark)',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A e.g. 255-165-0-1',
],
'rankInTeam_color2' => [
'title' => 'Rank in Team - Gradient color (light)',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A e.g. 255-165-0-1',
],
'showTeamRank' => [
'title' => 'Show Team Rank',
'type' => 'select',
'required' => false,
'values' => ['yes' => 'Yes', 'no' => 'No'],
],
'teamRank_title' => [
'title' => 'Team Rank Title',
'type' => 'text',
'required' => false,
'placeholder' => 'Team Rank',
],
'showManagerTeamRank' => [
'title' => 'Show Manager Team Rank',
'type' => 'select',
'required' => false,
'values' => ['yes' => 'Yes', 'no' => 'No'],
],
'managerTeamRank_title' => [
'title' => 'Manager Team Rank Title',
'type' => 'text',
'required' => false,
'placeholder' => 'View Team Rank',
],
'teamRank_color1' => [
'title' => 'Team Rank - Gradient color (dark)',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A e.g. 18-196-110-1',
],
'teamRank_color2' => [
'title' => 'Team Rank - Gradient color (light)',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A e.g. 18-196-110-1',
],
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled']],
'show_positions' => ['title' => 'Show Positions', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No']],
'show_positions_type' => ['title' => 'Show Positions Type', 'type' => 'select', 'required' => true, 'values' => ['all' => 'All', 'loggedin' => 'Logged In']],
'show_units_export' => ['title' => 'Show Units Export', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_user_attributes' => ['title' => 'Show User Attributes', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'user_attributes' => ['title' => 'Include User Attributes', 'type' => 'multiselect', 'values' => $userattr_dd, 'default' => []],
'show_normal_metrics' => ['title' => 'Show Normal Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_combined_metrics' => ['title' => 'Show Combined Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'manager_details' => ['title' => 'Manager Details', 'type' => 'multiselect', 'values' => ['l1manager' => 'L1 Manager', 'l2manager' => 'L2 Manager', 'l3manager' => 'L3 Manager', 'l4manager' => 'L4 Manager'], 'default' => ['l1manager']],
'total_units_name' => ['title' => 'Total Units Name', 'type' => 'text', 'required' => false, 'default' => ''],
'highlight_top' => [
'title' => 'Highlight Top',
'type' => 'text',
'required' => false,
'placeholder' => 'e.g. 10, 20, 30',
],
'highlight_color' => [
'title' => 'Highlight Color',
'type' => 'text',
'required' => false,
'placeholder' => 'R-G-B-A or CSS color',
],
'metrics' => ['title' => 'Metrics', 'type' => 'mapping', 'required' => false, 'metrics' => $metrics_dd, 'fullwidth' => true],
'combined_metrics' => ['title' => 'Combined Metrics (if any)', 'type' => 'combined_metrics', 'fullwidth' => true],
];
}
protected function _beforeform($type, $values)
{
if ($type === 'edit' && is_object($values)) {
foreach (['campaign_visible_roles', 'reporting_roles', 'intervals'] as $k) {
if (!isset($values->{$k})) {
$values->{$k} = [];
continue;
}
if (is_string($values->{$k})) {
$values->{$k} = $values->{$k} === '' ? [] : explode('#', $values->{$k});
} elseif (!is_array($values->{$k})) {
$values->{$k} = (array) $values->{$k};
}
}
if (!isset($values->metrics) || $values->metrics === null) {
$values->metrics = [];
} elseif (!is_array($values->metrics)) {
$values->metrics = (array) $values->metrics;
}
$values->metrics = array_values(array_map(function ($metric) {
return $this->normalizeMetricMapping($metric);
}, $values->metrics));
if (!isset($values->combined_metrics) || $values->combined_metrics === '') {
$values->combined_metrics = '[]';
}
if (!empty($values->teams_rank) && is_string($values->teams_rank)) {
$values->teams_rank = strtolower(substr($values->teams_rank, 0, 2));
}
$values->ranking_attributes = $this->normalizeRankingAttributes($values->ranking_attributes ?? null);
$yesNoDefaults = [
'showOverallRank' => 'yes',
'showRankInTeam' => 'yes',
'showTeamRank' => 'yes',
'showManagerOverallRank' => 'yes',
'showManagerTeamRank' => 'yes',
'show_units_export' => 'yes',
'show_normal_metrics' => 'yes',
'show_combined_metrics' => 'yes',
];
foreach ($yesNoDefaults as $field => $default) {
if (!isset($values->{$field}) || $values->{$field} === '' || $values->{$field} === null) {
$values->{$field} = $default;
}
}
$values->show_user_attributes = (($values->show_user_attributes ?? 'no') === 'yes') ? 'yes' : 'no';
$values->user_attributes = $this->normalizeAttributeCodes($values->user_attributes ?? []);
if (!isset($values->highlight_top) || $values->highlight_top === null) {
$values->highlight_top = '';
}
if (!isset($values->highlight_color) || $values->highlight_color === null) {
$values->highlight_color = '';
}
if (!isset($values->manager_details) || !is_array($values->manager_details) || $values->manager_details === []) {
$values->manager_details = ['l1manager'];
}
if (!isset($values->total_units_name) || $values->total_units_name === null) {
$values->total_units_name = '';
}
if (!isset($values->sort_order) || $values->sort_order === null || $values->sort_order === '') {
$values->sort_order = 0;
}
} elseif (is_object($values)) {
$values->ranking_attributes = [];
$values->sort_order = 0;
$values->showOverallRank = 'yes';
$values->showRankInTeam = 'yes';
$values->showTeamRank = 'yes';
$values->showManagerOverallRank = 'yes';
$values->showManagerTeamRank = 'yes';
$values->highlight_top = '';
$values->highlight_color = '';
$values->show_units_export = 'yes';
$values->show_user_attributes = 'no';
$values->user_attributes = [];
$values->show_normal_metrics = 'yes';
$values->show_combined_metrics = 'yes';
$values->manager_details = ['l1manager'];
$values->total_units_name = '';
}
return $values;
}
protected function normalizeRankingAttributes($raw): array
{
if (is_string($raw) && $raw !== '') {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : [];
}
if (is_object($raw)) {
$raw = json_decode(json_encode($raw), true);
}
if (!is_array($raw)) {
return [];
}
$normalized = [];
foreach ($raw as $row) {
if (is_object($row)) {
$row = (array) $row;
}
if (!is_array($row)) {
continue;
}
$code = trim((string) ($row['code'] ?? ''));
$value = trim((string) ($row['value'] ?? ''));
if ($code === '' || $value === '') {
continue;
}
$normalized[] = [
'code' => $code,
'value' => $value,
];
}
return $normalized;
}
protected function normalizeMetricRules($rules)
{
if (is_string($rules)) {
$decoded = json_decode($rules, true);
$rules = is_array($decoded) ? $decoded : [];
} elseif (is_object($rules)) {
$rules = json_decode(json_encode($rules), true);
}
if (!is_array($rules)) {
return [];
}
$normalized = [];
foreach ($rules as $rule) {
if (is_object($rule)) {
$rule = json_decode(json_encode($rule), true);
}
if (!is_array($rule) || empty($rule['rule_type'])) {
continue;
}
$ranges = $rule['ranges'] ?? [];
if (is_string($ranges)) {
$decodedRanges = json_decode($ranges, true);
$ranges = is_array($decodedRanges) ? $decodedRanges : [];
} elseif (is_object($ranges)) {
$ranges = json_decode(json_encode($ranges), true);
}
$normalizedRanges = [];
if (is_array($ranges)) {
foreach ($ranges as $range) {
if (is_object($range)) {
$range = json_decode(json_encode($range), true);
}
if (!is_array($range)) {
continue;
}
$normalizedRanges[] = [
'start' => isset($range['start']) ? (float) $range['start'] : 0,
'end' => isset($range['end']) ? (float) $range['end'] : 0,
'weight' => isset($range['weight']) ? (float) $range['weight'] : 0,
];
}
}
$normalized[] = [
'rule_type' => $rule['rule_type'],
'ranges' => $normalizedRanges,
];
}
return $normalized;
}
protected function normalizeRulesInterval($value)
{
$allowed = ['overall', 'monthly', 'weekly', 'daily', 'custom'];
if (is_string($value)) {
return [
'interval' => in_array($value, $allowed, true) ? $value : 'overall',
'custom_start' => '',
'custom_end' => '',
];
}
if (is_object($value)) {
$value = json_decode(json_encode($value), true);
}
if (!is_array($value)) {
return ['interval' => 'overall', 'custom_start' => '', 'custom_end' => ''];
}
$interval = in_array(($value['interval'] ?? 'overall'), $allowed, true)
? ($value['interval'] ?? 'overall')
: 'overall';
return [
'interval' => $interval,
'custom_start' => $value['custom_start'] ?? '',
'custom_end' => $value['custom_end'] ?? '',
];
}
protected function resolveRulesIntervalFromMetric($metric, $rawRules)
{
if (!empty($metric['rules_interval'])) {
return $this->normalizeRulesInterval([
'interval' => $metric['rules_interval'],
'custom_start' => $metric['rules_custom_start'] ?? '',
'custom_end' => $metric['rules_custom_end'] ?? '',
]);
}
$first = null;
if (is_array($rawRules) && !empty($rawRules)) {
$first = $rawRules[0];
if (is_object($first)) {
$first = json_decode(json_encode($first), true);
}
}
if (is_array($first) && !empty($first['interval'])) {
return $this->normalizeRulesInterval([
'interval' => $first['interval'],
'custom_start' => $first['custom_start'] ?? '',
'custom_end' => $first['custom_end'] ?? '',
]);
}
return $this->normalizeRulesInterval('overall');
}
protected function normalizeMetricMapping($metric)
{
if (is_object($metric)) {
$metric = json_decode(json_encode($metric), true);
}
if (!is_array($metric)) {
$metric = [];
}
$rawRules = $metric['rules'] ?? [];
$rulesInterval = $this->resolveRulesIntervalFromMetric($metric, $rawRules);
$rules = $this->normalizeMetricRules($rawRules);
return (object) [
'metric' => $metric['metric'] ?? '',
'display' => $metric['display'] ?? '',
'unit_type' => in_array(($metric['unit_type'] ?? 'sum'), ['sum', 'average'], true)
? ($metric['unit_type'] ?? 'sum')
: 'sum',
'sort' => isset($metric['sort']) ? (int) $metric['sort'] : 0,
'weight' => isset($metric['weight']) && $metric['weight'] !== '' ? (float) $metric['weight'] : 1,
'label_text' => $metric['label_text'] ?? 'units',
'rules_interval' => $rulesInterval['interval'],
'rules_custom_start' => $rulesInterval['custom_start'],
'rules_custom_end' => $rulesInterval['custom_end'],
'rules' => $rules,
];
}
protected function normalizeCombinedMetricMapping($metric)
{
if (is_object($metric)) {
$metric = json_decode(json_encode($metric), true);
}
if (!is_array($metric)) {
$metric = [];
}
$rawRules = $metric['rules'] ?? [];
$rulesInterval = $this->resolveRulesIntervalFromMetric($metric, $rawRules);
$rules = $this->normalizeMetricRules($rawRules);
return [
'type' => $metric['type'] ?? '',
'title' => $metric['title'] ?? '',
'fields' => $metric['fields'] ?? [],
'unit_type' => in_array(($metric['unit_type'] ?? 'sum'), ['sum', 'average'], true)
? ($metric['unit_type'] ?? 'sum')
: 'sum',
'weight' => isset($metric['weight']) && $metric['weight'] !== '' ? (float) $metric['weight'] : 1,
'label_text' => $metric['label_text'] ?? 'units',
'rules_interval' => $rulesInterval['interval'],
'rules_custom_start' => $rulesInterval['custom_start'],
'rules_custom_end' => $rulesInterval['custom_end'],
'rules' => $rules,
];
}
protected function _beforesave($type, $request)
{
$data = $request->except('_token');
$metricIds = $request->input('metric', []);
$displayUnits = $request->input('display_unit', []);
// Metric-row sort (name=metric_sort[]) — keep separate from campaign-level sort_order
$sortOrders = $request->input('metric_sort', $request->input('sort_order', []));
$unitTypes = $request->input('unit_type', []);
$weights = $request->input('weight', []);
$labelTexts = $request->input('label_text', []);
$metricRules = $request->input('metric_rules', []);
$metricRulesIntervals = $request->input('metric_rules_interval', []);
$metricRulesCustomStart = $request->input('metric_rules_custom_start', []);
$metricRulesCustomEnd = $request->input('metric_rules_custom_end', []);
$metrics = [];
if (is_array($metricIds)) {
foreach ($metricIds as $i => $metricId) {
if ($metricId === null || $metricId === '') {
continue;
}
$rules = [];
if (isset($metricRules[$i]) && $metricRules[$i] !== '') {
$rules = $this->normalizeMetricRules($metricRules[$i]);
}
$rulesInterval = $this->normalizeRulesInterval([
'interval' => $metricRulesIntervals[$i] ?? 'overall',
'custom_start' => $metricRulesCustomStart[$i] ?? '',
'custom_end' => $metricRulesCustomEnd[$i] ?? '',
]);
$metrics[] = [
'metric' => $metricId,
'display' => $displayUnits[$i] ?? '',
'unit_type' => in_array($unitTypes[$i] ?? 'sum', ['sum', 'average'], true)
? ($unitTypes[$i] ?? 'sum')
: 'sum',
'sort' => isset($sortOrders[$i]) && $sortOrders[$i] !== '' ? (int) $sortOrders[$i] : 0,
'weight' => isset($weights[$i]) && $weights[$i] !== '' ? (float) $weights[$i] : 1,
'label_text' => isset($labelTexts[$i]) && $labelTexts[$i] !== '' ? $labelTexts[$i] : 'units',
'rules_interval' => $rulesInterval['interval'],
'rules_custom_start' => $rulesInterval['custom_start'],
'rules_custom_end' => $rulesInterval['custom_end'],
'rules' => $rules,
];
}
}
$data['metrics'] = $metrics;
$combined_metrics = [];
if (isset($data['combined_metrics']) && is_array($data['combined_metrics']) && !empty($data['combined_metrics']['type'])) {
foreach ($data['combined_metrics']['type'] as $i => $v) {
if ($v === null || $v === '') {
continue;
}
$rules = [];
if (isset($data['combined_metrics']['rules'][$i]) && $data['combined_metrics']['rules'][$i] !== '') {
$rules = $this->normalizeMetricRules($data['combined_metrics']['rules'][$i]);
}
$combined_metrics[] = $this->normalizeCombinedMetricMapping([
'type' => $v,
'title' => $data['combined_metrics']['title'][$i] ?? '',
'fields' => $data['combined_metrics']['fields'][$i] ?? [],
'unit_type' => $data['combined_metrics']['unit_type'][$i] ?? 'sum',
'weight' => $data['combined_metrics']['weight'][$i] ?? 1,
'label_text' => $data['combined_metrics']['label_text'][$i] ?? 'units',
'rules_interval' => $data['combined_metrics']['rules_interval'][$i] ?? 'overall',
'rules_custom_start' => $data['combined_metrics']['rules_custom_start'][$i] ?? '',
'rules_custom_end' => $data['combined_metrics']['rules_custom_end'][$i] ?? '',
'rules' => $rules,
]);
}
}
$data['combined_metrics'] = json_encode($combined_metrics);
if ($request->file('bg_image')) {
$data['bg_image'] = $this->_uploadMedia($request, 'bg_image', 'metric');
}
if ($request->file('icon')) {
$data['icon'] = $this->_uploadMedia($request, 'icon', 'metric');
}
$rankingCodes = $request->input('ranking_attribute_code', []);
$rankingValues = $request->input('ranking_attribute_value', []);
$rankingAttributes = [];
if (is_array($rankingCodes)) {
foreach ($rankingCodes as $i => $code) {
$code = trim((string) $code);
$value = isset($rankingValues[$i]) ? trim((string) $rankingValues[$i]) : '';
if ($code === '' || $value === '') {
continue;
}
$rankingAttributes[] = [
'code' => $code,
'value' => $value,
];
}
}
$data['ranking_attributes'] = $rankingAttributes;
$data['sort_order'] = isset($data['sort_order']) && $data['sort_order'] !== '' && !is_array($data['sort_order'])
? (int) $data['sort_order']
: 0;
if (!isset($data['manager_details']) || !is_array($data['manager_details']) || $data['manager_details'] === []) {
$data['manager_details'] = ['l1manager'];
}
foreach (['show_units_export', 'show_normal_metrics', 'show_combined_metrics'] as $flag) {
if (!isset($data[$flag]) || $data[$flag] === '') {
$data[$flag] = 'yes';
}
}
$data['total_units_name'] = trim((string) ($data['total_units_name'] ?? ''));
$data['show_user_attributes'] = ($data['show_user_attributes'] ?? 'no') === 'yes' ? 'yes' : 'no';
$data['user_attributes'] = $this->normalizeAttributeCodes($data['user_attributes'] ?? []);
unset(
$data['metric'],
$data['display_unit'],
$data['metric_sort'],
$data['unit_type'],
$data['weight'],
$data['label_text'],
$data['metric_rules'],
$data['metric_rules_interval'],
$data['metric_rules_custom_start'],
$data['metric_rules_custom_end'],
$data['list_view'],
$data['ranking_attribute_code'],
$data['ranking_attribute_value']
);
return $data;
}
protected function normalizeAttributeCodes($raw): array
{
if (is_string($raw) && $raw !== '') {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : explode('#', $raw);
}
if (!is_array($raw)) {
return [];
}
$codes = [];
foreach ($raw as $code) {
if (is_object($code)) {
$code = $code->code ?? '';
}
$code = trim((string) $code);
if ($code !== '') {
$codes[] = $code;
}
}
return array_values(array_unique($codes));
}
}