SmartObjectInformationService   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 360
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 10
dl 0
loc 360
rs 9.6
c 0
b 0
f 0
ccs 0
cts 131
cp 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getInstance() 0 4 1
A getDatabaseInformation() 0 13 2
B getCustomModelFieldTca() 0 42 5
C getTcaInformation() 0 86 9
A useTableNameFileBase() 0 6 2
A getCustomDatabaseInformation() 0 19 4
A getDatabaseMappingByVarType() 0 7 1
A getCustomModelFields() 0 31 4
A generateSqlQuery() 0 8 2
A generateCompleteSqlQuery() 0 43 3
A getDataSet() 0 4 1
A getTcaTitle() 0 4 1
1
<?php
2
3
/**
4
 * SmartObjectInformationService.php.
5
 */
6
declare(strict_types = 1);
7
8
namespace HDNET\Autoloader\Service;
9
10
use Doctrine\Common\Annotations\AnnotationReader;
11
use HDNET\Autoloader\Annotation\DatabaseField;
12
use HDNET\Autoloader\Annotation\DatabaseKey;
13
use HDNET\Autoloader\Annotation\EnableRichText;
14
use HDNET\Autoloader\DataSet;
15
use HDNET\Autoloader\Mapper;
16
use HDNET\Autoloader\Utility\ArrayUtility;
17
use HDNET\Autoloader\Utility\ClassNamingUtility;
18
use HDNET\Autoloader\Utility\ExtendedUtility;
19
use HDNET\Autoloader\Utility\IconUtility;
20
use HDNET\Autoloader\Utility\ModelUtility;
21
use HDNET\Autoloader\Utility\TranslateUtility;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24
/**
25
 * SmartObjectInformationService.
26
 */
27
class SmartObjectInformationService
28
{
29
    /**
30
     * Get a instance of this object.
31
     *
32
     * @return \HDNET\Autoloader\Service\SmartObjectInformationService
33
     */
34
    public static function getInstance()
35
    {
36
        return GeneralUtility::makeInstance(self::class);
37
    }
38
39
    /**
40
     * Get database information.
41
     *
42
     * @param $modelClassName
43
     *
44
     * @return string
45
     */
46
    public function getDatabaseInformation($modelClassName)
47
    {
48
        $tableName = ModelUtility::getTableName($modelClassName);
49
        $custom = $this->getCustomDatabaseInformation($modelClassName);
50
51
        // disable complete table generation
52
        // for extending existing tables
53
        if ('' !== ModelUtility::getTableNameByModelReflectionAnnotation($modelClassName)) {
54
            return $this->generateSqlQuery($tableName, $custom);
55
        }
56
57
        return $this->generateCompleteSqlQuery($modelClassName, $tableName, $custom);
58
    }
59
60
    /**
61
     * Get the custom Model field TCA structure.
62
     *
63
     * @param       $modelClassName
64
     * @param array $searchFields
65
     *
66
     * @return array
67
     */
68
    public function getCustomModelFieldTca($modelClassName, &$searchFields = [])
69
    {
70
        $modelInformation = ClassNamingUtility::explodeObjectModelName($modelClassName);
71
        $extensionName = GeneralUtility::camelCaseToLowerCaseUnderscored($modelInformation['extensionName']);
72
        $tableName = ModelUtility::getTableName($modelClassName);
73
        $customFieldInfo = $this->getCustomModelFields($modelClassName);
74
        $searchFields = [];
75
        $customFields = [];
76
        foreach ($customFieldInfo as $info) {
77
            $key = $tableName . '.' . $info['name'];
78
79
            if ($this->useTableNameFileBase()) {
80
                // Without prefix !
81
                $key = $info['name'];
82
            }
83
84
            try {
85
                TranslateUtility::assureLabel($key, $extensionName, $info['name'], null, $tableName);
86
                $label = TranslateUtility::getLllOrHelpMessage($key, $extensionName, $tableName);
87
            } catch (\Exception $ex) {
88
                $label = $info['name'];
89
            }
90
91
            /** @var Mapper $mapper */
92
            $mapper = ExtendedUtility::create(Mapper::class);
93
            $field = $mapper->getTcaConfiguration(trim($info['var'], '\\'), $info['name'], $label);
94
95
            // RTE
96
            if ($info['rte']) {
97
                $field['config']['type'] = 'text';
98
                $field['config']['enableRichtext'] = '1';
99
                $field['config']['richtextConfiguration'] = 'default';
100
                $field['config']['softref'] = 'typolink_tag,email[subst],url';
101
                $field['defaultExtras'] = 'richtext:rte_transform[flag=rte_enabled|mode=ts_css]';
102
            }
103
104
            $searchFields[] = $info['name'];
105
            $customFields[$info['name']] = $field;
106
        }
107
108
        return $customFields;
109
    }
110
111
    /**
112
     * Pre build TCA information for the given model.
113
     *
114
     * @param string $modelClassName
115
     *
116
     * @return array
117
     */
118
    public function getTcaInformation($modelClassName)
119
    {
120
        $modelInformation = ClassNamingUtility::explodeObjectModelName($modelClassName);
121
        $extensionName = GeneralUtility::camelCaseToLowerCaseUnderscored($modelInformation['extensionName']);
122
        $reflectionTableName = ModelUtility::getTableNameByModelReflectionAnnotation($modelClassName);
123
        $tableName = ModelUtility::getTableNameByModelName($modelClassName);
124
125
        $searchFields = [];
126
        $customFields = $this->getCustomModelFieldTca($modelClassName, $searchFields);
127
128
        if ('' !== $reflectionTableName) {
129
            $customConfiguration = [
130
                'columns' => $customFields,
131
            ];
132
            $base = \is_array($GLOBALS['TCA'][$reflectionTableName]) ? $GLOBALS['TCA'][$reflectionTableName] : [];
133
134
            return ArrayUtility::mergeRecursiveDistinct($base, $customConfiguration);
135
        }
136
137
        $excludes = ModelUtility::getSmartExcludesByModelName($modelClassName);
138
139
        $dataSet = $this->getDataSet();
140
        $dataImplementations = $dataSet->getAllAndExcludeList($excludes);
141
        $baseTca = $dataSet->getTcaInformation($dataImplementations, $tableName);
142
143
        // title
144
        $fields = array_keys($customFields);
145
        $labelField = 'title';
146
        if (!\in_array($labelField, $fields, true)) {
147
            $labelField = $fields[0];
148
        }
149
150
        try {
151
            TranslateUtility::assureLabel($tableName, $extensionName, null, null, $tableName);
152
        } catch (\Exception $ex) {
153
            // Do not handle the error of the assureLabel method
154
        }
155
        if (!\is_array($baseTca['columns'])) {
156
            $baseTca['columns'] = [];
157
        }
158
        $baseTca['columns'] = ArrayUtility::mergeRecursiveDistinct($baseTca['columns'], $customFields);
159
160
        // items
161
        $showitem = $fields;
162
        if (!\in_array('language', $excludes, true)) {
163
            $showitem[] = '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,--palette--;;language';
164
        }
165
166
        if (!\in_array('workspaces', $excludes, true)) {
167
            $baseTca['ctrl']['shadowColumnsForNewPlaceholders'] .= ',' . $labelField;
168
        }
169
170
        $languagePrefix = 'LLL:EXT:frontend/Resources/Private/Language/';
171
        $languagePrefixCore = 'LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf';
172
        if (!\in_array('enableFields', $excludes, true)) {
173
            $showitem[] = '--div--;' . $languagePrefixCore . ':access';
174
            $showitem[] = '--palette--;' . $languagePrefix . 'locallang_tca.xlf:pages.palettes.access;access';
175
        }
176
        $showitem[] = '--div--;' . $languagePrefix . 'locallang_ttc.xlf:tabs.extended';
177
178
        $overrideTca = [
179
            'ctrl' => [
180
                'title' => $this->getTcaTitle($tableName, $extensionName),
181
                'label' => $labelField,
182
                'tstamp' => 'tstamp',
183
                'crdate' => 'crdate',
184
                'cruser_id' => 'cruser_id',
185
                'dividers2tabs' => true,
186
                'sortby' => 'sorting',
187
                'delete' => 'deleted',
188
                'searchFields' => implode(',', $searchFields),
189
                'iconfile' => IconUtility::getByModelName($modelClassName, true),
190
            ],
191
            'interface' => [
192
                'showRecordFieldList' => implode(',', array_keys($baseTca['columns'])),
193
            ],
194
            'types' => [
195
                '1' => ['showitem' => implode(',', $showitem)],
196
            ],
197
            'palettes' => [
198
                'access' => ['showitem' => 'starttime, endtime, --linebreak--, hidden, editlock, --linebreak--, fe_group'],
199
            ],
200
        ];
201
202
        return ArrayUtility::mergeRecursiveDistinct($baseTca, $overrideTca);
203
    }
204
205
    /**
206
     * Check if table name file base is used.
207
     *
208
     * @return bool
209
     */
210
    protected function useTableNameFileBase()
211
    {
212
        $configuration = unserialize((string)$GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['autoloader']);
213
214
        return isset($configuration['enableLanguageFileOnTableBase']) ? (bool)$configuration['enableLanguageFileOnTableBase'] : false;
215
    }
216
217
    /**
218
     * Get custom database information for the given model.
219
     *
220
     * @param string $modelClassName
221
     *
222
     * @return array
223
     */
224
    protected function getCustomDatabaseInformation($modelClassName)
225
    {
226
        $fieldInformation = $this->getCustomModelFields($modelClassName);
227
        $fields = [];
228
        foreach ($fieldInformation as $info) {
229
            if ('' === $info['db']) {
230
                $info['db'] = $this->getDatabaseMappingByVarType($info['var']);
231
            } else {
232
                try {
233
                    $info['db'] = $this->getDatabaseMappingByVarType($info['db']);
234
                } catch (\Exception $ex) {
235
                    // Do not handle the getDatabaseMappingByVarType by db, Fallback is the var call
236
                }
237
            }
238
            $fields[] = '`' . $info['name'] . '` ' . $info['db'];
239
        }
240
241
        return $fields;
242
    }
243
244
    /**
245
     * Get the right mapping.
246
     *
247
     * @param $var
248
     *
249
     * @throws \HDNET\Autoloader\Exception
250
     *
251
     * @return string
252
     */
253
    protected function getDatabaseMappingByVarType($var)
254
    {
255
        /** @var Mapper $mapper */
256
        $mapper = ExtendedUtility::create(Mapper::class);
257
258
        return $mapper->getDatabaseDefinition($var);
259
    }
260
261
    /**
262
     * Get custom database information for the given model.
263
     */
264
    protected function getCustomModelFields(string $modelClassName): array
265
    {
266
        /** @var AnnotationReader $annotationReader */
267
        $annotationReader = GeneralUtility::makeInstance(AnnotationReader::class);
268
269
        $reflectionClass = new \ReflectionClass($modelClassName);
270
        $properties = [];
271
        foreach ($reflectionClass->getProperties() as $property) {
272
            $propertiesCheck = $annotationReader->getPropertyAnnotation($property, DatabaseField::class);
273
            if (null !== $propertiesCheck) {
274
                $properties[$property->getName()] = $propertiesCheck;
275
            }
276
        }
277
278
        $tableName = ModelUtility::getTableName($modelClassName);
279
        $nameMapperService = GeneralUtility::makeInstance(NameMapperService::class);
280
        $fields = [];
281
282
        foreach ($properties as $name => $annotation) {
283
            $var = (string)$annotation->type;
284
            $fields[] = [
285
                'property' => $name,
286
                'name' => $nameMapperService->getDatabaseFieldName($tableName, $name),
287
                'db' => trim((string)$annotation->sql),
288
                'var' => trim((string)$var),
289
                'rte' => null !== $annotationReader->getPropertyAnnotation($reflectionClass->getProperty($name), EnableRichText::class),
290
            ];
291
        }
292
293
        return $fields;
294
    }
295
296
    /**
297
     * Generate SQL Query.
298
     *
299
     * @param string $tableName
300
     *
301
     * @return string
302
     */
303
    protected function generateSqlQuery($tableName, array $fields)
304
    {
305
        if (empty($fields)) {
306
            return '';
307
        }
308
309
        return LF . 'CREATE TABLE ' . $tableName . ' (' . LF . implode(',' . LF, $fields) . LF . ');' . LF;
310
    }
311
312
    /**
313
     * Generate complete SQL Query.
314
     *
315
     * @param string $modelClassName
316
     * @param string $tableName
317
     *
318
     * @return string
319
     */
320
    protected function generateCompleteSqlQuery($modelClassName, $tableName, array $custom)
321
    {
322
        $fields = [];
323
        $fields[] = 'uid int(11) NOT NULL auto_increment';
324
        $fields[] = 'pid int(11) DEFAULT \'0\' NOT NULL';
325
        $fields[] = 'tstamp int(11) unsigned DEFAULT \'0\' NOT NULL';
326
        $fields[] = 'crdate int(11) unsigned DEFAULT \'0\' NOT NULL';
327
        $fields[] = 'cruser_id int(11) unsigned DEFAULT \'0\' NOT NULL';
328
        $fields[] = 'deleted tinyint(4) unsigned DEFAULT \'0\' NOT NULL';
329
        $fields[] = 'sorting int(11) DEFAULT \'0\' NOT NULL';
330
331
        foreach ($custom as $field) {
332
            $fields[] = $field;
333
        }
334
335
        $excludes = ModelUtility::getSmartExcludesByModelName($modelClassName);
336
        $dataSet = $this->getDataSet();
337
        $dataImplementations = $dataSet->getAllAndExcludeList($excludes);
338
339
        // add data set fields
340
        $fields = array_merge($fields, $dataSet->getDatabaseSqlInformation($dataImplementations, $tableName));
341
342
        // default keys
343
        $fields[] = 'PRIMARY KEY (uid)';
344
        $fields[] = 'KEY parent (pid)';
345
346
        // add custom keys set by @key annotations
347
348
        $annotationReader = GeneralUtility::makeInstance(AnnotationReader::class);
349
        $key = $annotationReader->getClassAnnotation(new \ReflectionClass($modelClassName), DatabaseKey::class);
350
        if (null !== $key) {
351
            $additionalKeys = [(string)$key];
352
            array_walk($additionalKeys, function (&$item): void {
353
                $item = 'KEY ' . $item;
354
            });
355
            $fields = array_merge($fields, $additionalKeys);
356
        }
357
358
        // add data set keys
359
        $fields = array_merge($fields, $dataSet->getDatabaseSqlKeyInformation($dataImplementations, $tableName));
360
361
        return $this->generateSqlQuery($tableName, $fields);
362
    }
363
364
    /**
365
     * Get the data set object.
366
     *
367
     * @return \HDNET\Autoloader\DataSet
368
     */
369
    protected function getDataSet()
370
    {
371
        return GeneralUtility::makeInstance(DataSet::class);
372
    }
373
374
    /**
375
     * Get TCA title.
376
     *
377
     * @param string $tableName
378
     * @param string $extensionName
379
     *
380
     * @return string
381
     */
382
    protected function getTcaTitle($tableName, $extensionName)
383
    {
384
        return TranslateUtility::getLllOrHelpMessage($tableName, $extensionName, $tableName);
385
    }
386
}
387