value) and unresolved (list of tokens not resolved / not allowed) * Policy determines whether invalid tokens throw (fail) or are collected (blank|keep). * * @return array{values:array,unresolved:array} */ public function resolve(array $tokens, DocumentTemplate $template, Contract $contract, User $user, string $policy = 'fail'): array { $values = []; $unresolved = []; // Retrieve whitelist from DB settings (if present) and merge with config baseline (config acts as baseline; DB can add or override entity arrays) $settingsWhitelist = app(\App\Services\Documents\DocumentSettings::class)->get()->whitelist ?? []; $configWhitelist = config('documents.whitelist', []); // Merge preserving DB additions/overrides $globalWhitelist = array_replace($configWhitelist, $settingsWhitelist); $templateEntities = $template->entities ?: array_keys($globalWhitelist); foreach ($tokens as $token) { [$entity,$attr] = explode('.', $token, 2); if ($entity === 'generation') { $values[$token] = $this->generationAttribute($attr, $user); continue; } if (! in_array($entity, $templateEntities, true)) { if ($policy === 'fail') { throw new \RuntimeException("Nedovoljen entiteta token: $entity"); } $unresolved[] = $token; continue; } $allowed = ($template->columns[$entity] ?? []) ?: ($globalWhitelist[$entity] ?? []); if (! in_array($attr, $allowed, true)) { if ($policy === 'fail') { throw new \RuntimeException("Nedovoljen stolpec token: $token"); } $unresolved[] = $token; continue; } $values[$token] = $this->entityAttribute($entity, $attr, $contract) ?? ''; } return ['values' => $values, 'unresolved' => array_values(array_unique($unresolved))]; } private function generationAttribute(string $attr, User $user): string { return match ($attr) { 'timestamp' => (string) now()->timestamp, 'date' => now()->toDateString(), // raw ISO; formatting applied later 'user_name' => $user->name ?? 'Uporabnik', default => '' }; } private function entityAttribute(string $entity, string $attr, Contract $contract): ?string { switch ($entity) { case 'contract': return (string) ($contract->{$attr} ?? ''); case 'client_case': return (string) optional($contract->clientCase)->{$attr}; case 'client': return (string) optional(optional($contract->clientCase)->client)->{$attr}; case 'person': $person = optional(optional($contract->clientCase)->person); return (string) $person->{$attr}; case 'account': $account = optional($contract->account); return (string) $account->{$attr}; default: return ''; } } }