These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace DoctrineModule\Validator; |
||
6 | |||
7 | use Doctrine\Persistence\ObjectRepository; |
||
8 | use Laminas\Stdlib\ArrayUtils; |
||
9 | use Laminas\Validator\AbstractValidator; |
||
10 | use Laminas\Validator\Exception; |
||
11 | use function array_combine; |
||
12 | use function array_key_exists; |
||
13 | use function array_values; |
||
14 | use function count; |
||
15 | use function get_class; |
||
16 | use function gettype; |
||
17 | use function is_object; |
||
18 | use function is_string; |
||
19 | use function sprintf; |
||
20 | |||
21 | /** |
||
22 | * Class that validates if objects exist in a given repository with a given list of matched fields |
||
23 | * |
||
24 | * @link http://www.doctrine-project.org/ |
||
25 | */ |
||
26 | class ObjectExists extends AbstractValidator |
||
27 | { |
||
28 | /** |
||
29 | * Error constants |
||
30 | */ |
||
31 | public const ERROR_NO_OBJECT_FOUND = 'noObjectFound'; |
||
32 | |||
33 | /** @var mixed[] Message templates */ |
||
34 | protected $messageTemplates = [self::ERROR_NO_OBJECT_FOUND => "No object matching '%value%' was found"]; |
||
35 | |||
36 | /** |
||
37 | * ObjectRepository from which to search for entities |
||
38 | * |
||
39 | * @var ObjectRepository |
||
40 | */ |
||
41 | protected $objectRepository; |
||
42 | |||
43 | /** |
||
44 | * Fields to be checked |
||
45 | * |
||
46 | * @var mixed[] |
||
47 | */ |
||
48 | protected $fields; |
||
49 | |||
50 | /** |
||
51 | * Constructor |
||
52 | * |
||
53 | * @param mixed[] $options required keys are `object_repository`, which must be an instance of |
||
54 | * Doctrine\Persistence\ObjectRepository, and `fields`, with either |
||
55 | * a string or an array of strings representing the fields to be matched by the validator. |
||
56 | * |
||
57 | * @throws Exception\InvalidArgumentException |
||
58 | */ |
||
59 | 27 | public function __construct(array $options) |
|
60 | { |
||
61 | 27 | if (! isset($options['object_repository']) || ! $options['object_repository'] instanceof ObjectRepository) { |
|
62 | 3 | if (! array_key_exists('object_repository', $options)) { |
|
63 | 1 | $provided = 'nothing'; |
|
64 | } else { |
||
65 | 2 | if (is_object($options['object_repository'])) { |
|
66 | 1 | $provided = get_class($options['object_repository']); |
|
67 | } else { |
||
68 | 1 | $provided = gettype($options['object_repository']); |
|
69 | } |
||
70 | } |
||
71 | |||
72 | 3 | throw new Exception\InvalidArgumentException( |
|
73 | 3 | sprintf( |
|
74 | 'Option "object_repository" is required and must be an instance of' |
||
75 | 3 | . ' Doctrine\Persistence\ObjectRepository, %s given', |
|
76 | 3 | $provided |
|
77 | ) |
||
78 | ); |
||
79 | } |
||
80 | |||
81 | 24 | $this->objectRepository = $options['object_repository']; |
|
82 | |||
83 | 24 | if (! isset($options['fields'])) { |
|
84 | 1 | throw new Exception\InvalidArgumentException( |
|
85 | 'Key `fields` must be provided and be a field or a list of fields to be used when searching for' |
||
86 | 1 | . ' existing instances' |
|
87 | ); |
||
88 | } |
||
89 | |||
90 | 23 | $this->fields = $options['fields']; |
|
0 ignored issues
–
show
|
|||
91 | 23 | $this->validateFields(); |
|
92 | |||
93 | 21 | parent::__construct($options); |
|
94 | 21 | } |
|
95 | |||
96 | /** |
||
97 | * Filters and validates the fields passed to the constructor |
||
98 | * |
||
99 | * @return mixed[] |
||
100 | * |
||
101 | * @throws Exception\InvalidArgumentException |
||
102 | */ |
||
103 | 23 | private function validateFields() : array |
|
104 | { |
||
105 | 23 | $fields = (array) $this->fields; |
|
106 | |||
107 | 23 | if (empty($fields)) { |
|
108 | 1 | throw new Exception\InvalidArgumentException('Provided fields list was empty!'); |
|
109 | } |
||
110 | |||
111 | 22 | foreach ($fields as $key => $field) { |
|
112 | 22 | if (! is_string($field)) { |
|
113 | 1 | throw new Exception\InvalidArgumentException( |
|
114 | 1 | sprintf('Provided fields must be strings, %s provided for key %s', gettype($field), $key) |
|
115 | ); |
||
116 | } |
||
117 | } |
||
118 | |||
119 | 21 | $this->fields = array_values($fields); |
|
120 | |||
121 | 21 | return $this->fields; |
|
122 | } |
||
123 | |||
124 | /** |
||
125 | * @param string|mixed[] $value a field value or an array of field values if more fields have been configured to be |
||
126 | * matched |
||
127 | * |
||
128 | * @return mixed[] |
||
129 | * |
||
130 | * @throws Exception\RuntimeException |
||
131 | */ |
||
132 | 19 | protected function cleanSearchValue($value) : array |
|
133 | { |
||
134 | 19 | $value = is_object($value) ? [$value] : (array) $value; |
|
135 | |||
136 | 19 | if (ArrayUtils::isHashTable($value)) { |
|
137 | 5 | $matchedFieldsValues = []; |
|
138 | |||
139 | 5 | foreach ($this->fields as $field) { |
|
140 | 5 | if (! array_key_exists($field, $value)) { |
|
141 | 1 | throw new Exception\RuntimeException( |
|
142 | 1 | sprintf( |
|
143 | 'Field "%s" was not provided, but was expected since the configured field lists needs' |
||
144 | 1 | . ' it for validation', |
|
145 | 1 | $field |
|
146 | ) |
||
147 | ); |
||
148 | } |
||
149 | |||
150 | 5 | $matchedFieldsValues[$field] = $value[$field]; |
|
151 | } |
||
152 | } else { |
||
153 | 16 | $matchedFieldsValues = @array_combine($this->fields, $value); |
|
154 | |||
155 | 16 | if ($matchedFieldsValues === false) { |
|
156 | 1 | throw new Exception\RuntimeException( |
|
157 | 1 | sprintf( |
|
158 | 1 | 'Provided values count is %s, while expected number of fields to be matched is %s', |
|
159 | 1 | count($value), |
|
160 | 1 | count($this->fields) |
|
161 | ) |
||
162 | ); |
||
163 | } |
||
164 | } |
||
165 | |||
166 | 17 | return $matchedFieldsValues; |
|
167 | } |
||
168 | |||
169 | /** |
||
170 | * {@inheritDoc} |
||
171 | */ |
||
172 | 6 | public function isValid($value) |
|
173 | { |
||
174 | 6 | $cleanedValue = $this->cleanSearchValue($value); |
|
175 | 4 | $match = $this->objectRepository->findOneBy($cleanedValue); |
|
176 | |||
177 | 4 | if (is_object($match)) { |
|
178 | 2 | return true; |
|
179 | } |
||
180 | |||
181 | 2 | $this->error(self::ERROR_NO_OBJECT_FOUND, $value); |
|
182 | |||
183 | 2 | return false; |
|
184 | } |
||
185 | } |
||
186 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..