1 | <?php |
||
12 | class JsonDefinitionHash implements DefinitionElementInterface |
||
13 | { |
||
14 | /** |
||
15 | * @var string Name of this hash |
||
16 | */ |
||
17 | private $name; |
||
18 | /** |
||
19 | * @var JsonDefinition |
||
20 | */ |
||
21 | private $parent; |
||
22 | /** |
||
23 | * @var DefinitionElementInterface[] Array of fields.. |
||
24 | */ |
||
25 | private $fields = []; |
||
26 | /** |
||
27 | * @var Schema\Field Field definition |
||
28 | */ |
||
29 | private $definition; |
||
30 | |||
31 | /** |
||
32 | * Constructor |
||
33 | * |
||
34 | * @param string $name Name of this hash |
||
35 | * @param JsonDefinition $parent Parent definiton |
||
36 | * @param DefinitionElementInterface[] $fields Fields of the hash |
||
37 | * @param Schema\Field $definition Field definition |
||
|
|||
38 | */ |
||
39 | 42 | public function __construct($name, JsonDefinition $parent, array $fields, Schema\Field $definition = null) |
|
40 | { |
||
41 | 42 | $this->name = $name; |
|
42 | 42 | $this->parent = $parent; |
|
43 | 42 | $this->fields = $fields; |
|
44 | 42 | $this->definition = $definition; |
|
45 | 42 | } |
|
46 | |||
47 | /** |
||
48 | * Returns the hash name |
||
49 | * |
||
50 | * @return string Name |
||
51 | */ |
||
52 | 22 | public function getName() |
|
53 | { |
||
54 | 22 | return $this->name; |
|
55 | } |
||
56 | |||
57 | /** |
||
58 | * Returns the definition as array.. |
||
59 | * |
||
60 | * @return array the definition |
||
61 | */ |
||
62 | 6 | public function getDefAsArray() |
|
63 | { |
||
64 | 6 | return array_replace( |
|
65 | [ |
||
66 | 6 | 'name' => $this->getName(), |
|
67 | 6 | 'type' => $this->getType(), |
|
68 | 6 | 'exposedName' => $this->getName(), |
|
69 | 6 | 'doctrineType' => $this->getTypeDoctrine(), |
|
70 | 6 | 'serializerType' => $this->getTypeSerializer(), |
|
71 | 6 | 'relType' => self::REL_TYPE_EMBED, |
|
72 | 3 | 'isClassType' => true, |
|
73 | 3 | 'constraints' => [], |
|
74 | 3 | 'required' => false, |
|
75 | 6 | 'searchable' => 0, |
|
76 | 3 | ], |
|
77 | 6 | $this->definition === null ? [ |
|
78 | 4 | 'required' => $this->isRequired() |
|
79 | 2 | ] : [ |
|
80 | 2 | 'exposedName' => $this->definition->getExposeAs() ?: $this->getName(), |
|
81 | 2 | 'title' => $this->definition->getTitle(), |
|
82 | 2 | 'description' => $this->definition->getDescription(), |
|
83 | 2 | 'readOnly' => $this->definition->getReadOnly(), |
|
84 | 2 | 'required' => $this->definition->getRequired(), |
|
85 | 2 | 'searchable' => $this->definition->getSearchable(), |
|
86 | 2 | 'constraints' => array_map( |
|
87 | 2 | [Utils\ConstraintNormalizer::class, 'normalize'], |
|
88 | 4 | $this->definition->getConstraints() |
|
89 | 1 | ), |
|
90 | ] |
||
91 | 3 | ); |
|
92 | } |
||
93 | |||
94 | /** |
||
95 | * {@inheritDoc} |
||
96 | * |
||
97 | * @return string type |
||
98 | */ |
||
99 | 8 | public function getType() |
|
100 | { |
||
101 | 8 | return self::TYPE_HASH; |
|
102 | } |
||
103 | |||
104 | /** |
||
105 | * Whether this hash is anonymous, so has no own field definition (properly only defined |
||
106 | * by definitions such as "object.field", not an own definition) |
||
107 | * |
||
108 | * @return bool true if yes, false otherwise |
||
109 | */ |
||
110 | 4 | public function isAnonymous() |
|
111 | { |
||
112 | 4 | return ($this->definition === null); |
|
113 | } |
||
114 | |||
115 | /** |
||
116 | * in an 'anonymous' hash situation, we will check if any children are required |
||
117 | * |
||
118 | * @return bool if required or not |
||
119 | */ |
||
120 | 4 | public function isRequired() |
|
121 | { |
||
122 | 4 | $isRequired = false; |
|
123 | |||
124 | // see if on the first level of fields we have a required=true in the definition |
||
125 | 4 | foreach ($this->fields as $field) { |
|
126 | 2 | if ($field instanceof JsonDefinitionField && |
|
127 | 2 | $field->getDef() instanceof Field && |
|
128 | 2 | $field->getDef()->getRequired() === true |
|
129 | 1 | ) { |
|
130 | 2 | $isRequired = true; |
|
131 | 1 | } |
|
132 | 2 | } |
|
133 | |||
134 | 4 | return $isRequired; |
|
135 | } |
||
136 | |||
137 | /** |
||
138 | * {@inheritDoc} |
||
139 | * |
||
140 | * @return string type |
||
141 | */ |
||
142 | 8 | public function getTypeDoctrine() |
|
143 | { |
||
144 | 8 | return $this->getClassName(true); |
|
145 | } |
||
146 | |||
147 | /** |
||
148 | * Returns the field type in a serializer-understandable way.. |
||
149 | * |
||
150 | * @return string Type |
||
151 | */ |
||
152 | 8 | public function getTypeSerializer() |
|
153 | { |
||
154 | 8 | return $this->getClassName(true); |
|
155 | } |
||
156 | |||
157 | /** |
||
158 | * Returns the field definition of this hash from "local perspective", |
||
159 | * meaning that we only include fields inside this hash BUT with all |
||
160 | * the stuff from the json file. this is needed to generate a Document/Model |
||
161 | * from this hash (generate a json file again) |
||
162 | * |
||
163 | * @return JsonDefinition the definition of this hash in a standalone array ready to be json_encoded() |
||
164 | */ |
||
165 | 10 | public function getJsonDefinition() |
|
166 | { |
||
167 | 10 | $definition = (new Schema\Definition()) |
|
168 | 10 | ->setId($this->getClassName()) |
|
169 | 10 | ->setDescription($this->definition === null ? null : $this->definition->getDescription()) |
|
170 | 10 | ->setTitle($this->definition === null ? null : $this->definition->getTitle()) |
|
171 | 10 | ->setIsSubDocument(true) |
|
172 | 10 | ->setTarget(new Schema\Target()); |
|
173 | |||
174 | 10 | foreach ($this->fields as $field) { |
|
175 | 10 | foreach ($this->processFieldDefinitionsRecursive($field) as $definitions) { |
|
176 | 10 | $definition->getTarget()->addField($definitions); |
|
177 | 5 | } |
|
178 | 5 | } |
|
179 | 10 | foreach ($this->parent->getRelations() as $relation) { |
|
180 | 4 | $relation = $this->processParentRelation($relation); |
|
181 | 4 | if ($relation !== null) { |
|
182 | 4 | $definition->getTarget()->addRelation($relation); |
|
183 | 2 | } |
|
184 | 5 | } |
|
185 | |||
186 | 10 | return new JsonDefinition($definition); |
|
187 | } |
||
188 | |||
189 | /** |
||
190 | * Method getFieldDefinitionsRecursive |
||
191 | * |
||
192 | * @param DefinitionElementInterface $field |
||
193 | * @return Schema\Field[] |
||
194 | */ |
||
195 | 10 | private function processFieldDefinitionsRecursive(DefinitionElementInterface $field) |
|
196 | { |
||
197 | 10 | if ($field instanceof JsonDefinitionField) { |
|
198 | 10 | return [$this->cloneFieldDefinition($field->getDef())]; |
|
199 | 8 | } elseif ($field instanceof JsonDefinitionArray) { |
|
200 | 8 | return $this->processFieldDefinitionsRecursive($field->getElement()); |
|
201 | 4 | } elseif ($field instanceof JsonDefinitionHash) { |
|
202 | 4 | return array_reduce( |
|
203 | 4 | $field->fields, |
|
204 | 4 | function (array $subfields, DefinitionElementInterface $subfield) { |
|
205 | 4 | return array_merge($subfields, $this->processFieldDefinitionsRecursive($subfield)); |
|
206 | 4 | }, |
|
207 | 4 | $field->definition === null ? [] : [$this->cloneFieldDefinition($field->definition)] |
|
208 | 2 | ); |
|
209 | } |
||
210 | |||
211 | throw new \InvalidArgumentException(sprintf('Unknown field type "%s"', get_class($field))); |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Clone field definition |
||
216 | * |
||
217 | * @param Schema\Field $field Field |
||
218 | * @return Schema\Field |
||
219 | */ |
||
220 | 10 | private function cloneFieldDefinition(Schema\Field $field) |
|
226 | |||
227 | /** |
||
228 | * Process parent relation |
||
229 | * |
||
230 | * @param Schema\Relation $relation Parent relation |
||
231 | * @return Schema\Relation|null |
||
232 | */ |
||
233 | 4 | private function processParentRelation(Schema\Relation $relation) |
|
244 | |||
245 | /** |
||
246 | * Returns the class name of this hash, possibly |
||
247 | * taking the parent element into the name. this |
||
248 | * string here results in the name of the generated Document. |
||
249 | * |
||
250 | * @param boolean $fq if true, we'll return the class name full qualified |
||
251 | * |
||
252 | * @return string |
||
253 | */ |
||
254 | 20 | private function getClassName($fq = false) |
|
263 | } |
||
264 |
This check looks for
@param
annotations where the type inferred by our type inference engine differs from the declared type.It makes a suggestion as to what type it considers more descriptive.
Most often this is a case of a parameter that can be null in addition to its declared types.