Test Failed
Pull Request — master (#36)
by Thomas
02:48
created

Dbal::insert()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 2
nop 2
1
<?php
2
3
namespace ORM\Dbal;
4
5
use ORM\Entity;
6
use ORM\EntityManager;
7
use ORM\Exceptions\NotScalar;
8
use ORM\Exceptions\UnsupportedDriver;
9
10
/**
11
 * Base class for database abstraction
12
 *
13
 * @package ORM
14
 * @author  Thomas Flori <[email protected]>
15
 */
16
abstract class Dbal
17
{
18
    /** @var EntityManager */
19
    protected $em;
20
    /** @var string[] */
21
    protected static $registeredTypes = [];
22
23
    /** @var string */
24
    protected static $quotingCharacter = '"';
25
    /** @var string */
26
    protected static $identifierDivider = '.';
27
    /** @var string */
28
    protected static $booleanTrue = '1';
29
    /** @var string */
30
    protected static $booleanFalse = '0';
31
32
    /**
33
     * Dbal constructor.
34
     *
35
     * @param EntityManager $entityManager
36
     */
37
    public function __construct(EntityManager $entityManager)
38
    {
39
        $this->em = $entityManager;
40
    }
41
42
    /**
43
     * Returns $identifier quoted for use in a sql statement
44
     *
45
     * @param string $identifier Identifier to quote
46
     * @return string
47
     */
48
    public function escapeIdentifier($identifier)
49
    {
50
        $q = static::$quotingCharacter;
51
        $d = static::$identifierDivider;
52
        return $q . str_replace($d, $q . $d . $q, $identifier) . $q;
53
    }
54
55
    /**
56
     * Returns $value formatted to use in a sql statement.
57
     *
58
     * @param  mixed  $value The variable that should be returned in SQL syntax
59
     * @return string
60
     * @throws NotScalar
61
     */
62
    public function escapeValue($value)
63
    {
64
        switch (strtolower(gettype($value))) {
65
            case 'string':
66
                return $this->em->getConnection()->quote($value);
67
68
            case 'integer':
69
                return (string) $value;
70
71
            case 'double':
72
                return (string) $value;
73
74
            case 'null':
75
                return 'NULL';
76
77
            case 'boolean':
78
                return ($value) ? static::$booleanTrue : static::$booleanFalse;
79
80
            default:
81
                throw new NotScalar('$value has to be scalar data type. ' . gettype($value) . ' given');
82
        }
83
    }
84
85
    /**
86
     * Describe a table
87
     *
88
     * @param string $table
89
     * @return Column[]
90
     * @throws UnsupportedDriver
91
     */
92
    public function describe($table)
93
    {
94
        throw new UnsupportedDriver('Not supported for this driver');
95
    }
96
97
    /**
98
     * Inserts $entity and returns the new ID for autoincrement or true
99
     *
100
     * @param Entity $entity
101
     * @param bool   $useAutoIncrement
102
     * @return mixed
103
     * @throws UnsupportedDriver
104
     */
105
    public function insert($entity, $useAutoIncrement = true)
106
    {
107
        $statement = $this->buildInsertStatement($entity);
108
109
        if ($useAutoIncrement && $entity::isAutoIncremented()) {
110
            throw new UnsupportedDriver('Auto incremented column for this driver is not supported');
111
        }
112
113
        $this->em->getConnection()->query($statement);
114
        $this->em->sync($entity, true);
115
        return true;
116
    }
117
118
    /**
119
     * Update $entity in database
120
     *
121
     * @param Entity $entity
122
     * @return bool
123
     * @internal
124
     */
125
    public function update(Entity $entity)
126
    {
127
        $data = $entity->getData();
128
        $primaryKey = $entity->getPrimaryKey();
129
130
        $where = [];
131
        foreach ($primaryKey as $var => $value) {
132
            $col = $entity::getColumnName($var);
133
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
134
            if (isset($data[$col])) {
135
                unset($data[$col]);
136
            }
137
        }
138
139
        $set = [];
140
        foreach ($data as $col => $value) {
141
            $set[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
142
        }
143
144
        $statement = 'UPDATE ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
145
                     'SET ' . implode(',', $set) . ' ' .
146
                     'WHERE ' . implode(' AND ', $where);
147
        $this->em->getConnection()->query($statement);
148
149
        return true;
150
    }
151
152
    /**
153
     * Delete $entity from database
154
     *
155
     * This method does not delete from the map - you can still receive the entity via fetch.
156
     *
157
     * @param Entity $entity
158
     * @return bool
159
     */
160
    public function delete(Entity $entity)
161
    {
162
        $primaryKey = $entity->getPrimaryKey();
163
        $where = [];
164
        foreach ($primaryKey as $var => $value) {
165
            $col = $entity::getColumnName($var);
166
            $where[] = $this->escapeIdentifier($col) . ' = ' . $this->escapeValue($value);
167
        }
168
169
        $statement = 'DELETE FROM ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
170
                     'WHERE ' . implode(' AND ', $where);
171
        $this->em->getConnection()->query($statement);
172
173
        return true;
174
    }
175
176
    /**
177
     * Register $type for describe
178
     *
179
     * @param string $type The full qualified class name
180
     */
181
    public static function registerType($type)
182
    {
183
        if (!in_array($type, static::$registeredTypes)) {
184
            array_unshift(static::$registeredTypes, $type);
185
        }
186
    }
187
188
    /**
189
     * @param string $char
190
     */
191
    public static function setQuotingCharacter($char)
192
    {
193
        static::$quotingCharacter = $char;
194
    }
195
196
    /**
197
     * @param string $divider
198
     */
199
    public static function setIdentifierDivider($divider)
200
    {
201
        static::$identifierDivider = $divider;
202
    }
203
204
    /**
205
     * @param string $true
206
     */
207
    public static function setBooleanTrue($true)
208
    {
209
        static::$booleanTrue = $true;
210
    }
211
212
    /**
213
     * @param string $false
214
     */
215
    public static function setBooleanFalse($false)
216
    {
217
        static::$booleanFalse = $false;
218
    }
219
220
    /**
221
     * @return string
222
     */
223
    public static function getQuotingCharacter()
224
    {
225
        return static::$quotingCharacter;
226
    }
227
228
    /**
229
     * @return string
230
     */
231
    public static function getIdentifierDivider()
232
    {
233
        return static::$identifierDivider;
234
    }
235
236
    /**
237
     * @return string
238
     */
239
    public static function getBooleanTrue()
240
    {
241
        return static::$booleanTrue;
242
    }
243
244
    /**
245
     * @return string
246
     */
247
    public static function getBooleanFalse()
248
    {
249
        return static::$booleanFalse;
250
    }
251
252
    /**
253
     * Build the insert statement for $entity
254
     *
255
     * @param Entity $entity
256
     * @return string
257
     */
258
    protected function buildInsertStatement($entity)
259
    {
260
        $data = $entity->getData();
261
262
        $cols = array_map(function ($key) {
263
            return $this->escapeIdentifier($key);
264
        }, array_keys($data));
265
266
        $values = array_map(function ($value) use ($entity) {
267
            return $this->escapeValue($value);
268
        }, array_values($data));
269
270
        $statement = 'INSERT INTO ' . $this->escapeIdentifier($entity::getTableName()) . ' ' .
271
                     '(' . implode(',', $cols) . ') VALUES (' . implode(',', $values) . ')';
272
273
        return $statement;
274
    }
275
276
    /**
277
     * Normalize $type
278
     *
279
     * The type returned by mysql is for example VARCHAR(20) - this function converts it to varchar
280
     *
281
     * @param string $type
282
     * @return string
283
     */
284
    protected function normalizeType($type)
285
    {
286
        $type = strtolower($type);
287
288
        if (($p = strpos($type, '(')) !== false && $p > 0) {
289
            $type = substr($type, 0, $p);
290
        }
291
292
        return $type;
293
    }
294
295
    /**
296
     * Extract content from parenthesis in $type
297
     *
298
     * @param string $type
299
     * @return string
300
     */
301
    protected function extractParenthesis($type)
302
    {
303
        if (preg_match('/\(([\d,]+)\)/', $type, $match)) {
304
            return $match[1];
305
        }
306
307
        return null;
308
    }
309
310
    /**
311
     * Get the type for $columnDefinition
312
     *
313
     * Executes fromDefinition of each registered Type
314
     *
315
     * @param array $columnDefinition
316
     * @return TypeInterface
317
     */
318
    protected function getType($columnDefinition)
319
    {
320
        foreach (self::$registeredTypes as $class) {
321
            if ($type = $class::fromDefinition($columnDefinition)) {
322
                return $type;
323
            }
324
        }
325
326
        return new Type\Text();
327
    }
328
}
329