1<?php 2 3namespace LibreNMS; 4 5abstract class Model 6{ 7 protected static $table; 8 protected static $primaryKey = 'id'; 9 10 public static function create(array $data) 11 { 12 $instance = new static(); 13 $instance->fill($data); 14 15 return $instance; 16 } 17 18 protected function fill(array $data = []) 19 { 20 foreach ($data as $field => $value) { 21 $this->$field = $value; 22 } 23 } 24 25 /** 26 * Save Models and remove invalid Models 27 * This the sensors array should contain all the sensors of a specific class 28 * It may contain sensors from multiple tables and devices, but that isn't the primary use 29 * 30 * @param int $device_id 31 * @param array $models 32 * @param array $unique_fields fields to search for an existing entry 33 * @param array $ignored_update_fields Don't compare these field when updating 34 */ 35 final public static function sync($device_id, array $models, $unique_fields = [], $ignored_update_fields = []) 36 { 37 // save and collect valid ids 38 $valid_ids = []; 39 foreach ($models as $model) { 40 /** @var $this $model */ 41 if ($model->isValid()) { 42 $valid_ids[] = $model->save($unique_fields, $ignored_update_fields); 43 } 44 } 45 46 // delete invalid sensors 47 self::clean($device_id, $valid_ids); 48 } 49 50 /** 51 * Remove invalid Models. Passing an empty array will remove all models related to $device_id 52 * 53 * @param int $device_id 54 * @param array $model_ids valid Model ids 55 */ 56 protected static function clean($device_id, $model_ids) 57 { 58 $table = static::$table; 59 $key = static::$primaryKey; 60 61 $params = [$device_id]; 62 $where = '`device_id`=?'; 63 64 if (! empty($model_ids)) { 65 $where .= " AND `$key` NOT IN " . dbGenPlaceholders(count($model_ids)); 66 $params = array_merge($params, $model_ids); 67 } 68 69 $rows = dbFetchRows("SELECT * FROM `$table` WHERE $where", $params); 70 foreach ($rows as $row) { 71 static::onDelete(static::create($row)); 72 } 73 if (! empty($rows)) { 74 dbDelete($table, $where, $params); 75 } 76 } 77 78 /** 79 * Save this Model to the database. 80 * 81 * @param array $unique_fields fields to search for an existing entry 82 * @param array $ignored_update_fields Don't compare these field when updating 83 * @return int the id of this model in the database 84 */ 85 final public function save($unique_fields = [], $ignored_update_fields = []) 86 { 87 $db_model = $this->fetch($unique_fields); 88 $key = static::$primaryKey; 89 90 if ($db_model) { 91 $new_model = $this->toArray(array_merge([$key], $ignored_update_fields)); 92 $update = array_diff($new_model, $db_model); 93 94 if (empty($update)) { 95 static::onNoUpdate(); 96 } else { 97 dbUpdate($update, static::$table, "`$key`=?", [$this->$key]); 98 static::onUpdate($this); 99 } 100 } else { 101 $new_model = $this->toArray([$key]); 102 $this->$key = dbInsert($new_model, static::$table); 103 if ($this->$key !== null) { 104 static::onCreate($this); 105 } 106 } 107 108 return $this->$key; 109 } 110 111 /** 112 * Fetch the sensor from the database. 113 * If it doesn't exist, returns null. 114 * 115 * @param array $unique_fields fields to search for an existing entry 116 * @return array|null 117 */ 118 protected function fetch($unique_fields = []) 119 { 120 $table = static::$table; 121 $key = static::$primaryKey; 122 123 if (isset($this->id)) { 124 return dbFetchRow( 125 "SELECT `$table` FROM ? WHERE `$key`=?", 126 [$this->$key] 127 ); 128 } 129 130 $where = []; 131 $params = []; 132 foreach ($unique_fields as $field) { 133 if (isset($this->$field)) { 134 $where[] = " $field=?"; 135 $params[] = $this->$field; 136 } 137 } 138 139 if (empty($params)) { 140 return null; 141 } 142 143 $row = dbFetchRow( 144 "SELECT * FROM `$table` WHERE " . implode(' AND', $where), 145 $params 146 ); 147 148 $this->$key = $row[$key]; 149 150 return $row; 151 } 152 153 /** 154 * Convert this Model to an array with fields that match the database 155 * 156 * @param array $exclude Exclude the listed fields 157 * @return array 158 */ 159 abstract public function toArray($exclude = []); 160 161 /** 162 * Returns if this model passes validation and should be saved to the database 163 * 164 * @return bool 165 */ 166 abstract public function isValid(); 167 168 /** 169 * @param static $model 170 */ 171 public static function onDelete($model) 172 { 173 if (\App::runningInConsole()) { 174 echo '-'; 175 } 176 } 177 178 /** 179 * @param static $model 180 */ 181 public static function onCreate($model) 182 { 183 if (\App::runningInConsole()) { 184 echo '+'; 185 } 186 } 187 188 /** 189 * @param static $model 190 */ 191 public static function onUpdate($model) 192 { 193 if (\App::runningInConsole()) { 194 echo 'U'; 195 } 196 } 197 198 public static function onNoUpdate() 199 { 200 if (\App::runningInConsole()) { 201 echo '.'; 202 } 203 } 204} 205