Passed
Push — Accessing-and-setting-the-site... ( 5ec5f0...48995f )
by Stone
02:19
created

Model   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 93
dl 0
loc 272
rs 10
c 0
b 0
f 0
wmc 26

13 Methods

Rating   Name   Duplication   Size   Complexity  
A query() 0 3 1
A bind() 0 21 6
A execute() 0 6 2
A __construct() 0 4 1
A getResultSet() 0 8 1
A getTable() 0 41 5
A fetchAll() 0 2 1
A getResultSetLimited() 0 9 1
A getRowBySlug() 0 10 1
A returnArray() 0 9 3
A fetch() 0 2 1
A getRowById() 0 10 1
A getRowByColumn() 0 13 2
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
    private 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
        if(Config::TABLE_PREFIX != '')
1 ignored issue
show
introduced by
The condition Core\Config::TABLE_PREFIX != '' is always false.
Loading history...
140
        {
141
            $table = Config::TABLE_PREFIX.'_'.$table;
142
        }
143
144
        //see if table exists
145
        $sql = "SHOW TABLES LIKE :table";
146
        $stmt = $this->dbh->prepare($sql);
147
        $stmt->bindValue(':table', $table, PDO::PARAM_STR);
148
        $stmt->execute();
149
        $exists = $stmt->rowCount() > 0; //will return 1 if table exists or 0 if non existant
150
151
        if ($exists) {
152
            //the table exists
153
            return $table;
154
        }
155
156
        //if we are here, then table doesn't exist, check for view
157
        $view = 'v_' . $table;
158
        $stmt->bindValue(':table', $view, PDO::PARAM_STR);
159
        $stmt->execute();
160
        $exists = $stmt->rowCount() > 0; //will return 1 if table exists or 0 if non existant
161
162
        if ($exists) {
163
            //the view exists
164
            return $view;
165
        }
166
167
        //neither table or view exists
168
        //throw an error
169
        throw new \Exception("Table or view $table doesn't exist");
170
    }
171
172
    /**
173
     * checks if the result from a PDO query has any data.
174
     * If yes then we return the array
175
     * If debugging is enabled we throw an exception on no results
176
     * or we just return an empty array
177
     * @param mixed $result the PDO result of a query
178
     * @return array the result or empty
179
     * @throws \Exception if debugging is on and no result
180
     */
181
    private function returnArray($result): array
182
    {
183
        if ($result) {
184
            return $result;
185
        }
186
        if (Config::DEV_ENVIRONMENT) {
187
            throw new \Exception("No results in database");
188
        }
189
        return [];
190
    }
191
192
    /**
193
     * gets the entire table or view and returns the array
194
     * @param string $table the table to search in, if empty then get the table based on model name
195
     * @return array the results from database
196
     * @throws \ReflectionException
197
     */
198
    protected function getResultSet($table = ''): array
199
    {
200
        $tableName = $this->getTable($table);
201
        $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.
202
        $this->query($sql);
203
        $this->execute();
204
        $result = $this->stmt->fetchAll(); //returns an array or false if no results
205
        return $this->returnArray($result);
206
    }
207
208
    /**
209
     * gets the entire table or view and returns the array with a limit to the number of rows
210
     * @param string $table the table to search in, if empty then get the table based on model name
211
     * @param string $limit the limit of rows to return
212
     * @return array the results from database
213
     * @throws \ReflectionException
214
     */
215
    protected function getResultSetLimited($limit, $table = ''): array
216
    {
217
        $tableName = $this->getTable($table);
218
        $sql = "SELECT * FROM $tableName LIMIT :limit";
219
        $this->query($sql);
220
        $this->bind(':limit', $limit);
221
        $this->execute();
222
        $result = $this->stmt->fetchAll(); //returns an array or false if no results
223
        return $this->returnArray($result);
224
    }
225
226
    /**
227
     * get's the result of SELECT * FROM table where idtable=$id
228
     * @param int $rowId searched id
229
     * @param string $table the table to search, if blank then we get the table or view based on the model name
230
     * @return array result or empty array
231
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
232
     */
233
    protected function getRowById($rowId, $table = ''): array
234
    {
235
        $tableName = $this->getTable($table);
236
        $idName = 'id' . $tableName;
237
        $sql = "SELECT * FROM $tableName WHERE $idName = :rowId";
238
        $this->query($sql);
239
        $this->bind(':rowId', $rowId);
240
        $this->execute();
241
        $result = $this->stmt->fetch();
242
        return $this->returnArray($result);
243
    }
244
245
    /**
246
     * gets the row from the query SELECT * FROM table WHERE $columnName = $Value
247
     * @param string $columnName the column to search in. Does a regex check for security
248
     * @param string $value the value to search for
249
     * @param string $table the table to search, if blank then we get the table or view based on the model name
250
     * @return array the results or empty
251
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
252
     * @throws \Exception if the column name consists of other characters than lower case, numbers and underscore for security
253
     */
254
    protected function getRowByColumn($columnName, $value, $table = ''): array
255
    {
256
        $tableName = $this->getTable($table);
257
        $columnNameOk = preg_match("/^[a-z0-9_]+$/i", $columnName); //testing if column name only has lower case, numbers and underscore
258
        if (!$columnNameOk) {
259
            throw new \Exception("Syntax error : Column name \"$columnName\" is not legal");
260
        }
261
        $sql = "SELECT * FROM $tableName WHERE $columnName = :value";
262
        $this->query($sql);
263
        $this->bind(':value', $value);
264
        $this->execute();
265
        $result = $this->stmt->fetch();
266
        return $this->returnArray($result);
267
    }
268
269
    /**
270
     * get's the result of SELECT * FROM table where table_slug=$slug
271
     * @param string $slug the slug to look up
272
     * @param string $table the table to search, if blank then we get the table or view based on the model name
273
     * @return array result or empty array
274
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
275
     */
276
    protected function getRowBySlug(string $slug, $table = ''): array
277
    {
278
        $tableName = $this->getTable($table);
279
        $slugName = $tableName.'_slug';
280
        $sql = "SELECT * FROM $tableName WHERE $slugName = :slug";
281
        $this->query($sql);
282
        $this->bind(':slug', $slug);
283
        $this->execute();
284
        $result = $this->stmt->fetch();
285
        return $this->returnArray($result);
286
    }
287
}