1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* (c) Kitodo. Key to digital objects e.V. <[email protected]> |
5
|
|
|
* |
6
|
|
|
* This file is part of the Kitodo and TYPO3 projects. |
7
|
|
|
* |
8
|
|
|
* @license GNU General Public License version 3 or later. |
9
|
|
|
* For the full copyright and license information, please read the |
10
|
|
|
* LICENSE.txt file that was distributed with this source code. |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace Kitodo\DbDocs; |
14
|
|
|
|
15
|
|
|
use Doctrine\DBAL\Schema\Table; |
16
|
|
|
use Kitodo\Dlf\Common\Helper; |
17
|
|
|
use ReflectionClass; |
18
|
|
|
use TYPO3\CMS\Core\Database\Schema\Parser\Parser; |
19
|
|
|
use TYPO3\CMS\Core\Database\Schema\SqlReader; |
20
|
|
|
use TYPO3\CMS\Core\Localization\LanguageService; |
21
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
22
|
|
|
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager; |
23
|
|
|
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; |
24
|
|
|
use TYPO3\CMS\Extbase\Object\ObjectManager; |
25
|
|
|
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Aggregates information about database tables and generates an .rst reference page. |
29
|
|
|
* |
30
|
|
|
* @author Kajetan Dvoracek <[email protected]> |
31
|
|
|
* @package TYPO3 |
32
|
|
|
* @subpackage dlf |
33
|
|
|
* @access public |
34
|
|
|
*/ |
35
|
|
|
class Generator |
36
|
|
|
{ |
37
|
|
|
/** |
38
|
|
|
* @var ObjectManager |
39
|
|
|
*/ |
40
|
|
|
protected $objectManager; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var LanguageService |
44
|
|
|
*/ |
45
|
|
|
protected $languageService; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var DataMapper |
49
|
|
|
*/ |
50
|
|
|
protected $dataMapper; |
51
|
|
|
|
52
|
|
|
public function __construct() |
53
|
|
|
{ |
54
|
|
|
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class); |
55
|
|
|
$this->languageService = $this->objectManager->get(LanguageService::class); |
56
|
|
|
$this->dataMapper = $this->objectManager->get(DataMapper::class); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Collect information about relevant tables from `ext_tables.sql` and the |
61
|
|
|
* Extbase classmap. |
62
|
|
|
*/ |
63
|
|
|
public function collectTables(): array |
64
|
|
|
{ |
65
|
|
|
$sqlReader = $this->objectManager->get(SqlReader::class); |
|
|
|
|
66
|
|
|
$sqlCode = $sqlReader->getTablesDefinitionString(true); |
67
|
|
|
$createTableStatements = $sqlReader->getCreateTableStatementArray($sqlCode); |
68
|
|
|
|
69
|
|
|
$tableToClassName = $this->getTableClassMap(); |
70
|
|
|
|
71
|
|
|
$result = []; |
72
|
|
|
|
73
|
|
|
foreach ($createTableStatements as $statement) { |
74
|
|
|
$parser = new Parser($statement); |
75
|
|
|
list($table) = $parser->parse(); |
76
|
|
|
|
77
|
|
|
$tableName = $table->getName(); |
78
|
|
|
if (!str_starts_with($tableName, 'tx_dlf_')) { |
79
|
|
|
continue; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
$className = $tableToClassName[$tableName] ?? null; |
83
|
|
|
|
84
|
|
|
$result[] = $this->getTableInfo($table, $className); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
return $result; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Get a map from database table names to their domain model class names. |
92
|
|
|
*/ |
93
|
|
|
public function getTableClassMap(): array |
94
|
|
|
{ |
95
|
|
|
Helper::polyfillExtbaseClassesForTYPO3v9(); |
|
|
|
|
96
|
|
|
|
97
|
|
|
$configurationManager = $this->objectManager->get(ConfigurationManager::class); |
|
|
|
|
98
|
|
|
$frameworkConfiguration = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); |
99
|
|
|
|
100
|
|
|
$result = []; |
101
|
|
|
|
102
|
|
|
foreach ($frameworkConfiguration['persistence']['classes'] as $className => $tableConf) { |
103
|
|
|
$tableName = $tableConf['mapping']['tableName']; |
104
|
|
|
$result[$tableName] = $className; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
return $result; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Collect information about a single table. |
112
|
|
|
* |
113
|
|
|
* @param Table $table The table to be analyzed |
114
|
|
|
* @param string|null $className Fully qualified name of the domain model class |
115
|
|
|
*/ |
116
|
|
|
protected function getTableInfo(Table $table, ?string $className): object |
117
|
|
|
{ |
118
|
|
|
$tableName = $table->getName(); |
119
|
|
|
|
120
|
|
|
$isPrimary = []; |
121
|
|
|
if (!is_null($primaryKey = $table->getPrimaryKey())) { |
122
|
|
|
foreach ($primaryKey->getUnquotedColumns() as $primaryColumn) { |
123
|
|
|
$isPrimary[$primaryColumn] = true; |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
$columns = []; |
128
|
|
|
foreach ($table->getColumns() as $column) { |
129
|
|
|
$columnName = $column->getName(); |
130
|
|
|
|
131
|
|
|
$columns[$columnName] = (object) [ |
132
|
|
|
'name' => $columnName, |
133
|
|
|
'type' => $column->getType(), |
134
|
|
|
'isPrimary' => isset($isPrimary[$columnName]), |
135
|
|
|
'sqlComment' => $column->getComment() ?? '', |
136
|
|
|
'fieldComment' => '', |
137
|
|
|
'feComment' => $this->languageService->sL($GLOBALS['TCA'][$tableName]['columns'][$columnName]['label'] ?? ''), |
138
|
|
|
]; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
$result = (object) [ |
142
|
|
|
'name' => $tableName, |
143
|
|
|
'columns' => $columns, |
144
|
|
|
'modelClass' => null, |
145
|
|
|
'sqlComment' => $table->getComment() ?? '', |
146
|
|
|
'classComment' => '', |
147
|
|
|
'feComment' => $this->languageService->sL($GLOBALS['TCA'][$tableName]['ctrl']['title'] ?? ''), |
148
|
|
|
]; |
149
|
|
|
|
150
|
|
|
// Integrate doc-comments from model class and its fields |
151
|
|
|
if ($className !== null) { |
152
|
|
|
$reflection = new ReflectionClass($className); |
153
|
|
|
|
154
|
|
|
$dataMap = $this->dataMapper->getDataMap($className); |
155
|
|
|
|
156
|
|
|
foreach ($reflection->getProperties() as $property) { |
157
|
|
|
$column = $dataMap->getColumnMap($property->getName()); |
158
|
|
|
if ($column !== null) { |
159
|
|
|
$result->columns[$column->getColumnName()]->fieldComment = $this->parseDocComment($property->getDocComment()); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$result->modelClass = $className; |
164
|
|
|
$result->classComment = $this->parseDocComment($reflection->getDocComment()); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
return $result; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
protected function parseDocComment($docComment) |
171
|
|
|
{ |
172
|
|
|
// TODO: Consider using phpDocumentor (though that splits the docblock into summary and description) |
173
|
|
|
|
174
|
|
|
// Adopted from DocCommentParser in TYPO3 v9 |
175
|
|
|
// https://github.com/TYPO3/typo3/blob/57944c8c5add00f0e8a1a5e1d07f30a8f20a8201/typo3/sysext/extbase/Classes/Reflection/DocCommentParser.php |
176
|
|
|
$text = ''; |
177
|
|
|
$lines = explode("\n", $docComment); |
178
|
|
|
foreach ($lines as $line) { |
179
|
|
|
// Stop parsing at first tag |
180
|
|
|
if ($line !== '' && strpos($line, '@') !== false) { |
181
|
|
|
break; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
// There may be a single non-signifying space after the doc-comment asterisk, |
185
|
|
|
// which is not included. |
186
|
|
|
$text .= preg_replace('#\\s*/?[*/]*\\s?(.*)$#', '$1', $line) . "\n"; |
187
|
|
|
} |
188
|
|
|
$text = trim($text); |
189
|
|
|
|
190
|
|
|
return $text; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Transform table structure into .rst page. |
195
|
|
|
*/ |
196
|
|
|
public function generatePage(array $tables) |
197
|
|
|
{ |
198
|
|
|
$page = new RstSection(); |
199
|
|
|
$page->setHeader('Database Tables'); |
200
|
|
|
$page->addText(<<<RST |
201
|
|
|
This is a reference of all database tables defined by Kitodo.Presentation. |
202
|
|
|
|
203
|
|
|
.. tip:: This page is auto-generated. If you would like to edit it, please use doc-comments in the model class, COMMENT fields in ``ext_tables.sql`` if the table does not have one, or TCA labels. Then, you may re-generate the page by running ``composer docs:db`` inside the Kitodo.Presentation base folder. |
204
|
|
|
RST); |
205
|
|
|
|
206
|
|
|
// Sort tables alphabetically |
207
|
|
|
usort($tables, function ($lhs, $rhs) { |
208
|
|
|
return $lhs->name <=> $rhs->name; |
209
|
|
|
}); |
210
|
|
|
|
211
|
|
|
foreach ($tables as $tableInfo) { |
212
|
|
|
$section = $page->subsection(); |
213
|
|
|
|
214
|
|
|
// Set header |
215
|
|
|
$header = $tableInfo->name; |
216
|
|
|
if (!empty($tableInfo->feComment)) { |
217
|
|
|
$header .= ': ' . $tableInfo->feComment; |
218
|
|
|
} |
219
|
|
|
$section->setHeader($header); |
220
|
|
|
|
221
|
|
|
// Set introductory text of subsection |
222
|
|
|
if ($tableInfo->modelClass) { |
223
|
|
|
$section->addText('Extbase domain model: ``' . $tableInfo->modelClass . '``'); |
224
|
|
|
} |
225
|
|
|
$section->addText($tableInfo->classComment); |
226
|
|
|
$section->addText($tableInfo->sqlComment); |
227
|
|
|
|
228
|
|
|
// Generate main table |
229
|
|
|
$header = [[ |
230
|
|
|
'field' => 'Field', |
231
|
|
|
'description' => 'Description', |
232
|
|
|
]]; |
233
|
|
|
|
234
|
|
|
$rows = array_map(function ($column) use ($page) { |
235
|
|
|
return [ |
236
|
|
|
'field' => ( |
237
|
|
|
$page->format($column->name, ['bold' => $column->isPrimary]) |
238
|
|
|
. "\u{00a0}\u{00a0}" |
239
|
|
|
. $page->format($column->type->getName(), ['italic' => true]) |
240
|
|
|
), |
241
|
|
|
|
242
|
|
|
'description' => $page->paragraphs([ |
243
|
|
|
$page->format($column->feComment, ['italic' => true]), |
244
|
|
|
$column->fieldComment, |
245
|
|
|
$column->sqlComment, |
246
|
|
|
]), |
247
|
|
|
]; |
248
|
|
|
}, $tableInfo->columns); |
249
|
|
|
|
250
|
|
|
$section->addTable($rows, $header); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
return $page; |
254
|
|
|
} |
255
|
|
|
} |
256
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.