Completed
Push — master ( f8decb...0d0ae6 )
by Arnaud
15s
created

Model   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 91
dl 0
loc 256
rs 10
c 0
b 0
f 0
wmc 24

11 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 getResultSetLimited() 0 9 1
A getRowBySlug() 0 10 1
A returnArray() 0 9 3
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
     * END generic PDO query constructor
100
     * ---------------------------------------------
101
     */
102
103
    /**
104
     * correlation between the model name and the table name
105
     * 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 !!)
106
     * Also search if the table exists, if not do a check in the views (must be v_$table)
107
     * @param string|null $table the name of the table to get, if none the get the table of the models name
108
     * @return string the table name (with an s)
109
     * @throws \ReflectionException the model doesn't exist, should never happen
110
     * @throws \Exception table or view doesn't exist
111
     * @return string table or view name
112
     */
113
    private function getTable(string $table = null): string
114
    {
115
        //If no table is passed, get the calling model name
116
        if ($table === null) {
117
            $reflect = new \ReflectionClass(get_class($this));
118
            $table = $reflect->getShortName(); //this is to only get the model name, otherwise we get the full namespace
119
            $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
120
            $table = strtolower($table); //the database names are in lowercase
121
        }
122
123
        if(Config::TABLE_PREFIX != '')
1 ignored issue
show
introduced by
The condition Core\Config::TABLE_PREFIX != '' is always false.
Loading history...
124
        {
125
            $table = Config::TABLE_PREFIX.'_'.$table;
126
        }
127
128
        //see if table exists
129
        $sql = "SHOW TABLES LIKE :table";
130
        $stmt = $this->dbh->prepare($sql);
131
        $stmt->bindValue(':table', $table, PDO::PARAM_STR);
132
        $stmt->execute();
133
        $exists = $stmt->rowCount() > 0; //will return 1 if table exists or 0 if non existant
134
135
        if ($exists) {
136
            //the table exists
137
            return $table;
138
        }
139
140
        //if we are here, then table doesn't exist, check for view
141
        $view = 'v_' . $table;
142
        $stmt->bindValue(':table', $view, PDO::PARAM_STR);
143
        $stmt->execute();
144
        $exists = $stmt->rowCount() > 0; //will return 1 if table exists or 0 if non existant
145
146
        if ($exists) {
147
            //the view exists
148
            return $view;
149
        }
150
151
        //neither table or view exists
152
        //throw an error
153
        throw new \Exception("Table or view $table doesn't exist");
154
    }
155
156
    /**
157
     * checks if the result from a PDO query has any data.
158
     * If yes then we return the array
159
     * If debugging is enabled we throw an exception on no results
160
     * or we just return an empty array
161
     * @param mixed $result the PDO result of a query
162
     * @return array the result or empty
163
     * @throws \Exception if debugging is on and no result
164
     */
165
    private function returnArray($result): array
166
    {
167
        if ($result) {
168
            return $result;
169
        }
170
        if (Config::DEV_ENVIRONMENT) {
171
            throw new \Exception("No results in database");
172
        }
173
        return [];
174
    }
175
176
    /**
177
     * gets the entire table or view and returns the array
178
     * @param string $table the table to search in, if empty then get the table based on model name
179
     * @return array the results from database
180
     * @throws \ReflectionException
181
     */
182
    protected function getResultSet($table = ''): array
183
    {
184
        $tableName = $this->getTable($table);
185
        $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.
186
        $this->query($sql);
187
        $this->execute();
188
        $result = $this->stmt->fetchAll(); //returns an array or false if no results
189
        return $this->returnArray($result);
190
    }
191
192
    /**
193
     * gets the entire table or view and returns the array with a limit to the number of rows
194
     * @param string $table the table to search in, if empty then get the table based on model name
195
     * @param string $limit the limit of rows to return
196
     * @return array the results from database
197
     * @throws \ReflectionException
198
     */
199
    protected function getResultSetLimited($limit, $table = ''): array
200
    {
201
        $tableName = $this->getTable($table);
202
        $sql = "SELECT * FROM $tableName LIMIT :limit";
203
        $this->query($sql);
204
        $this->bind(':limit', $limit);
205
        $this->execute();
206
        $result = $this->stmt->fetchAll(); //returns an array or false if no results
207
        return $this->returnArray($result);
208
    }
209
210
    /**
211
     * get's the result of SELECT * FROM table where idtable=$id
212
     * @param int $rowId searched id
213
     * @param string $table the table to search, if blank then we get the table or view based on the model name
214
     * @return array result or empty array
215
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
216
     */
217
    protected function getRowById($rowId, $table = ''): array
218
    {
219
        $tableName = $this->getTable($table);
220
        $idName = 'id' . $tableName;
221
        $sql = "SELECT * FROM $tableName WHERE $idName = :rowId";
222
        $this->query($sql);
223
        $this->bind(':rowId', $rowId);
224
        $this->execute();
225
        $result = $this->stmt->fetch();
226
        return $this->returnArray($result);
227
    }
228
229
    /**
230
     * gets the row from the query SELECT * FROM table WHERE $columnName = $Value
231
     * @param string $columnName the column to search in. Does a regex check for security
232
     * @param string $value the value to search for
233
     * @param string $table the table to search, if blank then we get the table or view based on the model name
234
     * @return array the results or empty
235
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
236
     * @throws \Exception if the column name consists of other characters than lower case, numbers and underscore for security
237
     */
238
    protected function getRowByColumn($columnName, $value, $table = ''): array
239
    {
240
        $tableName = $this->getTable($table);
241
        $columnNameOk = preg_match("/^[a-z0-9_]+$/i", $columnName); //testing if column name only has lower case, numbers and underscore
242
        if (!$columnNameOk) {
243
            throw new \Exception("Syntax error : Column name \"$columnName\" is not legal");
244
        }
245
        $sql = "SELECT * FROM $tableName WHERE $columnName = :value";
246
        $this->query($sql);
247
        $this->bind(':value', $value);
248
        $this->execute();
249
        $result = $this->stmt->fetch();
250
        return $this->returnArray($result);
251
    }
252
253
    /**
254
     * get's the result of SELECT * FROM table where table_slug=$slug
255
     * @param string $slug the slug to look up
256
     * @param string $table the table to search, if blank then we get the table or view based on the model name
257
     * @return array result or empty array
258
     * @throws \ReflectionException (probably not, but will throw an exception if debugging is on and no results)
259
     */
260
    protected function getRowBySlug(string $slug, $table = ''): array
261
    {
262
        $tableName = $this->getTable($table);
263
        $slugName = $tableName.'_slug';
264
        $sql = "SELECT * FROM $tableName WHERE $slugName = :slug";
265
        $this->query($sql);
266
        $this->bind(':slug', $slug);
267
        $this->execute();
268
        $result = $this->stmt->fetch();
269
        return $this->returnArray($result);
270
    }
271
}