1
|
|
|
<?php |
2
|
|
|
namespace keeko\tools\services; |
3
|
|
|
|
4
|
|
|
use gossi\codegen\generator\CodeFileGenerator; |
5
|
|
|
use gossi\codegen\model\AbstractPhpStruct; |
6
|
|
|
use gossi\docblock\tags\AuthorTag; |
7
|
|
|
use keeko\framework\schema\CodegenSchema; |
8
|
|
|
use keeko\framework\schema\PackageSchema; |
9
|
|
|
use keeko\tools\utils\NamespaceResolver; |
10
|
|
|
use phootwork\file\File; |
11
|
|
|
use phootwork\file\Path; |
12
|
|
|
use Propel\Generator\Model\Table; |
13
|
|
|
|
14
|
|
|
class CodeGeneratorService extends AbstractService { |
15
|
|
|
|
16
|
|
|
private $codegen; |
17
|
|
|
|
18
|
|
|
public function getCodegenFile() { |
19
|
7 |
|
$basepath = dirname($this->project->getComposerFileName()); |
20
|
7 |
|
return $basepath . '/codegen.json'; |
21
|
7 |
|
} |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Loads the contents from codegen.json into a collection |
25
|
|
|
* |
26
|
|
|
* @throws JsonEmptyException |
27
|
|
|
* @throws \RuntimeException |
28
|
|
|
* @return CodegenSchema |
29
|
|
|
*/ |
30
|
|
|
public function getCodegen() { |
31
|
12 |
|
if ($this->codegen === null) { |
32
|
7 |
|
$file = new File($this->getCodegenFile()); |
33
|
7 |
|
$this->codegen = $file->exists() |
34
|
7 |
|
? CodegenSchema::fromFile($this->getCodegenFile()) |
35
|
|
|
: new CodegenSchema(); |
36
|
7 |
|
} |
37
|
12 |
|
|
38
|
|
|
return $this->codegen; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
// /** |
42
|
|
|
// * Returns the codegen part for the given action name or an empty map |
43
|
|
|
// * |
44
|
|
|
// * @param string $name |
45
|
|
|
// * @return Map |
46
|
|
|
// */ |
47
|
|
|
// public function getCodegenAction($name) { |
48
|
|
|
// $codegen = $this->getCodegen(); |
49
|
|
|
|
50
|
|
|
// if (isset($codegen['actions'])) { |
|
|
|
|
51
|
|
|
// $actions = $codegen['actions']; |
52
|
|
|
|
53
|
|
|
// if (isset($actions[$name])) { |
|
|
|
|
54
|
|
|
// return $actions[$name]; |
55
|
|
|
// } |
56
|
|
|
// } |
57
|
|
|
|
58
|
|
|
// return null; |
|
|
|
|
59
|
|
|
// } |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Adds authors to the docblock of the given struct |
63
|
|
|
* |
64
|
|
|
* @param AbstractPhpStruct $struct |
65
|
15 |
|
* @param array $package |
66
|
15 |
|
*/ |
67
|
|
|
public function addAuthors(AbstractPhpStruct $struct, PackageSchema $package) { |
68
|
15 |
|
$docblock = $struct->getDocblock(); |
69
|
|
|
|
70
|
|
|
foreach ($package->getAuthors() as $author) { |
71
|
15 |
|
/* @var $author AuthorSchema */ |
72
|
15 |
|
$tag = AuthorTag::create()->setName($author->getName()); |
73
|
15 |
|
$mail = $author->getEmail(); |
74
|
|
|
|
75
|
15 |
|
if (!empty($mail)) { |
76
|
|
|
$tag->setEmail($mail); |
77
|
15 |
|
} |
78
|
|
|
|
79
|
|
|
$docblock->appendTag($tag); |
80
|
|
|
} |
81
|
15 |
|
} |
82
|
15 |
|
|
83
|
15 |
|
/** |
84
|
|
|
* Returns code for hydrating a propel model |
85
|
|
|
* |
86
|
|
|
* @param string $modelName |
87
|
|
|
* @return string |
88
|
|
|
*/ |
89
|
|
|
public function getWriteFields($modelName) { |
90
|
|
|
$codegen = $this->getCodegen(); |
91
|
5 |
|
$filter = $codegen->getWriteFilter($modelName); |
92
|
5 |
|
$model = $this->modelService->getModel($modelName); |
93
|
5 |
|
$computed = $this->getComputedFields($model); |
|
|
|
|
94
|
5 |
|
$filter = array_merge($filter, $computed); |
95
|
5 |
|
|
96
|
5 |
|
$fields = []; |
97
|
5 |
|
$cols = $model->getColumns(); |
98
|
|
|
foreach ($cols as $col) { |
99
|
5 |
|
$prop = $col->getName(); |
100
|
5 |
|
|
101
|
5 |
|
if (!in_array($prop, $filter)) { |
102
|
5 |
|
$fields[] = $prop; |
103
|
|
|
} |
104
|
5 |
|
} |
105
|
5 |
|
|
106
|
|
|
return $fields; |
107
|
5 |
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Returns the fields for a model |
111
|
5 |
|
* |
112
|
5 |
|
* @param string $modelName |
113
|
5 |
|
* @return array |
114
|
|
|
*/ |
115
|
5 |
|
public function getReadFields($modelName) { |
116
|
5 |
|
$codegen = $this->getCodegen(); |
117
|
5 |
|
$model = $this->modelService->getModel($modelName); |
118
|
|
|
// $computed = $this->getComputedFields($model); |
|
|
|
|
119
|
5 |
|
$filter = $codegen->getReadFilter($modelName); |
120
|
|
|
// $filter = array_merge($filter, $computed); |
|
|
|
|
121
|
|
|
|
122
|
|
|
$fields = []; |
123
|
|
|
$cols = $model->getColumns(); |
124
|
|
|
foreach ($cols as $col) { |
125
|
|
|
$prop = $col->getName(); |
126
|
|
|
|
127
|
|
|
if (!in_array($prop, $filter)) { |
128
|
|
|
$fields[] = $prop; |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
return $fields; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
// /** |
136
|
|
|
// * Returns conversions for model columns |
137
|
|
|
// * |
138
|
|
|
// * @param string $model |
139
|
|
|
// * @param string $type |
140
|
|
|
// * @return array |
141
|
|
|
// */ |
142
|
|
|
// public function getConversions($model, $type) { |
143
|
|
|
// return $this->getActionProp($model, $type, 'conversion'); |
144
|
|
|
// } |
145
|
|
|
|
146
|
|
|
// /** |
147
|
|
|
// * Returns model columns that should be filtered |
148
|
|
|
// * |
149
|
|
|
// * @param string $model |
150
|
5 |
|
// * @param string $type |
151
|
5 |
|
// * @return array |
152
|
|
|
// */ |
153
|
|
|
// public function getFilter($model, $type) { |
154
|
5 |
|
// return $this->getActionProp($model, $type, 'filter'); |
155
|
5 |
|
// } |
156
|
5 |
|
|
157
|
5 |
|
/** |
158
|
5 |
|
* Returns computed model fields |
159
|
5 |
|
* |
160
|
|
|
* @param Table $table |
161
|
5 |
|
* @return array<string> |
162
|
|
|
*/ |
163
|
|
|
public function getComputedFields(Table $table) { |
164
|
5 |
|
$fields = []; |
165
|
5 |
|
|
166
|
|
|
// iterate over behaviors to get their respective columns |
167
|
5 |
|
foreach ($table->getBehaviors() as $behavior) { |
168
|
|
|
switch ($behavior->getName()) { |
169
|
|
|
case 'timestampable': |
170
|
|
|
$fields[] = $behavior->getParameter('create_column'); |
171
|
|
|
$fields[] = $behavior->getParameter('update_column'); |
172
|
|
|
break; |
173
|
|
|
|
174
|
|
|
case 'aggregate_column': |
175
|
|
|
$fields[] = $behavior->getParameter('name'); |
176
|
2 |
|
break; |
177
|
2 |
|
} |
178
|
2 |
|
} |
179
|
|
|
|
180
|
2 |
|
return $fields; |
181
|
|
|
} |
182
|
2 |
|
|
183
|
|
|
/** |
184
|
|
|
* Helper to represent an array as php code |
185
|
|
|
* |
186
|
2 |
|
* @param array $array |
187
|
|
|
* @return string |
188
|
|
|
*/ |
189
|
15 |
|
public function arrayToCode(array $array) { |
190
|
15 |
|
$fields = ''; |
191
|
15 |
|
foreach ($array as $item) { |
192
|
|
|
$fields .= sprintf("'%s', ", $item); |
193
|
15 |
|
} |
194
|
|
|
|
195
|
|
|
if (strlen($fields) > 0) { |
196
|
|
|
$fields = substr($fields, 0, -2); |
197
|
15 |
|
} |
198
|
15 |
|
|
199
|
15 |
|
return sprintf('[%s]', $fields); |
200
|
15 |
|
} |
201
|
15 |
|
|
202
|
|
|
// public function mapToCode(array $array) { |
|
|
|
|
203
|
|
|
// $fields = ''; |
204
|
15 |
|
// foreach ($array as $k => $item) { |
205
|
15 |
|
// $fields .= sprintf("\t'%s' => %s,\n", $k, $this->arrayToCode($item)); |
206
|
15 |
|
// } |
207
|
|
|
|
208
|
15 |
|
// if (strlen($fields) > 0) { |
|
|
|
|
209
|
|
|
// $fields = substr($fields, 0, -2); |
210
|
15 |
|
// } |
211
|
15 |
|
|
212
|
|
|
// return sprintf("[\n%s\n]", $fields); |
|
|
|
|
213
|
|
|
// } |
214
|
15 |
|
|
215
|
|
|
/** |
216
|
|
|
* Returns the filename for a given struct |
217
|
15 |
|
* |
218
|
15 |
|
* @param AbstractPhpStruct $struct |
219
|
15 |
|
* @return string |
220
|
|
|
*/ |
221
|
|
|
public function getFilename(AbstractPhpStruct $struct) { |
222
|
|
|
$package = $this->packageService->getPackage(); |
223
|
|
|
$relativeSourcePath = NamespaceResolver::getSourcePath($struct->getNamespace(), $package); |
224
|
|
|
|
225
|
|
|
if ($relativeSourcePath === null) { |
226
|
|
|
return null; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
$jsonFile = $this->project->getComposerFileName(); |
230
|
|
|
$path = new Path(dirname($jsonFile)); |
231
|
|
|
$path = $path->append($relativeSourcePath); |
232
|
|
|
$path = $path->append($struct->getName() . '.php'); |
233
|
|
|
return $path->toString(); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
public function dumpStruct(AbstractPhpStruct $struct, $overwrite = false) { |
237
|
|
|
$filename = $this->getFilename($struct); |
238
|
|
|
$file = new File($filename); |
239
|
|
|
|
240
|
|
|
if ($filename !== null && $file->exists() ? $overwrite : true) { |
241
|
|
|
// generate code |
242
|
|
|
$generator = new CodeFileGenerator(); |
243
|
|
|
$code = $generator->generate($struct); |
|
|
|
|
244
|
|
|
|
245
|
|
|
// write code to file |
246
|
|
|
$file->write($code); |
247
|
|
|
|
248
|
|
|
// tell user about |
249
|
|
|
$this->io->writeln(sprintf('Class <info>%s</info> written at <info>%s</info>', $struct->getQualifiedName(), $filename)); |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.