1 | <?php |
||||||
2 | |||||||
3 | namespace Mdiyakov\DoctrineSolrBundle\Config; |
||||||
4 | |||||||
5 | use Mdiyakov\DoctrineSolrBundle\Exception\ClientConfigException; |
||||||
6 | use Mdiyakov\DoctrineSolrBundle\Exception\ConfigFieldException; |
||||||
7 | use Mdiyakov\DoctrineSolrBundle\Exception\EntityClassConfigException; |
||||||
8 | use Mdiyakov\DoctrineSolrBundle\Exception\FilterConfigException; |
||||||
9 | use Mdiyakov\DoctrineSolrBundle\Exception\RequiredFieldException; |
||||||
10 | use Mdiyakov\DoctrineSolrBundle\Exception\SchemaConfigException; |
||||||
11 | use Mdiyakov\DoctrineSolrBundle\Exception\SchemaNotFoundException; |
||||||
12 | |||||||
13 | class ConfigValidator |
||||||
14 | { |
||||||
15 | /** |
||||||
16 | * @var string[] |
||||||
17 | */ |
||||||
18 | private $discriminatorValues = []; |
||||||
19 | |||||||
20 | /** |
||||||
21 | * @var string[] |
||||||
22 | */ |
||||||
23 | private $entityClasses = []; |
||||||
24 | |||||||
25 | /** |
||||||
26 | * @param string[][] $entityConfig |
||||||
27 | * @param string[][] $schemes |
||||||
28 | * @param string[][] $filters |
||||||
29 | * @param string[] $clients |
||||||
30 | * @throws SchemaNotFoundException |
||||||
31 | */ |
||||||
32 | public function validate($entityConfig, $schemes, $filters, $clients) |
||||||
33 | { |
||||||
34 | if (!array_key_exists($entityConfig['schema'], $schemes)) { |
||||||
35 | throw new SchemaNotFoundException( |
||||||
36 | sprintf('Schema "%s" is not found in "schema" config section. Check config.yml', $entityConfig['schema']) |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
37 | ); |
||||||
38 | } |
||||||
39 | |||||||
40 | $schemaConfig = $schemes[$entityConfig['schema']]; |
||||||
41 | $this->checkEntityContainRequiredFields($entityConfig['class'], $schemaConfig); |
||||||
0 ignored issues
–
show
$entityConfig['class'] of type string[] is incompatible with the type string expected by parameter $entityClass of Mdiyakov\DoctrineSolrBun...ContainRequiredFields() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() $schemaConfig of type string[] is incompatible with the type array<mixed,string[]> expected by parameter $schemaConfig of Mdiyakov\DoctrineSolrBun...ContainRequiredFields() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
42 | $this->checkClassesForUnique($entityConfig['class']); |
||||||
43 | $this->checkConfigFields($entityConfig, $schemaConfig); |
||||||
0 ignored issues
–
show
$schemaConfig of type string[] is incompatible with the type array<mixed,string[]> expected by parameter $schemaConfig of Mdiyakov\DoctrineSolrBun...or::checkConfigFields() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
44 | $this->checkFilters($entityConfig, $filters); |
||||||
45 | $this->checkClients($schemaConfig, $clients); |
||||||
46 | $this->checkDocumentFieldNamesAreUnique($schemaConfig); |
||||||
47 | } |
||||||
48 | |||||||
49 | /** |
||||||
50 | * @param string $entityClass |
||||||
51 | * @param string[][] $schemaConfig |
||||||
52 | * @throws RequiredFieldException |
||||||
53 | */ |
||||||
54 | private function checkEntityContainRequiredFields($entityClass, $schemaConfig) |
||||||
55 | { |
||||||
56 | foreach ($schemaConfig['fields'] as $fieldConfig) { |
||||||
57 | $this->checkEntityHasField($entityClass, $fieldConfig['entity_field_name']); |
||||||
58 | } |
||||||
59 | } |
||||||
60 | |||||||
61 | /** |
||||||
62 | * @param string[][] $entityConfig |
||||||
63 | * @param string[][] $schemaConfig |
||||||
64 | * @throws ConfigFieldException |
||||||
65 | */ |
||||||
66 | private function checkConfigFields($entityConfig, $schemaConfig) |
||||||
67 | { |
||||||
68 | $schemaConfigEntityFields = $schemaConfig['config_entity_fields']; |
||||||
69 | if (!is_array($schemaConfigEntityFields)) { |
||||||
0 ignored issues
–
show
|
|||||||
70 | return; |
||||||
71 | } |
||||||
72 | |||||||
73 | $configFields = $entityConfig['config']; |
||||||
74 | if (!is_array($configFields)) { |
||||||
0 ignored issues
–
show
|
|||||||
75 | throw new ConfigFieldException( |
||||||
76 | sprintf('Config fields for "%s" entity are not defined. Check entity config', $entityConfig['class']) |
||||||
77 | ); |
||||||
78 | } |
||||||
79 | $configFieldsNames = []; |
||||||
80 | foreach ($configFields as $configField) { |
||||||
81 | $configFieldsNames[$configField['name']] = $configField['value']; |
||||||
82 | } |
||||||
83 | |||||||
84 | foreach ($schemaConfigEntityFields as $fieldConfig) { |
||||||
85 | if (!array_key_exists($fieldConfig['config_field_name'], $configFieldsNames)) { |
||||||
86 | throw new ConfigFieldException( |
||||||
87 | sprintf( |
||||||
88 | '"%s" config field is not defined in indexed_entities for entity %s', |
||||||
89 | $fieldConfig['config_field_name'], |
||||||
90 | $entityConfig['class'] |
||||||
0 ignored issues
–
show
$entityConfig['class'] of type string[] is incompatible with the type string expected by parameter $args of sprintf() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
91 | ) |
||||||
92 | ); |
||||||
93 | } |
||||||
94 | |||||||
95 | if ($fieldConfig['discriminator'] === true) { |
||||||
96 | $discriminatorValue = $configFieldsNames[$fieldConfig['config_field_name']]; |
||||||
97 | if (in_array($discriminatorValue, $this->discriminatorValues)) { |
||||||
98 | throw new ConfigFieldException( |
||||||
99 | sprintf( |
||||||
100 | '"%s" discriminator value has already been used. |
||||||
101 | It seems there are two entity classes inside "indexed_entities" with identical discriminator config field value', |
||||||
102 | $discriminatorValue |
||||||
103 | ) |
||||||
104 | ); |
||||||
105 | } |
||||||
106 | $this->discriminatorValues[] = $discriminatorValue; |
||||||
107 | } |
||||||
108 | } |
||||||
109 | } |
||||||
110 | |||||||
111 | /** |
||||||
112 | * @param $schemaConfig |
||||||
113 | * @param $clients |
||||||
114 | * @throws ClientConfigException |
||||||
115 | */ |
||||||
116 | private function checkClients($schemaConfig, $clients) |
||||||
117 | { |
||||||
118 | if (!empty($schemaConfig['client']) && !array_key_exists($schemaConfig['client'], $clients)) { |
||||||
119 | throw new ClientConfigException( |
||||||
120 | sprintf('Solarium client "%s" is not defined in "solarium_clients" section', $schemaConfig['client']) |
||||||
121 | ); |
||||||
122 | } |
||||||
123 | } |
||||||
124 | |||||||
125 | /** |
||||||
126 | * @param string[][] $entityConfig |
||||||
127 | * @param string[][] $filters |
||||||
128 | */ |
||||||
129 | private function checkFilters($entityConfig, $filters) |
||||||
130 | { |
||||||
131 | if (array_key_exists('filters', $entityConfig)) { |
||||||
132 | |||||||
133 | $filtersKeys = array_fill_keys( |
||||||
134 | array_merge( |
||||||
135 | array_keys($filters['fields']), |
||||||
136 | array_keys($filters['services']) |
||||||
137 | ), |
||||||
138 | true |
||||||
139 | ); |
||||||
140 | |||||||
141 | foreach($entityConfig['filters'] as $filterName) { |
||||||
142 | if (!array_key_exists($filterName, $filtersKeys)) { |
||||||
143 | throw new FilterConfigException( |
||||||
144 | sprintf('Filter "%s" is not defined in "filters" section', $filterName) |
||||||
145 | ); |
||||||
146 | } |
||||||
147 | } |
||||||
148 | |||||||
149 | if (array_key_exists('fields', $filters) && is_array($filters['fields'])) { |
||||||
150 | foreach ($filters['fields'] as $filterName => $filterConfig) { |
||||||
151 | try { |
||||||
152 | $this->checkEntityHasField($entityConfig['class'], $filterConfig['entity_field_name']); |
||||||
153 | } catch (RequiredFieldException $e) { |
||||||
154 | $message = $e->getMessage() . ' Error occurred for "filters" section. Please check the filters configuration in config.yml'; |
||||||
155 | throw new RequiredFieldException($message); |
||||||
156 | } |
||||||
157 | |||||||
158 | } |
||||||
159 | } |
||||||
160 | } |
||||||
161 | } |
||||||
162 | |||||||
163 | /** |
||||||
164 | * @param string[][] $schemaConfig |
||||||
165 | * @throws SchemaConfigException |
||||||
166 | */ |
||||||
167 | private function checkDocumentFieldNamesAreUnique($schemaConfig) |
||||||
168 | { |
||||||
169 | $schemaFields = [ $schemaConfig['config_entity_fields'], $schemaConfig['fields'] ]; |
||||||
170 | $documentFieldsNames = []; |
||||||
171 | |||||||
172 | while ($fieldsConfig = array_shift($schemaFields)) { |
||||||
173 | foreach ($fieldsConfig as $fieldConfig) { |
||||||
174 | $documentFieldName = $fieldConfig['document_field_name']; |
||||||
175 | if (array_key_exists($documentFieldName, $documentFieldsNames)) { |
||||||
176 | throw new SchemaConfigException( |
||||||
177 | sprintf('You have more than one fields with the same document field name "%s"', $documentFieldName) |
||||||
178 | ); |
||||||
179 | } |
||||||
180 | $documentFieldsNames[$documentFieldName] = true; |
||||||
181 | } |
||||||
182 | } |
||||||
183 | } |
||||||
184 | |||||||
185 | /** |
||||||
186 | * @param string $entityClass |
||||||
187 | * @param string $entityFieldName |
||||||
188 | * @throws RequiredFieldException |
||||||
189 | */ |
||||||
190 | private function checkEntityHasField($entityClass, $entityFieldName) |
||||||
191 | { |
||||||
192 | $reflection = new \ReflectionClass($entityClass); |
||||||
193 | $getterMethodName = 'get' . ucfirst($entityFieldName); |
||||||
194 | $isserMethodName = 'is' . ucfirst($entityFieldName); |
||||||
195 | |||||||
196 | if ($reflection->hasMethod($getterMethodName)) { |
||||||
197 | $getterReflectionMethod = new \ReflectionMethod($entityClass, $getterMethodName); |
||||||
198 | if (!$getterReflectionMethod->isPublic()) { |
||||||
199 | throw new RequiredFieldException( |
||||||
200 | sprintf( |
||||||
201 | 'Getter method "%s" is not public in "%s".', |
||||||
202 | $getterMethodName, |
||||||
203 | $entityClass |
||||||
204 | ) |
||||||
205 | ); |
||||||
206 | } |
||||||
207 | } elseif ($reflection->hasMethod($isserMethodName)) { |
||||||
208 | $isserReflectionMethod = new \ReflectionMethod($entityClass, $isserMethodName); |
||||||
209 | if (!$isserReflectionMethod->isPublic()) { |
||||||
210 | throw new RequiredFieldException( |
||||||
211 | sprintf( |
||||||
212 | 'Isser method "%s" is not public in "%s".', |
||||||
213 | $isserMethodName, |
||||||
214 | $entityClass |
||||||
215 | ) |
||||||
216 | ); |
||||||
217 | } |
||||||
218 | } else { |
||||||
219 | throw new RequiredFieldException( |
||||||
220 | sprintf( |
||||||
221 | 'Either getter method "%s" or isser method "%s" is not found in %s.', |
||||||
222 | $getterMethodName, |
||||||
223 | $isserMethodName, |
||||||
224 | $entityClass |
||||||
225 | ) |
||||||
226 | ); |
||||||
227 | } |
||||||
228 | } |
||||||
229 | |||||||
230 | /** |
||||||
231 | * @param $entityClass |
||||||
232 | * @throws EntityClassConfigException |
||||||
233 | */ |
||||||
234 | private function checkClassesForUnique($entityClass) |
||||||
235 | { |
||||||
236 | if (!in_array($entityClass, $this->entityClasses)) { |
||||||
237 | $this->entityClasses[] = $entityClass; |
||||||
238 | } else { |
||||||
239 | throw new EntityClassConfigException( |
||||||
240 | sprintf( |
||||||
241 | 'It seems entity class "%s" has been configured more than once inside "indexed_entities" section. You can not have different config for the single entity class', |
||||||
242 | $entityClass |
||||||
243 | ) |
||||||
244 | ); |
||||||
245 | } |
||||||
246 | } |
||||||
247 | } |