orderByRaw("(ui->>'order')::int nulls last") ->get(); // Fallback: if no entities are seeded yet, return a sensible default set if ($entities->isEmpty()) { $entities = collect([ [ 'key' => 'person', 'canonical_root' => 'person', 'label' => 'Person', 'fields' => ['first_name', 'last_name', 'full_name', 'gender', 'birthday', 'tax_number', 'social_security_number', 'description'], 'ui' => ['order' => 1], ], [ 'key' => 'person_addresses', 'canonical_root' => 'address', 'label' => 'Person Addresses', 'fields' => ['address', 'country', 'type_id', 'description'], 'ui' => ['order' => 2], ], [ 'key' => 'person_phones', 'canonical_root' => 'phone', 'label' => 'Person Phones', 'fields' => ['nu', 'country_code', 'type_id', 'description'], 'ui' => ['order' => 3], ], [ 'key' => 'emails', 'canonical_root' => 'email', 'label' => 'Emails', 'fields' => ['value', 'is_primary', 'label'], 'ui' => ['order' => 4], ], [ 'key' => 'contracts', 'canonical_root' => 'contract', 'label' => 'Contracts', 'fields' => ['reference', 'start_date', 'end_date', 'description', 'type_id', 'client_case_id'], 'ui' => ['order' => 5], ], [ 'key' => 'accounts', 'canonical_root' => 'account', 'label' => 'Accounts', 'fields' => ['reference', 'balance_amount', 'contract_id', 'contract_reference', 'type_id', 'active', 'description'], 'ui' => ['order' => 6], ], ]); } else { // Ensure fields are arrays for frontend consumption $entities = $entities->map(function ($ent) { $ent->fields = is_array($ent->fields) ? $ent->fields : []; return $ent; }); } return response()->json(['entities' => $entities]); } public function suggest(Request $request) { $cols = $request->input('columns', []); if (! is_array($cols)) { $cols = []; } // Optional filter: only suggest for specific entity keys (e.g., template meta.entities) $only = $request->input('only_entities'); $query = ImportEntity::query()->orderByRaw("(ui->>'order')::int nulls last"); if (is_array($only) && ! empty($only)) { $query->whereIn('key', array_values(array_filter($only, fn ($v) => is_string($v) && $v !== ''))); } $entities = $query->get(); $suggestions = []; foreach ($cols as $col) { $s = $this->suggestFor($col, $entities); if ($s) { $suggestions[$col] = $s; } } return response()->json(['suggestions' => $suggestions]); } private function suggestFor(string $source, $entities): ?array { $s = trim(mb_strtolower($source)); $best = null; $bestRank = PHP_INT_MAX; foreach ($entities as $ent) { $rules = (array) ($ent->rules ?? []); foreach ($rules as $rule) { $pattern = $rule['pattern'] ?? null; $field = $rule['field'] ?? null; if (! $pattern || ! $field) { continue; } if (@preg_match($pattern, $s)) { // Rank preferences: payments over address for amount/date-like terms $rank = (int) ($ent->ui['order'] ?? 999); $isPayment = ($ent->canonical_root ?? null) === 'payment'; $isAddress = ($ent->canonical_root ?? null) === 'address'; if ($isPayment) { $rank -= 10; // boost payments } elseif ($isAddress && in_array($field, ['amount', 'payment_date', 'payment_nu', 'reference'])) { $rank += 10; // demote addresses for these } if ($rank < $bestRank) { $bestRank = $rank; $best = [ 'entity' => $ent->key, 'field' => $field, 'canonical_root' => $ent->canonical_root, ]; } } } } return $best; } }