Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
MySqlDatabaseObject
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 9
702
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 table
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __isset
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 __get
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 __set
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 fresh
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 export
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 store
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
42
 delete
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace UserFrosting;
4
5abstract class MySqlDatabaseObject extends MySqlDatabase implements DatabaseObjectInterface {
6
7    /**
8     * @var DatabaseTable The table for this database object.  Must be specified by child class.
9     */
10    protected $_table;
11
12    protected $_id;          // The id of this object.  Table must have an `id` column.
13    protected $_properties;  // A mapping of the columns in the table that this object can access, to their values.
14
15    /**
16     * @var array Columns that should not be included in UPDATE statements.
17     * Useful for computed columns in views that can be read but not written.
18     * Child classes can override this to exclude specific columns.
19     */
20    protected $_nonUpdatableColumns = [];
21    
22    public function __construct($properties, $id = null) {       
23        // Set all valid properties
24        foreach ($properties as $column => $value){
25            if ($column != "id" && in_array($column, $this->_table->columns))
26                $this->_properties[$column] = $value;
27        }
28    
29        // Set id
30        $this->_id = $id;        
31    }
32       
33    public function table(){
34        return $this->_table;
35    }
36    
37    public function __isset($name) {
38        if ($name == "id" || isset($this->_properties[$name]))
39            return true;
40        else
41            return false;
42    }
43    
44    public function __get($name){
45        if ($name == "id")
46            return $this->_id;
47        else if (in_array($name, $this->_table->columns))
48            return $this->_properties[$name];
49        else {
50            $table = $this->_table->name;
51            throw new \Exception("The column '$name' does not exist in the table '$table'.");
52        }
53    }
54
55    // This function only allows whitelisted column names!  This is VERY IMPORTANT, otherwise the database will be open to SQL injection attacks.
56    public function __set($name, $value){
57        if (in_array($name, $this->_table->columns))
58            return $this->_properties[$name] = $value;
59        else {
60            $table = $this->_table->name;
61            throw new \Exception("The column '$name' does not exist in the table '$table'.");
62        }
63    }
64    
65    /* Refresh the object from the DB.
66     *
67     */
68    // TODO: Should this just update the internal contents of this object, rather than create a new one?
69    public function fresh(){
70        if (isset($this->_id)){
71            $db = static::connection();
72            
73            $table = $this->_table->name;
74            
75            $query = "SELECT * FROM `$table` WHERE id = :id LIMIT 1";
76            
77            $stmt = $db->prepare($query);
78            
79            $sqlVars[':id'] = $this->_id;
80            
81            $stmt->execute($sqlVars);
82              
83            $results = $stmt->fetch(\PDO::FETCH_ASSOC);
84            
85            // PDO returns false if no record found
86            if ($results)
87                return $results;
88        }
89        throw new \Exception("Could not refresh this object!  Either it does not exist in the database, or is in an invalid state.");
90    }
91      
92    /* Get the properties of this object as an associative array.
93     *
94     */  
95    public function export(){
96        return array_merge(["id" => $this->_id], $this->_properties);
97    }
98    
99    /* Store the object in the DB, creating a new record if one doesn't already exist.
100     *
101     */    
102    public function store() {
103        // Get connection
104        $db = static::connection();
105        $table = $this->_table->name;
106        
107        // If `id` is set, then update the existing record.
108        // Using UPDATE instead of INSERT...ON DUPLICATE KEY UPDATE
109        // to support views that can't accept INSERTs.
110
111        if ($this->_id) {
112            $set_terms = [];
113            $sqlVars = [];
114            foreach ($this->_properties as $name => $value){
115                // Skip non-updatable columns (e.g., computed columns in views)
116                if (in_array($name, $this->_nonUpdatableColumns)) {
117                    continue;
118                }
119                $set_terms[] = "`$name` = :$name";
120                $sqlVars[":$name"] = $value;
121            }
122
123            $sqlVars[':id'] = $this->_id;
124
125            $set_clause = implode(", ", $set_terms);
126
127            $query = "
128                UPDATE `$table`
129                SET $set_clause
130                WHERE id = :id";
131
132            $stmt = $db->prepare($query);
133            $stmt->execute($sqlVars);
134        } else {
135            $sqlVars = [];
136            $column_list = [];
137            $value_list = [];
138            foreach ($this->_properties as $name => $value){
139                // Skip non-updatable columns for inserts too
140                if (in_array($name, $this->_nonUpdatableColumns)) {
141                    continue;
142                }
143                $column_list[] = "`$name`";
144                $value_list[] = ":$name";
145                $sqlVars[":$name"] = $value;
146            }
147        
148            $column_clause = implode(",", $column_list);            
149            $value_clause = implode(",", $value_list);
150            
151            $query = "
152                INSERT INTO `$table`
153                ( $column_clause )
154                VALUES ( $value_clause );";
155        
156            $stmt = $db->prepare($query);
157            $stmt->execute($sqlVars);
158            $this->_id = $db->lastInsertId();
159        }
160        return $this->_id;
161    }
162    
163    /*** Delete the object from the database, if it exists
164    ***/
165    public function delete(){
166        // Get connection
167        $db = static::connection();
168        $table = $this->_table->name;
169        
170        // Can only delete an object where `id` is set
171        if (!$this->_id) {
172            return false;
173        }
174        
175        $sqlVars[":id"] = $this->_id;
176        
177        $query = "
178            DELETE FROM `$table`
179            WHERE id = :id";
180            
181        $stmt = $db->prepare($query);
182        $stmt->execute($sqlVars);
183        
184        if ($stmt->rowCount())
185            return true;
186        else
187            return false;
188    }
189}
190
191?>