Passed
Pull Request — master (#2)
by David
03:49
created

DefaultNamingStrategy::toCamelCase()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 16
Ratio 100 %

Importance

Changes 0
Metric Value
dl 16
loc 16
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
1
<?php
2
3
4
namespace TheCodingMachine\TDBM\Utils;
5
6
use Doctrine\Common\Inflector\Inflector;
7
use Doctrine\DBAL\Schema\Column;
8
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
9
use TheCodingMachine\TDBM\TDBMException;
10
11
class DefaultNamingStrategy extends AbstractNamingStrategy
12
{
13
    private $beanPrefix = '';
14
    private $beanSuffix = '';
15
    private $baseBeanPrefix = 'Abstract';
16
    private $baseBeanSuffix = '';
17
    private $daoPrefix = '';
18
    private $daoSuffix = 'Dao';
19
    private $baseDaoPrefix = 'Abstract';
20
    private $baseDaoSuffix = 'Dao';
21
    private $exceptions = [];
22
23
    /**
24
     * Sets the string prefix to any bean class name.
25
     *
26
     * @param string $beanPrefix
27
     */
28
    public function setBeanPrefix(string $beanPrefix)
29
    {
30
        $this->beanPrefix = $beanPrefix;
31
    }
32
33
    /**
34
     * Sets the string suffix to any bean class name.
35
     *
36
     * @param string $beanSuffix
37
     */
38
    public function setBeanSuffix(string $beanSuffix)
39
    {
40
        $this->beanSuffix = $beanSuffix;
41
    }
42
43
    /**
44
     * Sets the string prefix to any base bean class name.
45
     *
46
     * @param string $baseBeanPrefix
47
     */
48
    public function setBaseBeanPrefix(string $baseBeanPrefix)
49
    {
50
        $this->baseBeanPrefix = $baseBeanPrefix;
51
    }
52
53
    /**
54
     * Sets the string suffix to any base bean class name.
55
     *
56
     * @param string $baseBeanSuffix
57
     */
58
    public function setBaseBeanSuffix(string $baseBeanSuffix)
59
    {
60
        $this->baseBeanSuffix = $baseBeanSuffix;
61
    }
62
63
    /**
64
     * Sets the string prefix to any DAO class name.
65
     *
66
     * @param string $daoPrefix
67
     */
68
    public function setDaoPrefix(string $daoPrefix)
69
    {
70
        $this->daoPrefix = $daoPrefix;
71
    }
72
73
    /**
74
     * Sets the string suffix to any DAO class name.
75
     *
76
     * @param string $daoSuffix
77
     */
78
    public function setDaoSuffix(string $daoSuffix)
79
    {
80
        $this->daoSuffix = $daoSuffix;
81
    }
82
83
    /**
84
     * Sets the string prefix to any base DAO class name.
85
     *
86
     * @param string $baseDaoPrefix
87
     */
88
    public function setBaseDaoPrefix(string $baseDaoPrefix)
89
    {
90
        $this->baseDaoPrefix = $baseDaoPrefix;
91
    }
92
93
    /**
94
     * Sets the string suffix to any base DAO class name.
95
     *
96
     * @param string $baseDaoSuffix
97
     */
98
    public function setBaseDaoSuffix(string $baseDaoSuffix)
99
    {
100
        $this->baseDaoSuffix = $baseDaoSuffix;
101
    }
102
103
104
    /**
105
     * Returns the bean class name from the table name (excluding the namespace).
106
     *
107
     * @param string $tableName
108
     * @return string
109
     */
110
    public function getBeanClassName(string $tableName): string
111
    {
112
        return $this->beanPrefix.$this->toSingularCamelCase($tableName).$this->beanSuffix;
113
    }
114
115
    /**
116
     * Returns the base bean class name from the table name (excluding the namespace).
117
     *
118
     * @param string $tableName
119
     * @return string
120
     */
121
    public function getBaseBeanClassName(string $tableName): string
122
    {
123
        return $this->baseBeanPrefix.$this->toSingularCamelCase($tableName).$this->baseBeanSuffix;
124
    }
125
126
    /**
127
     * Returns the name of the DAO class from the table name (excluding the namespace).
128
     *
129
     * @param string $tableName
130
     * @return string
131
     */
132
    public function getDaoClassName(string $tableName): string
133
    {
134
        return $this->daoPrefix.$this->toSingularCamelCase($tableName).$this->daoSuffix;
135
    }
136
137
    /**
138
     * Returns the name of the base DAO class from the table name (excluding the namespace).
139
     *
140
     * @param string $tableName
141
     * @return string
142
     */
143
    public function getBaseDaoClassName(string $tableName): string
144
    {
145
        return $this->baseDaoPrefix.$this->toSingularCamelCase($tableName).$this->baseDaoSuffix;
146
    }
147
148
    /**
149
     * Tries to put string to the singular form (if it is plural) and camel case form.
150
     * We assume the table names are in english.
151
     *
152
     * @param $str string
153
     *
154
     * @return string
155
     */
156 View Code Duplication
    private function toSingularCamelCase(string $str): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
    {
158
        // Let's first check if this is not in the exceptions directory.
159
        if (isset($this->exceptions[$str])) {
160
            return $this->exceptions[$str];
161
        }
162
163
        $tokens = preg_split("/[_ ]+/", $str);
164
165
        $str = '';
166
        foreach ($tokens as $token) {
167
            $str .= ucfirst(Inflector::singularize($token));
168
        }
169
170
        return $str;
171
    }
172
173
    /**
174
     * Put string to camel case form.
175
     *
176
     * @param $str string
177
     *
178
     * @return string
179
     */
180 View Code Duplication
    private function toCamelCase(string $str): string
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
    {
182
        // Let's first check if this is not in the exceptions directory.
183
        if (isset($this->exceptions[$str])) {
184
            return $this->exceptions[$str];
185
        }
186
187
        $tokens = preg_split("/[_ ]+/", $str);
188
189
        $str = '';
190
        foreach ($tokens as $token) {
191
            $str .= ucfirst($token);
192
        }
193
194
        return $str;
195
    }
196
197
    /**
198
     * Returns the class name for the DAO factory.
199
     *
200
     * @return string
201
     */
202
    public function getDaoFactoryClassName(): string
203
    {
204
        return 'DaoFactory';
205
    }
206
207
    /**
208
     * Sets exceptions in the naming of classes.
209
     * The key is the name of the table, the value the "base" name of beans and DAOs.
210
     *
211
     * This is very useful for dealing with plural to singular translations in non english table names.
212
     *
213
     * For instance if you are dealing with a table containing horses in French ("chevaux" that has a singular "cheval"):
214
     *
215
     * [
216
     *     "chevaux" => "Cheval"
217
     * ]
218
     *
219
     * @param array<string,string> $exceptions
220
     */
221
    public function setExceptions(array $exceptions): void
222
    {
223
        $this->exceptions = $exceptions;
224
    }
225
226
    protected function getForeignKeyUpperCamelCaseName(ForeignKeyConstraint $foreignKey, bool $alternativeName): string
227
    {
228
        // First, are there many column or only one?
229
        // If one column, we name the setter after it. Otherwise, we name the setter after the table name
230
        if (count($foreignKey->getLocalColumns()) > 1) {
231
            $name = $this->toSingularCamelCase($foreignKey->getForeignTableName());
232
            if ($alternativeName) {
233
                $camelizedColumns = array_map(['TheCodingMachine\\TDBM\\Utils\\TDBMDaoGenerator', 'toCamelCase'], $foreignKey->getLocalColumns());
234
235
                $name .= 'By'.implode('And', $camelizedColumns);
236
            }
237
        } else {
238
            $column = $foreignKey->getLocalColumns()[0];
239
            // Let's remove any _id or id_.
240
            if (strpos(strtolower($column), 'id_') === 0) {
241
                $column = substr($column, 3);
242
            }
243
            if (strrpos(strtolower($column), '_id') === strlen($column) - 3) {
244
                $column = substr($column, 0, strlen($column) - 3);
245
            }
246
            $name = $this->toCamelCase($column);
247
            if ($alternativeName) {
248
                $name .= 'Object';
249
            }
250
        }
251
252
        return $name;
253
    }
254
255
    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.

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

Loading history...
256
    {
257
        return $this->toCamelCase($columnName);
258
    }
259
260
    protected function getUpperCamelCaseName(AbstractBeanPropertyDescriptor $property): string
261
    {
262
        if ($property instanceof ObjectBeanPropertyDescriptor) {
263
            return $this->getForeignKeyUpperCamelCaseName($property->getForeignKey(), $property->isAlternativeName());
0 ignored issues
show
Bug introduced by
It seems like $property->getForeignKey() can be null; however, getForeignKeyUpperCamelCaseName() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
264
        } elseif ($property instanceof ScalarBeanPropertyDescriptor) {
265
            return $this->getScalarColumnUpperCamelCaseName($property->getColumnName(), $property->isAlternativeName());
266
        } else {
267
            throw new TDBMException('Unexpected property type. Should be either ObjectBeanPropertyDescriptor or ScalarBeanPropertyDescriptor');
268
        }
269
    }
270
}
271