1
|
|
|
<?php |
2
|
|
|
/******************************************************************************* |
3
|
|
|
* This file is part of the GraphQL Bundle package. |
4
|
|
|
* |
5
|
|
|
* (c) YnloUltratech <[email protected]> |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the LICENSE |
8
|
|
|
* file that was distributed with this source code. |
9
|
|
|
******************************************************************************/ |
10
|
|
|
|
11
|
|
|
namespace Ynlo\GraphQLBundle\Definition\Extension; |
12
|
|
|
|
13
|
|
|
use GraphQL\Type\Definition\Type; |
14
|
|
|
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; |
15
|
|
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; |
16
|
|
|
use Symfony\Component\Form\Extension\Core\Type\EmailType; |
17
|
|
|
use Symfony\Component\Form\Extension\Core\Type\IntegerType; |
18
|
|
|
use Symfony\Component\Form\Extension\Core\Type\TextType; |
19
|
|
|
use Symfony\Component\Form\FormFactory; |
20
|
|
|
use Symfony\Component\Form\FormInterface; |
21
|
|
|
use Ynlo\GraphQLBundle\Definition\ArgumentDefinition; |
22
|
|
|
use Ynlo\GraphQLBundle\Definition\DefinitionInterface; |
23
|
|
|
use Ynlo\GraphQLBundle\Definition\FieldDefinition; |
24
|
|
|
use Ynlo\GraphQLBundle\Definition\InputObjectDefinition; |
25
|
|
|
use Ynlo\GraphQLBundle\Definition\MutationDefinition; |
26
|
|
|
use Ynlo\GraphQLBundle\Definition\NodeAwareDefinitionInterface; |
27
|
|
|
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint; |
28
|
|
|
use Ynlo\GraphQLBundle\Form\Type\GraphQLType; |
29
|
|
|
use Ynlo\GraphQLBundle\Form\Type\IDType; |
30
|
|
|
use Ynlo\GraphQLBundle\Util\ClassUtils; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* MutationFormResolverExtension |
34
|
|
|
*/ |
35
|
|
|
class MutationFormResolverExtension extends AbstractDefinitionExtension |
36
|
|
|
{ |
37
|
|
|
/** |
38
|
|
|
* @var FormFactory |
39
|
|
|
*/ |
40
|
|
|
protected $formFactory; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @param FormFactory $formFactory |
44
|
|
|
*/ |
45
|
1 |
|
public function __construct(FormFactory $formFactory) |
46
|
|
|
{ |
47
|
1 |
|
$this->formFactory = $formFactory; |
48
|
1 |
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* {@inheritDoc} |
52
|
|
|
*/ |
53
|
1 |
|
public function getName(): string |
54
|
|
|
{ |
55
|
1 |
|
return 'form'; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* {@inheritDoc} |
60
|
|
|
*/ |
61
|
1 |
|
public function buildConfig(ArrayNodeDefinition $root) |
62
|
|
|
{ |
63
|
|
|
$config = $root |
64
|
1 |
|
->info('Resolve the form to use as input for mutations') |
65
|
1 |
|
->addDefaultsIfNotSet() |
66
|
1 |
|
->canBeDisabled() |
67
|
1 |
|
->children(); |
68
|
|
|
|
69
|
1 |
|
$config->variableNode('type') |
70
|
1 |
|
->defaultNull() |
71
|
1 |
|
->info( |
72
|
1 |
|
'Specify the form type to use, |
73
|
|
|
[string] Name of the form type to use |
74
|
|
|
[true|null] The form will be automatically resolved to ...Bundle\Form\Input\{Node}\{MutationName}Input. |
75
|
|
|
[true] Throw a exception if the form can`t be located |
76
|
|
|
[false] The form is not required and should not be resolved' |
77
|
|
|
); |
78
|
1 |
|
$config->variableNode('options')->defaultValue([])->info('Form options'); |
79
|
1 |
|
$config->variableNode('argument') |
80
|
1 |
|
->defaultValue('input') |
81
|
1 |
|
->info('Name of the argument to use as input'); |
82
|
|
|
|
83
|
1 |
|
$config->booleanNode('client_mutation_id') |
84
|
1 |
|
->defaultTrue() |
85
|
1 |
|
->info('Automatically add a field called clientMutationId'); |
86
|
1 |
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* {@inheritDoc} |
90
|
|
|
*/ |
91
|
1 |
|
public function configure(DefinitionInterface $definition, Endpoint $endpoint, array $config) |
92
|
|
|
{ |
93
|
1 |
|
if (!$config || !$config['enabled'] || !$definition instanceof MutationDefinition) { |
|
|
|
|
94
|
1 |
|
return; |
95
|
|
|
} |
96
|
|
|
|
97
|
1 |
|
$formType = $config['type'] ?? null; |
98
|
|
|
|
99
|
|
|
//the related class is used to match a form using naming conventions |
100
|
1 |
|
$relatedClass = null; |
101
|
1 |
|
if ($definition instanceof NodeAwareDefinitionInterface && $definition->getNode()) { |
102
|
1 |
|
$relatedClass = $definition->getNode(); |
103
|
1 |
|
if ($class = $endpoint->getClassForType($relatedClass)) { |
104
|
1 |
|
$relatedClass = $class; |
105
|
|
|
} |
106
|
|
|
} elseif ($definition->getResolver()) { |
107
|
|
|
$relatedClass = $definition->getResolver(); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
//try find the form using a related class |
111
|
1 |
|
if ($relatedClass && (!$formType || true === $formType)) { |
112
|
1 |
|
$bundleNamespace = ClassUtils::relatedBundleNamespace($relatedClass); |
113
|
1 |
|
$formClass = ClassUtils::applyNamingConvention( |
114
|
1 |
|
$bundleNamespace, |
115
|
1 |
|
'Form\Input', |
116
|
1 |
|
$definition->getNode(), |
117
|
1 |
|
ucfirst($definition->getName()), |
118
|
1 |
|
'Input' |
119
|
|
|
); |
120
|
1 |
|
if (class_exists($formClass)) { |
121
|
1 |
|
$formType = $formClass; |
122
|
|
|
} elseif (true === $formType) { |
123
|
|
|
$error = sprintf( |
124
|
|
|
'Can`t find a valid input form type to use in "%s". |
125
|
|
|
Create the form "%s" or specify a custom form', |
126
|
|
|
$definition->getName(), |
127
|
|
|
$formClass |
128
|
|
|
); |
129
|
|
|
throw new \Exception($error); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
133
|
1 |
|
if ($formType) { |
134
|
1 |
|
$config['type'] = $formType; |
135
|
|
|
|
136
|
1 |
|
$form = $this->formFactory->create($formType, null, $config['options'] ?? []); |
137
|
1 |
|
$inputObject = $this->createFormInputObject($endpoint, $form, ucfirst($definition->getName())); |
138
|
1 |
|
$endpoint->addType($inputObject); |
139
|
|
|
|
140
|
1 |
|
$input = new ArgumentDefinition(); |
141
|
1 |
|
$input->setName($config['argument']); |
142
|
1 |
|
$input->setType($inputObject->getName()); |
143
|
|
|
|
144
|
1 |
|
if ($config['client_mutation_id']) { |
145
|
1 |
|
$clientMutationId = new FieldDefinition(); |
146
|
1 |
|
$clientMutationId->setName('clientMutationId'); |
147
|
1 |
|
$clientMutationId->setType(Type::STRING); |
148
|
1 |
|
$clientMutationId->setDescription('A unique identifier for the client performing the mutation.'); |
149
|
1 |
|
$inputObject->prependField($clientMutationId); |
150
|
|
|
} |
151
|
|
|
|
152
|
1 |
|
$definition->addArgument($input); |
153
|
1 |
|
$definition->setMeta('form', $config); |
154
|
|
|
} |
155
|
1 |
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @param Endpoint $endpoint |
159
|
|
|
* @param FormInterface $form |
160
|
|
|
* @param string $name |
161
|
|
|
* |
162
|
|
|
* @return InputObjectDefinition |
163
|
|
|
*/ |
164
|
1 |
|
public function createFormInputObject(Endpoint $endpoint, FormInterface $form, $name) |
165
|
|
|
{ |
166
|
1 |
|
$inputObject = new InputObjectDefinition(); |
167
|
1 |
|
$inputObject->setName($name.'Input'); |
168
|
|
|
|
169
|
1 |
|
foreach ($form->all() as $formField) { |
170
|
1 |
|
$field = new FieldDefinition(); |
171
|
1 |
|
$field->setName($formField->getConfig()->getOption('label') ?? $formField->getName()); |
172
|
1 |
|
$field->setNonNull($formField->isRequired()); |
173
|
1 |
|
$field->setOriginName($formField->getName()); |
174
|
|
|
|
175
|
1 |
|
if ($formField->all()) { |
176
|
1 |
|
$childName = $name.ucfirst($formField->getName()); |
177
|
1 |
|
$child = $this->createFormInputObject($endpoint, $formField, $childName); |
178
|
1 |
|
$endpoint->addType($child); |
179
|
1 |
|
$field->setType($child->getName()); |
180
|
|
|
} else { |
181
|
1 |
|
$this->resolveFormFieldDefinition($field, $formField); |
182
|
|
|
} |
183
|
|
|
|
184
|
1 |
|
$inputObject->addField($field); |
185
|
|
|
} |
186
|
|
|
|
187
|
1 |
|
return $inputObject; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @param FieldDefinition $field |
192
|
|
|
* @param FormInterface $form |
193
|
|
|
*/ |
194
|
1 |
|
public function resolveFormFieldDefinition(FieldDefinition $field, FormInterface $form) |
195
|
|
|
{ |
196
|
1 |
|
$type = null; |
197
|
1 |
|
$resolver = $form->getConfig()->getType()->getOptionsResolver(); |
198
|
1 |
|
if ($resolver->hasDefault('graphql_type')) { |
199
|
|
|
$type = $resolver->resolve([])['graphql_type']; |
200
|
|
|
} |
201
|
|
|
|
202
|
1 |
|
if (is_a($form->getConfig()->getType()->getInnerType(), GraphQLType::class, true)) { |
203
|
1 |
|
$type = $form->getConfig()->getOptions()['graphql_type']; |
204
|
|
|
} |
205
|
|
|
|
206
|
1 |
|
if (is_a($form->getConfig()->getType()->getInnerType(), IDType::class, true)) { |
207
|
1 |
|
if ($form->getConfig()->hasOption('multiple') && $form->getConfig()->getOption('multiple')) { |
208
|
1 |
|
$field->setList(true); |
209
|
|
|
} |
210
|
1 |
|
$type = Type::ID; |
211
|
|
|
} |
212
|
|
|
|
213
|
1 |
|
if (is_a($form->getConfig()->getType()->getInnerType(), TextType::class, true)) { |
214
|
1 |
|
$type = Type::STRING; |
215
|
|
|
} |
216
|
|
|
|
217
|
1 |
|
if (is_a($form->getConfig()->getType()->getInnerType(), EmailType::class, true)) { |
218
|
1 |
|
$type = Type::STRING; |
219
|
|
|
} |
220
|
|
|
|
221
|
1 |
|
if (is_a($form->getConfig()->getType()->getInnerType(), CheckboxType::class, true)) { |
222
|
1 |
|
$type = Type::BOOLEAN; |
223
|
|
|
} |
224
|
|
|
|
225
|
1 |
|
if (is_a($form->getConfig()->getType()->getInnerType(), IntegerType::class, true)) { |
226
|
|
|
$type = Type::INT; |
227
|
|
|
} |
228
|
|
|
|
229
|
1 |
|
if (!$type) { |
230
|
|
|
$error = sprintf( |
231
|
|
|
'The field "%s" in the parent form "%s" does not have a valid type. |
232
|
|
|
If your are using a custom type, must define a option called "graphql_type" to resolve the form to a valid GraphQL type', |
233
|
|
|
$form->getName(), |
234
|
|
|
$form->getParent()->getName() |
235
|
|
|
); |
236
|
|
|
throw new \InvalidArgumentException($error); |
237
|
|
|
} |
238
|
|
|
|
239
|
1 |
|
$field->setType($type); |
240
|
1 |
|
} |
241
|
|
|
} |
242
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.