1 <?php
2 /**
3 * Model.php
4 *
5 * @author Elvyrra SAS
6 * @license http://rem.mit-license.org/ MIT
7 */
8
9 namespace Hawk;
10
11 /**
12 * This class describes the data models behavior. each model defined in plugin must inherits this class
13 *
14 * @package Core
15 */
16 class Model{
17 use Utils;
18
19 /**
20 * The table name containing the data in database
21 *
22 * @var string
23 */
24 protected static $tablename;
25
26 /**
27 * The primary column of the elements in the table (default 'id')
28 *
29 * @var string
30 */
31 protected static $primaryColumn = 'id';
32
33 /**
34 * The DB instance name to get data in database default MAINDB
35 *
36 * @var string
37 */
38 protected static $dbname = MAINDB;
39
40
41 /**
42 * Constructor : Instanciate a new Model object
43 *
44 * @param array $data The initial data to set.
45 */
46 public function __construct($data = array()){
47 $this->map($data);
48 }
49
50
51 /**
52 * Get all the elements of the table
53 *
54 * @param string $index The field that will be used to affect the result array keys.
55 * If not set, the method will return a non associative array
56 * @param array $fields The fields to set in the instances
57 * @param array $order The order to get the results. Each key of this array must be a column name in the table,
58 * and the associated value is the order value ('ASC' or 'DESC')
59 *
60 * @return array An array of all Model instances
61 */
62 public static function getAll($index = null, $fields = array(), $order = array()){
63 return self::getListByExample(null, $index, $fields, $order);
64 }
65
66
67 /**
68 * Get a model instance by it primary column
69 *
70 * @param int $id The id of the instance to get
71 * @param array $fields The fields to set in the instance
72 *
73 * @return Model The found Model instance
74 */
75 public static function getById($id, $fields = array()){
76 $example = new DBExample(array(static::$primaryColumn => $id));
77 return self::getByExample($example, $fields);
78 }
79
80
81 /**
82 * Get a model instance by an example
83 *
84 * @param DBExample $example The example to find the data line
85 * @param array $fields The fields to set in the model instance
86 *
87 * @return Model The found Model instance
88 */
89 public static function getByExample(DBExample $example = null, $fields = array()){
90 return self::getDbInstance()->select(
91 array(
92 'fields' => $fields,
93 'from' => static::getTable(),
94 'where' => $example,
95 'return' => get_called_class(),
96 'one' => true,
97 )
98 );
99 }
100
101
102 /**
103 * Get a list of model instances by an example
104 *
105 * @param DBExample $example The example to find the data lines
106 * @param string $index The field that will be used as array key in the result.
107 * If not set, the result will be a non associative array
108 * @param array $fields The fields to set in the instances
109 * @param array $order The order to get the results. Each key of this array must be a column name in the table,
110 * and the associated value is the order value ('ASC' or 'DESC')
111 *
112 * @return Model[] The array containing found models
113 */
114 public static function getListByExample(DBExample $example = null, $index = null, $fields = array(), $order = array()){
115 return self::getDbInstance()->select(
116 array(
117 'fields' => $fields,
118 'from' => static::getTable(),
119 'where' => $example,
120 'index' => $index,
121 'return' => get_called_class(),
122 'orderby' => $order
123 )
124 );
125 }
126
127
128 /**
129 * Get a model instance by sql condition
130 *
131 * @param string $where The SQL condition
132 * @param array $binds The binded values
133 * @param array $fields The fields to set in the instance
134 *
135 * @return Model The found Model instance
136 */
137 public static function getBySQL($where = null, $binds = array(), $fields = array()){
138 return self::getDbInstance()->select(
139 array(
140 'fields' => $fields,
141 'from' => static::getTable(),
142 'where' => $where,
143 'binds' => $binds,
144 'return' => get_called_class(),
145 'one' => true,
146 )
147 );
148 }
149
150
151 /**
152 * Get a list of model instances by a SQL condition
153 *
154 * @param string $where The SQL condition to find the elements
155 * @param array $binds The binded values
156 * @param string $index The field that will be used as array key in the result.
157 * If not set, the result will be a non associative array
158 * @param array $fields The fields to set in the instances
159 * @param array $order The order to get the results. Each key of this array must be a column name in the table,
160 * and the associated value is the order value ('ASC' or 'DESC')
161 *
162 * @return Model[] the array containing found models instances
163 */
164 public static function getListBySQL($where = null, $binds = array(), $index = null, $fields = array(), $order = array()){
165 return self::getDbInstance()->select(
166 array(
167 'fields' => $fields,
168 'from' => static::getTable(),
169 'where' => $where,
170 'binds' => $binds,
171 'index' => $index,
172 'return' => get_called_class(),
173 'orderby' => $order
174 )
175 );
176 }
177
178
179
180 /**
181 * Delete data in the database from an example
182 *
183 * @param DBExample $example The example to find the lines to delete
184 *
185 * @return int the number of deleted elements in the database
186 */
187 public static function deleteByExample(DBExample $example = null){
188 return self::getDbInstance()->delete(static::getTable(), $example);
189 }
190
191 /**
192 * Delete data in the database from a SQL condition
193 *
194 * @param string $where The SQL condition to find the lines to delete
195 * @param array $binds - The binded values
196 *
197 * @return int The number of deleted elements in the database
198 */
199 public static function deleteBySQL($where = null, $binds = array()){
200 return self::getDbInstance()->delete(static::getTable(), $where, $binds);
201 }
202
203
204 /**
205 * Count the number of elements filtered by an example
206 *
207 * @param DBExample $example The example to find the lines to delete
208 * @param array $group - The fields used to group the results
209 *
210 * @return int The number of found elements in the database
211 */
212 public static function countElementsByExample(DBExample $example = null, $group = array()){
213 return self::getDbInstance()->count(static::getTable(), $example, array(), self::$primaryColumn, $group);
214 }
215
216
217 /**
218 * Count the number of elements filtered by a SQL condition
219 *
220 * @param string $where The SQL condition
221 * @param array $binds - the binded values
222 * @param array $group - The fields used to group the results
223 *
224 * @return int The number of found elements in the database
225 */
226 public static function countElementsBySQL($where = null, $binds = array(), $group = array()){
227 return self::getDbInstance()->count(static::getTable(), $where, $binds, self::$primaryColumn, $group);
228 }
229
230
231 /**
232 * Prepare the data to save in the database
233 *
234 * @return array The data to be inserted for method save or update
235 */
236 private function prepareDatabaseData(){
237 $fields = self::getDbInstance()->query('SHOW COLUMNS FROM ' . self::getTable(), array(), array('index' => 'Field', 'return' => DB::RETURN_ARRAY));
238
239 $insert = array();
240 foreach(get_object_vars($this) as $key => $value){
241 if(isset($fields[$key])) {
242 $insert[$key] = $value;
243 }
244 }
245
246 return $insert;
247 }
248
249
250 /**
251 * This method save a new Model in the database or update it if it exists.
252 * It is based on INSERT ... ON DUPLICATE KEY.
253 * If a new element is saved, then the id (or the value of the primary key) is set on the instance corresponding property
254 */
255 public function save(){
256 $insert = $this->prepareDatabaseData();
257 $duplicateUpdates = array_map(
258 function ($key) {
259 if($key == static::$primaryColumn) {
260 return "`$key`=LAST_INSERT_ID(`$key`)";
261 }
262 else{
263 return "`$key` = VALUES(`$key`)";
264 }
265 }, array_keys($insert)
266 );
267
268 if(!isset($insert[static::$primaryColumn])) {
269 $key = static::$primaryColumn;
270 $duplicateUpdates[] = "`$key`=LAST_INSERT_ID(`$key`)";
271 }
272 $onduplicate = implode(', ', $duplicateUpdates);
273
274 $lastid = self::getDbInstance()->insert(static::getTable(), $insert, '', $onduplicate);
275 if($lastid) {
276 $id = static::$primaryColumn;
277 $this->$id = $lastid;
278 }
279 }
280
281
282 /**
283 * Create a new element in the database
284 *
285 * @param array $data The data to insert in the database
286 *
287 * @return Model The added instance
288 */
289 public static function add($data){
290 $class = get_called_class();
291 $instance = new $class($data);
292 $instance->save();
293
294 (new Event(strtolower($class).'.added', array('data' => $data)))->trigger();
295
296 return $instance;
297 }
298
299
300 /**
301 * Add the model in the database only if does not alreay exists
302 */
303 public function addIfNotExists(){
304 $id = static::$primaryColumn;
305 $insert = $this->prepareDatabaseData();
306
307 $lastid = self::getDbInstance()->insert(static::getTable(), $insert, 'IGNORE');
308 if($lastid) {
309 $this->$id = $lastid;
310 }
311 }
312
313
314 /**
315 * Update the model data in the database
316 */
317 public function update(){
318 $update = $this->prepareDatabaseData();
319 $id = static::$primaryColumn;
320 self::getDbInstance()->update(static::getTable(), new DBExample(array($id => $this->$id)), $update);
321 }
322
323
324 /**
325 * Delete the model data from the database
326 *
327 * @return true if the data has been sucessfully removed from the database, false in other cases
328 */
329 public function delete(){
330 $class = get_called_class();
331 $id = static::$primaryColumn;
332 $deleted = self::getDbInstance()->delete(static::getTable(), new DBExample(array($id => $this->$id)));
333
334 (new Event(strtolower($class).'.deleted', array('object' => $this)))->trigger();
335
336 return (bool) $deleted;
337 }
338
339
340 /**
341 * Get the model data, only the data present in the database.
342 *
343 * @return array The object properties with their value
344 */
345 public function getData(){
346 return get_object_vars($this);
347 }
348
349
350
351 /**
352 * Set a property value to the object.
353 * You can use this method to set only one property, or an array of properties
354 *
355 * @param string|array $field If a string is set, then it is the name of the property.
356 * If an array is set, then set multiple properties will be set
357 * @param mixed $value The value to set to the property, only if $field is a string
358 */
359 public function set($field, $value = null){
360 if(is_array($field) && $value === null) {
361 foreach($field as $key => $value){
362 $this->set($key, $value);
363 }
364 }
365 else{
366 $this->$field = $value;
367 }
368 }
369
370
371 /**
372 * Get the table name of this model
373 *
374 * @return string the table name of the model
375 */
376 public static function getTable(){
377 return (static::$dbname == MAINDB ? App::conf()->get('db.prefix') : '') . static::$tablename;
378 }
379
380
381 /**
382 * Get the primary column of the model
383 *
384 * @return string The primary column of the model
385 */
386 public static function getPrimaryColumn(){
387 return static::$primaryColumn;
388 }
389
390
391 /**
392 * Get the DB instance name of the model
393 *
394 * @return string The name of the DB instance for the model
395 */
396 public static function getDbName(){
397 return static::$dbname;
398 }
399
400
401 /**
402 * Get the DB instance of the model
403 *
404 * @return DB The DB instance of the model
405 */
406 public static function getDbInstance(){
407 return DB::get(static::$dbname);
408 }
409
410
411 /**
412 * Set the table name of the model
413 *
414 * @param string $table The table name to set
415 */
416 public static function setTable($table){
417 static::$tablename = $table;
418 }
419
420 /**
421 * Set the primary column of the model
422 *
423 * @param string $primaryColumn The column to set as primary column
424 */
425 public static function setPrimaryColumn($primaryColumn){
426 static::$primaryColumn = $primaryColumn;
427 }
428
429 /**
430 * Set the DB instance name of the model
431 *
432 * @param string $name The instance name to set
433 */
434 public static function setDbName($name){
435 static::$dbname = $name;
436 }
437 }