1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the SexyField package. |
5
|
|
|
* |
6
|
|
|
* (c) Dion Snoeijen <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
declare(strict_types=1); |
13
|
|
|
|
14
|
|
|
namespace Tardigrades\Command; |
15
|
|
|
|
16
|
|
|
use Symfony\Component\Console\Command\Command; |
17
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
18
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
19
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
20
|
|
|
use Symfony\Component\Yaml\Yaml; |
21
|
|
|
use Tardigrades\SectionField\Service\ApplicationManagerInterface; |
22
|
|
|
use Tardigrades\SectionField\Service\FieldManagerInterface; |
23
|
|
|
use Tardigrades\SectionField\Service\FieldTypeManagerInterface; |
24
|
|
|
use Tardigrades\SectionField\Service\FieldTypeNotFoundException; |
25
|
|
|
use Tardigrades\SectionField\Service\LanguageManagerInterface; |
26
|
|
|
use Tardigrades\SectionField\Service\NotFoundException; |
27
|
|
|
use Tardigrades\SectionField\Service\SectionManagerInterface; |
28
|
|
|
use Tardigrades\SectionField\ValueObject\ApplicationConfig; |
29
|
|
|
use Tardigrades\SectionField\ValueObject\ConfigWithHandleInterface; |
30
|
|
|
use Tardigrades\SectionField\ValueObject\FieldConfig; |
31
|
|
|
use Tardigrades\SectionField\ValueObject\FullyQualifiedClassName; |
32
|
|
|
use Tardigrades\SectionField\ValueObject\LanguageConfig; |
33
|
|
|
use Tardigrades\SectionField\ValueObject\SectionConfig; |
34
|
|
|
use Tardigrades\SectionField\ValueObject\Type; |
35
|
|
|
|
36
|
|
|
class InstallDirectoryCommand extends Command |
37
|
|
|
{ |
38
|
|
|
/** @var ApplicationConfig[] */ |
39
|
|
|
private $applications = []; |
40
|
|
|
|
41
|
|
|
/** @var ?LanguageConfig */ |
42
|
|
|
private $languages = null; |
43
|
|
|
|
44
|
|
|
/** @var SectionConfig[] */ |
45
|
|
|
private $sections = []; |
46
|
|
|
|
47
|
|
|
/** @var FieldConfig[] */ |
48
|
|
|
private $fields = []; |
49
|
|
|
|
50
|
|
|
/** @var ApplicationManagerInterface */ |
51
|
|
|
private $applicationManager; |
52
|
|
|
|
53
|
|
|
/** @var LanguageManagerInterface */ |
54
|
|
|
private $languageManager; |
55
|
|
|
|
56
|
|
|
/** @var SectionManagerInterface */ |
57
|
|
|
private $sectionManager; |
58
|
|
|
|
59
|
|
|
/** @var FieldManagerInterface */ |
60
|
|
|
private $fieldManager; |
61
|
|
|
|
62
|
|
|
/** @var FieldTypeManagerInterface */ |
63
|
|
|
private $fieldTypeManager; |
64
|
|
|
|
65
|
|
|
public function __construct( |
66
|
|
|
ApplicationManagerInterface $applicationManager, |
67
|
|
|
LanguageManagerInterface $languageManager, |
68
|
|
|
SectionManagerInterface $sectionManager, |
69
|
|
|
FieldManagerInterface $fieldManager, |
70
|
|
|
FieldTypeManagerInterface $fieldTypeManager |
71
|
|
|
) { |
72
|
|
|
parent::__construct('sf:install-directory'); |
73
|
|
|
$this->applicationManager = $applicationManager; |
74
|
|
|
$this->languageManager = $languageManager; |
75
|
|
|
$this->sectionManager = $sectionManager; |
76
|
|
|
$this->fieldManager = $fieldManager; |
77
|
|
|
$this->fieldTypeManager = $fieldTypeManager; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
protected function configure(): void |
81
|
|
|
{ |
82
|
|
|
$this |
83
|
|
|
->setDescription('Install all configuration in a directory') |
84
|
|
|
->setHelp('This command installs all .yml files in a directory, recursively. Pass it the path to' . |
85
|
|
|
'the directory, for example "app/config/my-sexy-field-config".') |
86
|
|
|
->addArgument('directory', InputArgument::REQUIRED, 'The config directory'); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @param InputInterface $input |
91
|
|
|
* @param OutputInterface $output |
92
|
|
|
* @throws \Exception |
93
|
|
|
*/ |
94
|
|
|
protected function execute(InputInterface $input, OutputInterface $output): void |
95
|
|
|
{ |
96
|
|
|
foreach (static::getAllYamls($input->getArgument('directory')) as $fileName => $config) { |
97
|
|
|
$this->classifyFile($fileName, $config); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
$this->verifyConfig(); |
101
|
|
|
|
102
|
|
|
$this->createLanguages($this->languages); |
|
|
|
|
103
|
|
|
$output->writeln("<info>Languages created!</info>"); |
104
|
|
|
|
105
|
|
|
$applicationCount = $this->createApplications($this->applications); |
106
|
|
|
$output->writeln("<info>$applicationCount applications created!</info>"); |
107
|
|
|
|
108
|
|
|
$fieldTypeCount = $this->installFieldTypes($this->fields); |
109
|
|
|
$output->writeln("<info>$fieldTypeCount field types installed!</info>"); |
110
|
|
|
|
111
|
|
|
$fieldCount = $this->createFields($this->fields); |
112
|
|
|
$output->writeln("<info>$fieldCount fields created!</info>"); |
113
|
|
|
|
114
|
|
|
$sectionCount = $this->createSections($this->sections); |
115
|
|
|
$output->writeln("<info>$sectionCount sections created!</info>"); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @throws \Exception |
120
|
|
|
*/ |
121
|
|
|
private function verifyConfig(): void |
122
|
|
|
{ |
123
|
|
|
if (count($this->applications) === 0) { |
124
|
|
|
throw new \Exception("Could not find any application config files"); |
125
|
|
|
} |
126
|
|
|
if (is_null($this->languages)) { |
127
|
|
|
throw new \Exception("Could not find a language config file"); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* @param FieldConfig[] $fields |
133
|
|
|
* @return int the number of field types detected and installed |
134
|
|
|
*/ |
135
|
|
|
private function installFieldTypes(array $fields): int |
136
|
|
|
{ |
137
|
|
|
$fieldTypes = []; |
138
|
|
|
foreach ($fields as $field) { |
139
|
|
|
$fieldType = $field->toArray()['field']['type']; |
140
|
|
|
if (!in_array($fieldType, $fieldTypes)) { |
141
|
|
|
$fieldTypes[] = $fieldType; |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
foreach ($fieldTypes as $fieldType) { |
146
|
|
|
try { |
147
|
|
|
$this->fieldTypeManager->readByType(Type::fromString($fieldType)); |
148
|
|
|
// Already installed, so continue |
149
|
|
|
// There's no clean way of updating field types |
150
|
|
|
} catch (FieldTypeNotFoundException $exception) { |
151
|
|
|
// Not installed, so install |
152
|
|
|
if ($fieldType === 'DateTimeField') { |
153
|
|
|
// DateTime has "Field" at the end of its name to avoid confusion with \DateTime. |
154
|
|
|
// All other field types follow Tardigrades\FieldType\{fieldType}\{fieldType}. |
155
|
|
|
// This solution is hacky, but there's no good way to detect classes before they've been loaded. |
156
|
|
|
$className = "Tardigrades\\FieldType\\DateTime\\$fieldType"; |
157
|
|
|
} else { |
158
|
|
|
$className = "Tardigrades\\FieldType\\$fieldType\\$fieldType"; |
159
|
|
|
} |
160
|
|
|
$this->fieldTypeManager->createWithFullyQualifiedClassName( |
161
|
|
|
FullyQualifiedClassName::fromString($className) |
162
|
|
|
); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
return count($fieldTypes); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param SectionConfig[] $sections |
171
|
|
|
* @return int the number of sections created |
172
|
|
|
*/ |
173
|
|
|
private function createSections(array $sections): int |
174
|
|
|
{ |
175
|
|
|
return $this->createOrUpdateConfigs($sections, $this->sectionManager); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @param FieldConfig[] $fields |
180
|
|
|
* @return int the number of fields created |
181
|
|
|
*/ |
182
|
|
|
private function createFields(array $fields): int |
183
|
|
|
{ |
184
|
|
|
return $this->createOrUpdateConfigs($fields, $this->fieldManager); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* @param ApplicationConfig[] $applications |
189
|
|
|
* @return int the number of applications created |
190
|
|
|
*/ |
191
|
|
|
private function createApplications(array $applications): int |
192
|
|
|
{ |
193
|
|
|
return $this->createOrUpdateConfigs($applications, $this->applicationManager); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* @param ConfigWithHandleInterface[] $configs |
198
|
|
|
* @param ApplicationManagerInterface|FieldManagerInterface|SectionManagerInterface $manager |
199
|
|
|
* @return int |
200
|
|
|
*/ |
201
|
|
|
private function createOrUpdateConfigs(array $configs, $manager): int |
202
|
|
|
{ |
203
|
|
|
foreach ($configs as $config) { |
204
|
|
|
try { |
205
|
|
|
$handle = $config->getHandle(); |
206
|
|
|
$object = $manager->readByHandle($handle); |
207
|
|
|
$manager->updateByConfig($config, $object); |
|
|
|
|
208
|
|
|
} catch (NotFoundException $exception) { |
209
|
|
|
$manager->createByConfig($config); |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
return count($configs); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* @param LanguageConfig $languages |
217
|
|
|
*/ |
218
|
|
|
private function createLanguages(LanguageConfig $languages): void |
219
|
|
|
{ |
220
|
|
|
$this->languageManager->createByConfig($languages); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @param string $fileName |
225
|
|
|
* @param mixed $config |
226
|
|
|
* @throws \Exception |
227
|
|
|
*/ |
228
|
|
|
private function classifyFile(string $fileName, $config): void |
229
|
|
|
{ |
230
|
|
|
if (!is_array($config)) { |
231
|
|
|
throw new \Exception("Malformed file $fileName"); |
232
|
|
|
} |
233
|
|
|
$keys = array_keys($config); |
234
|
|
|
if (count($keys) !== 1) { |
235
|
|
|
throw new \Exception("Malformed file $fileName"); |
236
|
|
|
} |
237
|
|
|
[$key] = $keys; |
238
|
|
|
switch ($key) { |
239
|
|
|
case 'field': |
240
|
|
|
$this->fields[] = FieldConfig::fromArray($config); |
241
|
|
|
break; |
242
|
|
|
case 'section': |
243
|
|
|
$this->sections[] = SectionConfig::fromArray($config); |
244
|
|
|
break; |
245
|
|
|
case 'application': |
246
|
|
|
$this->applications[] = ApplicationConfig::fromArray($config); |
247
|
|
|
break; |
248
|
|
|
case 'language': |
249
|
|
|
if (!is_nulL($this->languages)) { |
250
|
|
|
throw new \Exception("Found multiple language config files"); |
251
|
|
|
} |
252
|
|
|
$this->languages = LanguageConfig::fromArray($config); |
253
|
|
|
break; |
254
|
|
|
default: |
255
|
|
|
throw new \Exception("Could not identify file $fileName with key $key"); |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Get the contents of all yaml files in a directory, recursively. |
261
|
|
|
* @param string $directory |
262
|
|
|
* @return \Generator |
263
|
|
|
*/ |
264
|
|
|
private static function getAllYamls(string $directory): \Generator |
265
|
|
|
{ |
266
|
|
|
/** @var \SplFileInfo $file */ |
267
|
|
|
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory)) as $file) { |
268
|
|
|
if ($file->isFile() && $file->getExtension() === 'yml') { |
269
|
|
|
$fileName = $file->getPathname(); |
270
|
|
|
yield $fileName => Yaml::parse(file_get_contents($fileName)); |
271
|
|
|
} |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|