DefaultNamingStrategy::getUpperCamelCaseName()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 3
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace TheCodingMachine\TDBM\Utils;
6
7
use Doctrine\DBAL\Schema\AbstractSchemaManager;
8
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
9
use Doctrine\DBAL\Schema\Schema;
10
use Doctrine\Inflector\Inflector;
11
use Doctrine\Inflector\InflectorFactory;
12
use TheCodingMachine\TDBM\TDBMException;
13
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
14
use TheCodingMachine\TDBM\Utils\Annotation\Bean;
15
16
class DefaultNamingStrategy extends AbstractNamingStrategy
17
{
18
    /** @var string */
19
    private $beanPrefix = '';
20
    /** @var string */
21
    private $beanSuffix = '';
22
    /** @var string */
23
    private $baseBeanPrefix = 'Abstract';
24
    /** @var string */
25
    private $baseBeanSuffix = '';
26
    /** @var string */
27
    private $daoPrefix = '';
28
    /** @var string */
29
    private $daoSuffix = 'Dao';
30
    /** @var string */
31
    private $baseDaoPrefix = 'Abstract';
32
    /** @var string */
33
    private $baseDaoSuffix = 'Dao';
34
    /** @var string */
35
    private $resultIteratorPrefix = '';
36
    /** @var string */
37
    private $resultIteratorSuffix = 'ResultIterator';
38
    /** @var string */
39
    private $baseResultIteratorPrefix = 'Abstract';
40
    /** @var string */
41
    private $baseResultIteratorSuffix = 'ResultIterator';
42
    /** @var array<string,string> */
43
    private $exceptions = [];
44
    /** @var AnnotationParser */
45
    private $annotationParser;
46
    /** @var AbstractSchemaManager */
47
    private $schemaManager;
48
    /** @var Schema */
49
    private $schema;
50
    /** @var Inflector */
51
    private $inflector;
52
53
    public function __construct(AnnotationParser $annotationParser, AbstractSchemaManager $schemaManager)
54
    {
55
        $this->annotationParser = $annotationParser;
56
        $this->schemaManager = $schemaManager;
57
        $this->inflector = InflectorFactory::create()->build();
58
    }
59
60
    /**
61
     * Sets the string prefix to any bean class name.
62
     *
63
     * @param string $beanPrefix
64
     */
65
    public function setBeanPrefix(string $beanPrefix): void
66
    {
67
        $this->beanPrefix = $beanPrefix;
68
    }
69
70
    /**
71
     * Sets the string suffix to any bean class name.
72
     *
73
     * @param string $beanSuffix
74
     */
75
    public function setBeanSuffix(string $beanSuffix): void
76
    {
77
        $this->beanSuffix = $beanSuffix;
78
    }
79
80
    /**
81
     * Sets the string prefix to any base bean class name.
82
     *
83
     * @param string $baseBeanPrefix
84
     */
85
    public function setBaseBeanPrefix(string $baseBeanPrefix): void
86
    {
87
        $this->baseBeanPrefix = $baseBeanPrefix;
88
    }
89
90
    /**
91
     * Sets the string suffix to any base bean class name.
92
     *
93
     * @param string $baseBeanSuffix
94
     */
95
    public function setBaseBeanSuffix(string $baseBeanSuffix): void
96
    {
97
        $this->baseBeanSuffix = $baseBeanSuffix;
98
    }
99
100
    /**
101
     * Sets the string prefix to any DAO class name.
102
     *
103
     * @param string $daoPrefix
104
     */
105
    public function setDaoPrefix(string $daoPrefix): void
106
    {
107
        $this->daoPrefix = $daoPrefix;
108
    }
109
110
    /**
111
     * Sets the string suffix to any DAO class name.
112
     *
113
     * @param string $daoSuffix
114
     */
115
    public function setDaoSuffix(string $daoSuffix): void
116
    {
117
        $this->daoSuffix = $daoSuffix;
118
    }
119
120
    /**
121
     * Sets the string prefix to any base DAO class name.
122
     *
123
     * @param string $baseDaoPrefix
124
     */
125
    public function setBaseDaoPrefix(string $baseDaoPrefix): void
126
    {
127
        $this->baseDaoPrefix = $baseDaoPrefix;
128
    }
129
130
    /**
131
     * Sets the string suffix to any base DAO class name.
132
     *
133
     * @param string $baseDaoSuffix
134
     */
135
    public function setBaseDaoSuffix(string $baseDaoSuffix): void
136
    {
137
        $this->baseDaoSuffix = $baseDaoSuffix;
138
    }
139
140
141
    /**
142
     * Returns the bean class name from the table name (excluding the namespace).
143
     *
144
     * @param string $tableName
145
     * @return string
146
     */
147
    public function getBeanClassName(string $tableName): string
148
    {
149
        return $this->beanPrefix.$this->tableNameToSingularCamelCase($tableName).$this->beanSuffix;
150
    }
151
152
    /**
153
     * Returns the base bean class name from the table name (excluding the namespace).
154
     *
155
     * @param string $tableName
156
     * @return string
157
     */
158
    public function getBaseBeanClassName(string $tableName): string
159
    {
160
        return $this->baseBeanPrefix.$this->tableNameToSingularCamelCase($tableName).$this->baseBeanSuffix;
161
    }
162
163
    /**
164
     * Returns the name of the DAO class from the table name (excluding the namespace).
165
     *
166
     * @param string $tableName
167
     * @return string
168
     */
169
    public function getDaoClassName(string $tableName): string
170
    {
171
        return $this->daoPrefix.$this->tableNameToSingularCamelCase($tableName).$this->daoSuffix;
172
    }
173
174
    /**
175
     * Returns the name of the base DAO class from the table name (excluding the namespace).
176
     *
177
     * @param string $tableName
178
     * @return string
179
     */
180
    public function getBaseDaoClassName(string $tableName): string
181
    {
182
        return $this->baseDaoPrefix.$this->tableNameToSingularCamelCase($tableName).$this->baseDaoSuffix;
183
    }
184
185
    /**
186
     * Returns the name of the ResultIterator class from the table name (excluding the namespace).
187
     *
188
     * @param string $tableName
189
     * @return string
190
     */
191
    public function getResultIteratorClassName(string $tableName): string
192
    {
193
        return $this->resultIteratorPrefix.$this->tableNameToSingularCamelCase($tableName).$this->resultIteratorSuffix;
194
    }
195
196
    /**
197
     * Returns the name of the base ResultIterator class from the table name (excluding the namespace).
198
     *
199
     * @param string $tableName
200
     * @return string
201
     */
202
    public function getBaseResultIteratorClassName(string $tableName): string
203
    {
204
        return $this->baseResultIteratorPrefix.$this->tableNameToSingularCamelCase($tableName).$this->baseResultIteratorSuffix;
205
    }
206
207
    private function tableNameToSingularCamelCase(string $tableName): string
208
    {
209
        // Now, let's check if we have a @Bean annotation on it.
210
        /** @var Bean|null $beanAnnotation */
211
        $beanAnnotation = $this->annotationParser->getTableAnnotations($this->getSchema()->getTable($tableName))->findAnnotation(Bean::class);
212
        if ($beanAnnotation !== null) {
213
            return $beanAnnotation->name;
214
        }
215
216
        return $this->toSingularCamelCase($tableName);
217
    }
218
219
    /**
220
     * Tries to put string to the singular form (if it is plural) and camel case form.
221
     * We assume the table names are in english.
222
     *
223
     * @param string $str
224
     *
225
     * @return string
226
     */
227
    private function toSingularCamelCase(string $str): string
228
    {
229
        // Let's first check if this is not in the exceptions directory.
230
        if (isset($this->exceptions[$str])) {
231
            return $this->exceptions[$str];
232
        }
233
234
        // If everything is in uppercase (Oracle), let's lowercase everything
235
        if (strtoupper($str) === $str) {
236
            $str = strtolower($str);
237
        }
238
239
        $tokens = preg_split("/[_ ]+/", $str);
240
        if ($tokens === false) {
241
            throw new \RuntimeException('Unexpected preg_split error'); // @codeCoverageIgnore
242
        }
243
244
        $str = '';
245
        foreach ($tokens as $token) {
246
            $str .= ucfirst($this->inflector->singularize($token));
247
        }
248
249
        return $str;
250
    }
251
252
    /**
253
     * Put string to camel case form.
254
     *
255
     * @param string $str
256
     *
257
     * @return string
258
     */
259
    private function toCamelCase(string $str): string
260
    {
261
        // Let's first check if this is not in the exceptions directory.
262
        if (isset($this->exceptions[$str])) {
263
            return $this->exceptions[$str];
264
        }
265
266
        // If everything is in uppercase (Oracle), let's lowercase everything
267
        if (strtoupper($str) === $str) {
268
            $str = strtolower($str);
269
        }
270
271
        $tokens = preg_split("/[_ ]+/", $str);
272
        if ($tokens === false) {
273
            throw new \RuntimeException('Unexpected preg_split error'); // @codeCoverageIgnore
274
        }
275
276
        $str = '';
277
        foreach ($tokens as $token) {
278
            $str .= ucfirst($token);
279
        }
280
281
        return $str;
282
    }
283
284
    /**
285
     * Returns the class name for the DAO factory.
286
     *
287
     * @return string
288
     */
289
    public function getDaoFactoryClassName(): string
290
    {
291
        return 'DaoFactory';
292
    }
293
294
    /**
295
     * Sets exceptions in the naming of classes.
296
     * The key is the name of the table, the value the "base" name of beans and DAOs.
297
     *
298
     * This is very useful for dealing with plural to singular translations in non english table names.
299
     *
300
     * For instance if you are dealing with a table containing horses in French ("chevaux" that has a singular "cheval"):
301
     *
302
     * [
303
     *     "chevaux" => "Cheval"
304
     * ]
305
     *
306
     * @param array<string,string> $exceptions
307
     */
308
    public function setExceptions(array $exceptions): void
309
    {
310
        $this->exceptions = $exceptions;
311
    }
312
313
    protected function getForeignKeyUpperCamelCaseName(ForeignKeyConstraint $foreignKey, bool $alternativeName): string
314
    {
315
        // First, are there many column or only one?
316
        // If one column, we name the setter after it. Otherwise, we name the setter after the table name
317
        if (count($foreignKey->getUnquotedLocalColumns()) > 1) {
318
            $name = $this->tableNameToSingularCamelCase($foreignKey->getForeignTableName());
319
            if ($alternativeName) {
320
                $camelizedColumns = array_map(['TheCodingMachine\\TDBM\\Utils\\TDBMDaoGenerator', 'toCamelCase'], $foreignKey->getUnquotedLocalColumns());
321
322
                $name .= 'By'.implode('And', $camelizedColumns);
323
            }
324
        } else {
325
            $column = $foreignKey->getUnquotedLocalColumns()[0];
326
            // Let's remove any _id or id_.
327
            if (strpos(strtolower($column), 'id_') === 0) {
328
                $column = substr($column, 3);
329
            }
330
            if (strrpos(strtolower($column), '_id') === strlen($column) - 3) {
331
                $column = substr($column, 0, strlen($column) - 3);
332
            }
333
            $name = $this->toCamelCase($column);
334
            if ($alternativeName) {
335
                $name .= 'Object';
336
            }
337
        }
338
339
        return $name;
340
    }
341
342
    protected function getScalarColumnUpperCamelCaseName(string $columnName, bool $alternativeName): string
0 ignored issues
show
Unused Code introduced by
The parameter $alternativeName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

342
    protected function getScalarColumnUpperCamelCaseName(string $columnName, /** @scrutinizer ignore-unused */ bool $alternativeName): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
343
    {
344
        return $this->toCamelCase($columnName);
345
    }
346
347
    protected function getUpperCamelCaseName(AbstractBeanPropertyDescriptor $property): string
348
    {
349
        if ($property instanceof ObjectBeanPropertyDescriptor) {
350
            return $this->getForeignKeyUpperCamelCaseName($property->getForeignKey(), $property->isAlternativeName());
351
        }
352
        if ($property instanceof ScalarBeanPropertyDescriptor) {
353
            return $this->getScalarColumnUpperCamelCaseName($property->getColumnName(), $property->isAlternativeName());
354
        }
355
        throw new TDBMException('Unexpected property type. Should be either ObjectBeanPropertyDescriptor or ScalarBeanPropertyDescriptor'); // @codeCoverageIgnore
356
    }
357
358
    private function getSchema(): Schema
359
    {
360
        if ($this->schema === null) {
361
            $this->schema = $this->schemaManager->createSchema();
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\Abs...Manager::createSchema() has been deprecated: Use {@link introspectSchema()} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

361
            $this->schema = /** @scrutinizer ignore-deprecated */ $this->schemaManager->createSchema();

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.

Loading history...
362
        }
363
        return $this->schema;
364
    }
365
366
    public function getAutoPivotEntityName(ForeignKeyConstraint $constraint, bool $useAlternativeName): string
367
    {
368
        return $this->getForeignKeyUpperCamelCaseName($constraint, $useAlternativeName);
369
    }
370
}
371