Passed
Push — Accessing-and-setting-the-site... ( 48995f...ec05f1 )
by Stone
04:16
created

Model::getTablePrefix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Core;
4
5
use PDO;
6
7
/**
8
 * Class Model here we have all the generic calls to be inherited by the App\Models
9
 * using PDO connections
10
 * @package Core
11
 *
12
 * PHP version 7
13
 */
14
abstract class Model
15
{
16
    /**
17
     * @var PDO the database handeler
18
     */
19
    protected $dbh;
20
21
    /**
22
     * @var \PDOStatement|boolean the prepared sql statement
23
     */
24
    protected $stmt;
25
26
    /**
27
     * @var Container the dependancy injector
28
     */
29
    private $container;
30
31
    /**
32
     * Model constructor. prepares the database connection
33
     * @param Container $container
34
     */
35
    public function __construct(Container $container)
36
    {
37
        $this->container = $container;
38
        $this->dbh = $this->container->setPdo();
39
    }
40
41
    /*
42
     * generic PDO query constructor
43
     * ---------------------------------------------
44
     */
45
46
    /**
47
     * creating and storing the query
48
     * @param $sql string creating the sql query
49
     */
50
    protected function query($sql): void
51
    {
52
        $this->stmt = $this->dbh->prepare($sql);
53
    }
54
55
    /**
56
     * binding the parameters to the query. Need the stmt to be declared before via query()
57
     * @param $param
58
     * @param $value
59
     * @param  $type
60
     * @throws \Exception error if no sql query to bind to
61
     */
62
    protected function bind($param, $value, $type = null): void
63
    {
64
        if ($this->stmt == null) {
65
            throw new \Exception("No query to bind to");
66
        }
67
        if (is_null($type)) { //need a bind value, so just check it in code. that way we can just call bind(param,value)
68
            switch (true) {
69
                case is_int($value):
70
                    $type = PDO::PARAM_INT;
71
                    break;
72
                case is_bool($value):
73
                    $type = PDO::PARAM_BOOL;
74
                    break;
75
                case is_null($value):
76
                    $type = PDO::PARAM_NULL;
77
                    break;
78
                default:
79
                    $type = PDO::PARAM_STR;
80
            }
81
        }
82
        $this->stmt->bindValue($param, $value, $type);
83
    }
84
85
    /**
86
     * Execute our constructed SQL statement
87
     * @return bool
88
     * @throws \Exception if the statement is empty
89
     */
90
    protected function execute()
91
    {
92
        if ($this->stmt == null) {
93
            throw new \Exception("No statement to execute");
94
        }
95
        return $this->stmt->execute();
96
    }
97
98
    /**
99
     * fetches the result from an executed query
100
     * @return array
101
     */
102
    protected function fetchAll(){
103
        return $this->stmt->fetchAll();
104
    }
105
106
    /**
107
     * returns a single line from the executed query
108
     * @return mixed
109
     */
110
    protected function fetch(){
111
        return $this->stmt->fetch();
112
    }
113
114
    /*
115
     * END generic PDO query constructor
116
     * ---------------------------------------------
117
     */
118
119
    /**
120
     * correlation between the model name and the table name
121
     * if we don't have a table name, get the table that has the same name as the model will be returned (else, we do nothing !!)
122
     * Also search if the table exists, if not do a check in the views (must be v_$table)
123
     * @param string|null $table the name of the table to get, if none the get the table of the models name
124
     * @return string the table name (with an s)
125
     * @throws \ReflectionException the model doesn't exist, should never happen
126
     * @throws \Exception table or view doesn't exist
127
     * @return string table or view name
128
     */
129
    protected function getTable(string $table = null): string
130
    {
131
        //If no table is passed, get the calling model name
132
        if ($table === null) {
133
            $reflect = new \ReflectionClass(get_class($this));
134
            $table = $reflect->getShortName(); //this is to only get the model name, otherwise we get the full namespace
135
            $table = $table . 's'; //adding the s since the table should be plural. Might be some special case where the plural isn't just with an s
136
            $table = strtolower($table); //the database names are in lowercase
137
        }
138
139
        $table = $this->getTablePrefix($table);
140
141
        //see if table exists
142
        $sql = "SHOW TABLES LIKE :table";
143
        $stmt = $this->dbh->prepare($sql);
144
        $stmt->bindValue(':table', $table, PDO::PARAM_STR);
145
        $stmt->execute();
146
        $exists = $stmt->rowCount() > 0; //will return 1 if table exists or 0 if non existant
147
148
        if ($exists) {
149
            //the table exists
150
            return $table;
151
        }
152
153
        //if we are here, then table doesn't exist, check for view
154
        $view = 'v_' . $table;
155
        $stmt->bindValue(':table', $view, PDO::PARAM_STR);
156
        $stmt->execute();
157
        $exists = $stmt->rowCount() > 0; //will return 1 if table exists or 0 if non existant
158
159
        if ($exists) {
160
            //the view exists
161
            return $view;
162
        }
163
164
        //neither table or view exists
165
        //throw an error
166
        throw new \Exception("Table or view $table doesn't exist");
167
    }
168
169
    /**
170
     * This function adds the table prefix if set and returns the name
171
     * Use this if we are sure of the table name. Avoids the DB calls
172
     * @param $table string the table name
173
     * @return string
174
     */
175
    protected function getTablePrefix($table){
176
        if(Config::TABLE_PREFIX != '')
1 ignored issue
show
introduced by
The condition Core\Config::TABLE_PREFIX != '' is always false.
Loading history...
177
        {
178
            $table = Config::TABLE_PREFIX.'_'.$table;
179
        }
180
        return $table;
181
    }
182
183
    /**
184
     * checks if the result from a PDO query has any data.
185
     * If yes then we return the array
186
     * If debugging is enabled we throw an exception on no results
187
     * or we just return an empty array
188
     * @param mixed $result the PDO result of a query
189
     * @return array the result or empty
190
     * @throws \Exception if debugging is on and no result
191
     */
192
    private function returnArray($result): array
193
    {
194
        if ($result) {
195
            return $result;
196
        }
197
        if (Config::DEV_ENVIRONMENT) {
198
            throw new \Exception("No results in database");
199
        }
200
        return [];
201
    }
202
203
    /**
204
     * gets the entire table or view and returns the array
205
     * @param string $table the table to search in, if empty then get the table based on model name
206
     * @return array the results from database
207
     * @throws \ReflectionException
208
     */
209
    protected function getResultSet($table = ''): array
210
    {
211
        $tableName = $this->getTable($table);
212
        $sql = "SELECT * FROM $tableName"; //can not pass table name as :parameter. since we already have tested if the table exists, this var should be safe.
213
        $this->query($sql);
214
        $this->execute();
215
        $result = $this->stmt->fetchAll(); //returns an array or false if no results
216
        return $this->returnArray($result);
217
    }
218
219
    /**
220
     * gets the entire table or view and returns the array with a limit to the number of rows
221
     * @param string $table the table to search in, if empty then get the table based on model name
222
     * @param string $limit the limit of rows to return
223
     * @return array the results from database
224
     * @throws \ReflectionException
225
     */
226
    protected function getResultSetLimited($limit, $table = ''): array
227
    {
228
        $tableName = $this->getTable($table);
229
        $sql = "SELECT * FROM $tableName LIMIT :limit";
230
        $this->query($sql);
231
        $this->bind(':limit', $limit);
232
        $this->execute();
233
        $result = $this->stmt->fetchAll(); //returns an array or false if no results
234
        return $this->returnArray($result);
235
    }
236
237
    /**
238
     * get's the result of SELECT * FROM table where idtable=$id
239
     * @param int $rowId searched id
240
     * @param string $table the table to search, if blank then we get the table or view based on the model name
241
     * @return array result or empty array
242
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
243
     */
244
    protected function getRowById($rowId, $table = ''): array
245
    {
246
        $tableName = $this->getTable($table);
247
        $idName = 'id' . $tableName;
248
        $sql = "SELECT * FROM $tableName WHERE $idName = :rowId";
249
        $this->query($sql);
250
        $this->bind(':rowId', $rowId);
251
        $this->execute();
252
        $result = $this->stmt->fetch();
253
        return $this->returnArray($result);
254
    }
255
256
    /**
257
     * gets the row from the query SELECT * FROM table WHERE $columnName = $Value
258
     * @param string $columnName the column to search in. Does a regex check for security
259
     * @param string $value the value to search for
260
     * @param string $table the table to search, if blank then we get the table or view based on the model name
261
     * @return array the results or empty
262
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
263
     * @throws \Exception if the column name consists of other characters than lower case, numbers and underscore for security
264
     */
265
    protected function getRowByColumn($columnName, $value, $table = ''): array
266
    {
267
        $tableName = $this->getTable($table);
268
        $columnNameOk = preg_match("/^[a-z0-9_]+$/i", $columnName); //testing if column name only has lower case, numbers and underscore
269
        if (!$columnNameOk) {
270
            throw new \Exception("Syntax error : Column name \"$columnName\" is not legal");
271
        }
272
        $sql = "SELECT * FROM $tableName WHERE $columnName = :value";
273
        $this->query($sql);
274
        $this->bind(':value', $value);
275
        $this->execute();
276
        $result = $this->stmt->fetch();
277
        return $this->returnArray($result);
278
    }
279
280
    /**
281
     * get's the result of SELECT * FROM table where table_slug=$slug
282
     * @param string $slug the slug to look up
283
     * @param string $table the table to search, if blank then we get the table or view based on the model name
284
     * @return array result or empty array
285
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
286
     */
287
    protected function getRowBySlug(string $slug, $table = ''): array
288
    {
289
        $tableName = $this->getTable($table);
290
        $slugName = $tableName.'_slug';
291
        $sql = "SELECT * FROM $tableName WHERE $slugName = :slug";
292
        $this->query($sql);
293
        $this->bind(':slug', $slug);
294
        $this->execute();
295
        $result = $this->stmt->fetch();
296
        return $this->returnArray($result);
297
    }
298
}