Completed
Push — master ( c5a8cb...db309f )
by Jared
02:27
created

DatabaseAdapter::getTablename()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
/**
4
 * @author Jared King <[email protected]>
5
 *
6
 * @link http://jaredtking.com
7
 *
8
 * @copyright 2015 Jared King
9
 * @license MIT
10
 */
11
namespace Pulsar\Adapter;
12
13
use Carbon\Carbon;
14
use Pulsar\Exception\AdapterException;
15
use Pulsar\Model;
16
use Pulsar\Query;
17
use PDOException;
18
use PDOStatement;
19
use Pimple\Container;
20
21
class DatabaseAdapter implements AdapterInterface
22
{
23
    /**
24
     * @var \Pimple\Container
25
     */
26
    private $app;
27
28
    /**
29
     * @param \Pimple\Container $app
30
     */
31
    public function __construct(Container $app = null)
32
    {
33
        $this->app = $app;
34
    }
35
36
    public function createModel(Model $model, array $parameters)
37
    {
38
        $values = $this->serialize($model, $parameters);
39
        $tablename = $model::tablename();
40
41
        try {
42
            return $this->app['db']->insert($values)
43
                ->into($tablename)
44
                ->execute() instanceof PDOStatement;
45
        } catch (PDOException $original) {
46
            $e = new AdapterException('An error occurred in the database adapter when creating the '.$model::modelName());
47
            $e->setException($original);
48
            throw $e;
49
        }
50
    }
51
52
    public function getCreatedID(Model $model, $propertyName)
53
    {
54
        try {
55
            $id = $this->app['db']->getPDO()->lastInsertId();
56
        } catch (PDOException $original) {
57
            $e = new AdapterException('An error occurred in the database adapter when getting the ID of the new '.$model::modelName());
58
            $e->setException($original);
59
            throw $e;
60
        }
61
62
        return $id;
63
    }
64
65
    public function updateModel(Model $model, array $parameters)
66
    {
67
        if (count($parameters) == 0) {
68
            return true;
69
        }
70
71
        $values = $this->serialize($model, $parameters);
72
        $tablename = $model::tablename();
73
74
        try {
75
            return $this->app['db']->update($tablename)
76
                ->values($values)
77
                ->where($model->ids())
78
                ->execute() instanceof PDOStatement;
79
        } catch (PDOException $original) {
80
            $e = new AdapterException('An error occurred in the database adapter when updating the '.$model::modelName());
81
            $e->setException($original);
82
            throw $e;
83
        }
84
    }
85
86
    public function deleteModel(Model $model)
87
    {
88
        $tablename = $model::tablename();
89
90
        try {
91
            return $this->app['db']->delete($tablename)
92
                ->where($model->ids())
93
                ->execute() instanceof PDOStatement;
94
        } catch (PDOException $original) {
95
            $e = new AdapterException('An error occurred in the database adapter while deleting the '.$model::modelName());
96
            $e->setException($original);
97
            throw $e;
98
        }
99
    }
100
101
    public function queryModels(Query $query)
102
    {
103
        $model = $query->getModel();
104
        $tablename = $model::tablename();
105
106
        // build a DB query from the model query
107
        $dbQuery = $this->app['db']
108
            ->select($this->prefixSelect('*', $tablename))
109
            ->from($tablename)
110
            ->where($this->prefixWhere($query->getWhere(), $tablename))
111
            ->limit($query->getLimit(), $query->getStart())
112
            ->orderBy($this->prefixSort($query->getSort(), $tablename));
113
114
        // join conditions
115
        foreach ($query->getJoins() as $join) {
116
            list($foreignModel, $column, $foreignKey) = $join;
117
118
            $foreignTablename = $foreignModel::tablename();
119
            $condition = $this->prefixColumn($column, $tablename).'='.$this->prefixColumn($foreignKey, $foreignTablename);
120
121
            $dbQuery->join($foreignTablename, $condition);
122
        }
123
124
        try {
125
            $data = $dbQuery->all();
126
        } catch (PDOException $original) {
127
            $e = new AdapterException('An error occurred in the database adapter while performing the '.$model::modelName().' query');
128
            $e->setException($original);
129
            throw $e;
130
        }
131
132
        return $data;
133
    }
134
135
    public function totalRecords(Query $query)
136
    {
137
        $model = $query->getModel();
138
        $tablename = $model::tablename();
139
140
        try {
141
            return (int) $this->app['db']->select('count(*)')
142
                ->from($tablename)
143
                ->where($query->getWhere())
144
                ->scalar();
145
        } catch (PDOException $original) {
146
            $e = new AdapterException('An error occurred in the database adapter while getting the number of '.$model::modelName().' objects');
147
            $e->setException($original);
148
            throw $e;
149
        }
150
    }
151
152
    /**
153
     * Marshals a value to storage.
154
     *
155
     * @param mixed $value
156
     * @param Model|string optional model class
157
     * @param string $property optional property name
158
     *
159
     * @return mixed serialized value
160
     */
161
    public function serializeValue($value, $model = null, $property = null)
162
    {
163
        // convert dates back to their string representation
164
        if ($value instanceof Carbon) {
165
            if (!$model) {
166
                $model = 'Pulsar\Model';
167
            }
168
169
            $format = $model::getDateFormat($property);
170
171
            return $value->format($format);
172
        }
173
174
        // encode arrays/objects as JSON
175
        if (is_array($value) || is_object($value)) {
176
            return json_encode($value);
177
        }
178
179
        return $value;
180
    }
181
182
    /**
183
     * Serializes an array of values.
184
     *
185
     * @param Model|string model class
186
     * @param array $values
187
     *
188
     * @return array
189
     */
190
    private function serialize($model, array $values)
191
    {
192
        foreach ($values as $k => &$value) {
193
            $value = $this->serializeValue($value, $model, $k);
194
        }
195
196
        return $values;
197
    }
198
199
    /**
200
     * Returns a prefixed select statement.
201
     *
202
     * @param string $columns
203
     * @param string $tablename
204
     *
205
     * @return string
206
     */
207
    private function prefixSelect($columns, $tablename)
208
    {
209
        $prefixed = [];
210
        foreach (explode(',', $columns) as $column) {
211
            $prefixed[] = $this->prefixColumn($column, $tablename);
212
        }
213
214
        return implode(',', $prefixed);
215
    }
216
217
    /**
218
     * Returns a prefixed where statement.
219
     *
220
     * @param string $columns
0 ignored issues
show
Bug introduced by
There is no parameter named $columns. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
221
     * @param string $tablename
222
     *
223
     * @return string
224
     */
225
    private function prefixWhere(array $where, $tablename)
226
    {
227
        $return = [];
228
        foreach ($where as $key => $condition) {
229
            // handles $where[property] = value
230
            if (!is_numeric($key)) {
231
                $return[$this->prefixColumn($key, $tablename)] = $condition;
232
            // handles $where[] = [property, value, '=']
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
233
            } elseif (is_array($condition)) {
234
                $condition[0] = $this->prefixColumn($condition[0], $tablename);
235
                $return[] = $condition;
236
            // handles raw SQL - do nothing
237
            } else {
238
                $return[] = $condition;
239
            }
240
        }
241
242
        return $return;
243
    }
244
245
    /**
246
     * Returns a prefixed sort statement.
247
     *
248
     * @param string $columns
0 ignored issues
show
Bug introduced by
There is no parameter named $columns. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
249
     * @param string $tablename
250
     *
251
     * @return string
252
     */
253
    private function prefixSort(array $sort, $tablename)
254
    {
255
        foreach ($sort as &$condition) {
256
            $condition[0] = $this->prefixColumn($condition[0], $tablename);
257
        }
258
259
        return $sort;
260
    }
261
262
    /**
263
     * Prefix columns with tablename that contains only
264
     * alphanumeric/underscores/*.
265
     *
266
     * @param string $column
267
     * @param string $tablename
268
     *
269
     * @return string prefixed column
270
     */
271
    private function prefixColumn($column, $tablename)
272
    {
273
        if ($column === '*' || preg_match('/^[a-z0-9_]+$/i', $column)) {
274
            return "$tablename.$column";
275
        }
276
277
        return $column;
278
    }
279
}
280