Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
97.83% |
45 / 46 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
| RelationMmFieldResolver | |
100.00% |
45 / 45 |
|
100.00% |
4 / 4 |
31 | |
100.00% |
1 / 1 |
| resolveForeignKeyColumn | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
8 | |||
| resolveVisibleRelationFields | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
8 | |||
| filterConfiguredFields | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
| filterPrimaryModuleFields | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
11 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | /** @license For full copyright and license information, please see the LICENSE.md file. */ |
| 6 | |
| 7 | namespace App\Core\Engine\Application\Service\Relation; |
| 8 | |
| 9 | defined('AMMONLY_APP') || exit('Direct script access is forbidden.'); |
| 10 | |
| 11 | use App\Core\Engine\Domain\Model\FieldMetadata; |
| 12 | use App\Core\Engine\Domain\Model\ModuleMetadata; |
| 13 | use App\Core\Engine\Domain\Model\RelationMmMetadata; |
| 14 | |
| 15 | /** |
| 16 | * Relation M:M Field Resolver. |
| 17 | * |
| 18 | * Resolves foreign key columns and determines visible columns for relation DataGrid tabs and record pickers. |
| 19 | * |
| 20 | * @package App\Core\Engine\Application\Service\Relation |
| 21 | */ |
| 22 | final readonly class RelationMmFieldResolver |
| 23 | { |
| 24 | /** |
| 25 | * Resolves foreign key column name based on module name. |
| 26 | * |
| 27 | * @param string $moduleName Machine module name (e.g. 'contacts'). |
| 28 | * @return string Column name (e.g. 'contact_id'). |
| 29 | */ |
| 30 | public function resolveForeignKeyColumn(string $moduleName): string |
| 31 | { |
| 32 | return match ($moduleName) { |
| 33 | 'companies' => 'company_id', |
| 34 | 'contacts' => 'contact_id', |
| 35 | 'partners' => 'partner_id', |
| 36 | 'tickets' => 'ticket_id', |
| 37 | 'contracts' => 'contract_id', |
| 38 | 'opportunities' => 'opportunity_id', |
| 39 | default => rtrim($moduleName, 's') . '_id', |
| 40 | }; |
| 41 | } |
| 42 | |
| 43 | /** |
| 44 | * Resolves visible fields for relation tab and picker DataGrid views. |
| 45 | * |
| 46 | * @param ModuleMetadata $targetModule Target module metadata. |
| 47 | * @param array<FieldMetadata> $fields All module fields. |
| 48 | * @param RelationMmMetadata|null $relation Optional M:M relation configuration. |
| 49 | * @return array<FieldMetadata> Filtered prioritized visible fields. |
| 50 | */ |
| 51 | public function resolveVisibleRelationFields( |
| 52 | ModuleMetadata $targetModule, |
| 53 | array $fields, |
| 54 | ?RelationMmMetadata $relation = null, |
| 55 | ): array { |
| 56 | if ($relation !== null && $relation->visibleFields !== []) { |
| 57 | $configured = $this->filterConfiguredFields($fields, $relation->visibleFields); |
| 58 | if ($configured !== []) { |
| 59 | return $configured; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | $primary = $this->filterPrimaryModuleFields($targetModule->name, $fields); |
| 64 | if ($primary !== []) { |
| 65 | return $primary; |
| 66 | } |
| 67 | |
| 68 | $visible = array_values(array_filter( |
| 69 | $fields, |
| 70 | static fn (FieldMetadata $f): bool => !$f->isSystem && ($f->isFilterable || $f->isSortable) |
| 71 | )); |
| 72 | |
| 73 | return $visible !== [] ? array_slice($visible, 0, 6) : array_slice($fields, 0, 6); |
| 74 | } |
| 75 | |
| 76 | /** |
| 77 | * Filters fields based on relation configured visible field keys. |
| 78 | * |
| 79 | * @param array<FieldMetadata> $fields Field metadata list. |
| 80 | * @param array<string> $visibleKeys Configured field keys. |
| 81 | * @return array<FieldMetadata> Matching field metadata list. |
| 82 | */ |
| 83 | public function filterConfiguredFields(array $fields, array $visibleKeys): array |
| 84 | { |
| 85 | $fieldMap = []; |
| 86 | foreach ($fields as $f) { |
| 87 | $fieldMap[$f->fieldKey] = $f; |
| 88 | } |
| 89 | $configured = []; |
| 90 | foreach ($visibleKeys as $key) { |
| 91 | if (isset($fieldMap[$key])) { |
| 92 | $configured[] = $fieldMap[$key]; |
| 93 | } |
| 94 | } |
| 95 | return $configured; |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * Filters primary domain columns for standard modules. |
| 100 | * |
| 101 | * @param string $moduleName Module machine name. |
| 102 | * @param array<FieldMetadata> $fields All module fields. |
| 103 | * @return array<FieldMetadata> Prioritized primary fields. |
| 104 | */ |
| 105 | public function filterPrimaryModuleFields(string $moduleName, array $fields): array |
| 106 | { |
| 107 | $primaryKeys = match ($moduleName) { |
| 108 | 'contacts' => ['first_name', 'last_name', 'job_title', 'email', 'phone', 'address_city'], |
| 109 | 'companies' => ['name', 'tax_identifier', 'email', 'phone', 'address_city', 'status'], |
| 110 | 'partners' => ['name', 'tax_identifier', 'email', 'phone', 'address_city', 'status'], |
| 111 | 'tickets' => ['ticket_no', 'subject', 'ticket_status', 'priority', 'resolution_due'], |
| 112 | 'contracts' => ['contract_no', 'subject', 'contract_status', 'start_date', 'end_date'], |
| 113 | default => [], |
| 114 | }; |
| 115 | |
| 116 | if ($primaryKeys === []) { |
| 117 | return []; |
| 118 | } |
| 119 | |
| 120 | $visible = []; |
| 121 | foreach ($primaryKeys as $key) { |
| 122 | foreach ($fields as $f) { |
| 123 | if ($f->fieldKey === $key) { |
| 124 | $visible[] = $f; |
| 125 | break; |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | return $visible; |
| 130 | } |
| 131 | } |