Passed
Pull Request — master (#146)
by David
03:02
created

DefaultNamingStrategy::setBaseDaoPrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

306
    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...
307
    {
308
        return $this->toCamelCase($columnName);
309
    }
310
311
    protected function getUpperCamelCaseName(AbstractBeanPropertyDescriptor $property): string
312
    {
313
        if ($property instanceof ObjectBeanPropertyDescriptor) {
314
            return $this->getForeignKeyUpperCamelCaseName($property->getForeignKey(), $property->isAlternativeName());
315
        } elseif ($property instanceof ScalarBeanPropertyDescriptor) {
316
            return $this->getScalarColumnUpperCamelCaseName($property->getColumnName(), $property->isAlternativeName());
317
        } else {
318
            throw new TDBMException('Unexpected property type. Should be either ObjectBeanPropertyDescriptor or ScalarBeanPropertyDescriptor'); // @codeCoverageIgnore
319
        }
320
    }
321
322
    private function getSchema(): Schema
323
    {
324
        if ($this->schema === null) {
325
            $this->schema = $this->schemaManager->createSchema();
326
        }
327
        return $this->schema;
328
    }
329
330
    public function getAutopivotEntityNameFrom(ForeignKeyConstraint $constraint, bool $useAlternativeName): string
331
    {
332
        return $this->getForeignKeyUpperCamelCaseName($constraint, $useAlternativeName);
333
    }
334
}
335