PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/library/core/class.model.php

https://github.com/ghoppe/Garden
PHP | 481 lines | 188 code | 80 blank | 213 comment | 39 complexity | e2365ae841b463c2ac2a8a44112333c2 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php if (!defined('APPLICATION')) exit();
  2. /*
  3. Copyright 2008, 2009 Vanilla Forums Inc.
  4. This file is part of Garden.
  5. Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  6. Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  7. You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
  8. Contact Vanilla Forums Inc. at support [at] vanillaforums [dot] com
  9. */
  10. /**
  11. * Represents, enforces integrity, and aids in the management of: data. This
  12. * generic model can be instantiated (with the table name it is intended to
  13. * represent) and used directly, or it can be extended and overridden for more
  14. * complicated procedures related to different tables.
  15. *
  16. *
  17. * @author Mark O'Sullivan
  18. * @copyright 2009 Mark O'Sullivan
  19. * @license http://www.opensource.org/licenses/gpl-2.0.php GPL
  20. * @package Garden
  21. * @version @@GARDEN-VERSION@@
  22. * @namespace Garden.Core
  23. */
  24. /**
  25. * Represents, enforces integrity, and aids in the management of: data. This
  26. * generic model can be instantiated (with the table name it is intended to
  27. * represent) and used directly, or it can be extended and overridden for more
  28. * complicated procedures related to different tables.
  29. *
  30. * @package Garden
  31. */
  32. class Gdn_Model extends Gdn_Pluggable {
  33. /**
  34. * An object representation of the current working dataset.
  35. *
  36. * @var Gdn_DataSet
  37. */
  38. public $Data;
  39. /**
  40. * Database object
  41. *
  42. * @var Gdn_Database The database object.
  43. */
  44. public $Database;
  45. /**
  46. * The name of the field that stores the insert date for a record. This
  47. * field will be automatically filled by the model if it exists.
  48. *
  49. * @var string
  50. */
  51. public $DateInserted = 'DateInserted';
  52. /**
  53. * The name of the field that stores the update date for a record. This
  54. * field will be automatically filled by the model if it exists.
  55. *
  56. * @var string
  57. */
  58. public $DateUpdated = 'DateUpdated';
  59. /**
  60. * The name of the field that stores the id of the user that inserted it.
  61. * This field will be automatically filled by the model if it exists and
  62. * @@Session::UserID is a valid integer.
  63. *
  64. * @var string
  65. */
  66. public $InsertUserID = 'InsertUserID';
  67. /**
  68. * The name of the table that this model is intended to represent. The
  69. * default value assigned to $this->Name will be the name that the
  70. * model was instantiated with (defined in $this->__construct()).
  71. *
  72. * @var string
  73. */
  74. public $Name;
  75. /**
  76. * The name of the primary key field of this model. The default is 'id'. If
  77. * $this->DefineSchema() is called, this value will be automatically changed
  78. * to any primary key discovered when examining the table schema.
  79. *
  80. * @var string
  81. */
  82. public $PrimaryKey = 'id';
  83. /**
  84. * An object that is used to store and examine database schema information
  85. * related to this model. This object is defined and populated with
  86. * $this->DefineSchema().
  87. *
  88. * @var Gdn_Schema
  89. */
  90. public $Schema;
  91. /**
  92. * Contains the sql driver for the object.
  93. *
  94. * @var Gdn_SQLDriver
  95. */
  96. public $SQL;
  97. /**
  98. * The name of the field that stores the id of the user that updated it.
  99. * This field will be automatically filled by the model if it exists and
  100. * @@Session::UserID is a valid integer.
  101. *
  102. * @var string
  103. */
  104. public $UpdateUserID = 'UpdateUserID';
  105. /**
  106. * An object that is used to manage and execute data integrity rules on this
  107. * object. By default, this object only enforces maxlength, data types, and
  108. * required fields (defined when $this->DefineSchema() is called).
  109. *
  110. * @var Gdn_Validation
  111. */
  112. public $Validation;
  113. /**
  114. * Class constructor. Defines the related database table name.
  115. *
  116. * @param string $Name An optional parameter that allows you to explicitly define the name of
  117. * the table that this model represents. You can also explicitly set this
  118. * value with $this->Name.
  119. */
  120. public function __construct($Name = '') {
  121. if ($Name == '')
  122. $Name = get_class($this);
  123. $this->Database = Gdn::Database();
  124. $this->SQL = $this->Database->SQL();
  125. $this->Validation = new Gdn_Validation();
  126. $this->Name = $Name;
  127. parent::__construct();
  128. }
  129. /**
  130. * Connects to the database and defines the schema associated with
  131. * $this->Name. Also instantiates and automatically defines
  132. * $this->Validation.
  133. *
  134. */
  135. public function DefineSchema() {
  136. if (!isset($this->Schema)) {
  137. $this->Schema = new Gdn_Schema($this->Name, $this->Database);
  138. $this->PrimaryKey = $this->Schema->PrimaryKey($this->Name, $this->Database);
  139. if (is_array($this->PrimaryKey)) {
  140. //print_r($this->PrimaryKey);
  141. $this->PrimaryKey = $this->PrimaryKey[0];
  142. }
  143. $this->Validation->ApplyRulesBySchema($this->Schema);
  144. }
  145. }
  146. /**
  147. * Takes a set of form data ($Form->_PostValues), validates them, and
  148. * inserts or updates them to the datatabase.
  149. *
  150. * @param array $FormPostValues An associative array of $Field => $Value pairs that represent data posted
  151. * from the form in the $_POST or $_GET collection.
  152. * @param array $Settings If a custom model needs special settings in order to perform a save, they
  153. * would be passed in using this variable as an associative array.
  154. * @return unknown
  155. */
  156. public function Save($FormPostValues, $Settings = FALSE) {
  157. // Define the primary key in this model's table.
  158. $this->DefineSchema();
  159. // See if a primary key value was posted and decide how to save
  160. $PrimaryKeyVal = ArrayValue($this->PrimaryKey, $FormPostValues);
  161. $Insert = $PrimaryKeyVal === FALSE ? TRUE : FALSE;
  162. if ($Insert) {
  163. $this->AddInsertFields($FormPostValues);
  164. } else {
  165. $this->AddUpdateFields($FormPostValues);
  166. }
  167. // Validate the form posted values
  168. if ($this->Validate($FormPostValues, $Insert) === TRUE) {
  169. $Fields = $this->Validation->ValidationFields();
  170. $Fields = RemoveKeyFromArray($Fields, $this->PrimaryKey); // Don't try to insert or update the primary key
  171. if ($Insert === FALSE) {
  172. $this->Update($Fields, array($this->PrimaryKey => $PrimaryKeyVal));
  173. } else {
  174. $PrimaryKeyVal = $this->Insert($Fields);
  175. }
  176. } else {
  177. $PrimaryKeyVal = FALSE;
  178. }
  179. return $PrimaryKeyVal;
  180. }
  181. /**
  182. * @param unknown_type $Fields
  183. * @return unknown
  184. * @todo add doc
  185. */
  186. public function Insert($Fields) {
  187. $Result = FALSE;
  188. $this->AddInsertFields($Fields);
  189. if ($this->Validate($Fields, TRUE)) {
  190. // Strip out fields that aren't in the schema.
  191. // This is done after validation to allow custom validations to work.
  192. $SchemaFields = $this->Schema->Fields();
  193. $Fields = array_intersect_key($Fields, $SchemaFields);
  194. // Quote all of the fields.
  195. $QuotedFields = array();
  196. foreach ($Fields as $Name => $Value) {
  197. $QuotedFields[$this->SQL->QuoteIdentifier(trim($Name, '`'))] = $Value;
  198. }
  199. $Result = $this->SQL->Insert($this->Name, $QuotedFields);
  200. }
  201. return $Result;
  202. }
  203. /**
  204. * @param unknown_type $Fields
  205. * @param unknown_type $Where
  206. * @param unknown_type $Limit
  207. * @todo add doc
  208. */
  209. public function Update($Fields, $Where = FALSE, $Limit = FALSE) {
  210. $Result = FALSE;
  211. // primary key (always included in $Where when updating) might be "required"
  212. $AllFields = $Fields;
  213. if (is_array($Where))
  214. $AllFields = array_merge($Fields, $Where);
  215. if ($this->Validate($AllFields)) {
  216. $this->AddUpdateFields($Fields);
  217. // Strip out fields that aren't in the schema.
  218. // This is done after validation to allow custom validations to work.
  219. $SchemaFields = $this->Schema->Fields();
  220. $Fields = array_intersect_key($Fields, $SchemaFields);
  221. // Quote all of the fields.
  222. $QuotedFields = array();
  223. foreach ($Fields as $Name => $Value) {
  224. $QuotedFields[$this->SQL->QuoteIdentifier(trim($Name, '`'))] = $Value;
  225. }
  226. $Result = $this->SQL->Put($this->Name, $QuotedFields, $Where, $Limit);
  227. }
  228. return $Result;
  229. }
  230. /**
  231. * @param unknown_type $Where
  232. * @param unknown_type $Limit
  233. * @param unknown_type $ResetData
  234. * @todo add doc
  235. */
  236. public function Delete($Where = '', $Limit = FALSE, $ResetData = FALSE) {
  237. if(is_numeric($Where))
  238. $Where = array($this->Name.'ID' => $Where);
  239. if($ResetData) {
  240. $this->SQL->Delete($this->Name, $Where, $Limit);
  241. } else {
  242. $this->SQL->NoReset()->Delete($this->Name, $Where, $Limit);
  243. }
  244. }
  245. /**
  246. * Returns an array with only those keys that are actually in the schema.
  247. *
  248. * @param array $Data An array of key/value pairs.
  249. * @return array The filtered array.
  250. */
  251. public function FilterSchema($Data) {
  252. $Fields = $this->Schema->Fields($this->Name);
  253. $Result = array_intersect_key($Data, $Fields);
  254. return $Result;
  255. }
  256. /**
  257. * @param unknown_type $OrderFields
  258. * @param unknown_type $OrderDirection
  259. * @param unknown_type $Limit
  260. * @param unknown_type $Offset
  261. * @return unknown
  262. * @todo add doc
  263. */
  264. public function Get($OrderFields = '', $OrderDirection = 'asc', $Limit = FALSE, $Offset = FALSE) {
  265. return $this->SQL->Get($this->Name, $OrderFields, $OrderDirection, $Limit, $Offset);
  266. }
  267. /**
  268. * Returns a count of the # of records in the table
  269. * @param array $Wheres
  270. */
  271. public function GetCount($Wheres = '') {
  272. $this->SQL
  273. ->Select('*', 'count', 'Count')
  274. ->From($this->Name);
  275. if (is_array($Wheres))
  276. $this->SQL->Where($Wheres);
  277. $Data = $this->SQL
  278. ->Get()
  279. ->FirstRow();
  280. return $Data === FALSE ? 0 : $Data->Count;
  281. }
  282. /**
  283. * Get the data from the model based on its primary key.
  284. *
  285. * @param mixed $ID The value of the primary key in the database.
  286. * @param string $DatasetType The format of the result dataset.
  287. * @return Gdn_DataSet
  288. */
  289. public function GetID($ID, $DatasetType = FALSE) {
  290. $Result = $this->GetWhere(array("{$this->Name}ID" => $ID))->FirstRow($DatasetType);
  291. return $Result;
  292. }
  293. /**
  294. * @param unknown_type $Where
  295. * @param unknown_type $OrderFields
  296. * @param unknown_type $OrderDirection
  297. * @param unknown_type $Limit
  298. * @param unknown_type $Offset
  299. * @return Gdn_DataSet
  300. * @todo add doc
  301. */
  302. public function GetWhere($Where = FALSE, $OrderFields = '', $OrderDirection = 'asc', $Limit = FALSE, $Offset = FALSE) {
  303. return $this->SQL->GetWhere($this->Name, $Where, $OrderFields, $OrderDirection, $Limit, $Offset);
  304. }
  305. /**
  306. * Returns the $this->Validation->ValidationResults() array.
  307. *
  308. * @return unknown
  309. * @todo add return type
  310. */
  311. public function ValidationResults() {
  312. return $this->Validation->Results();
  313. }
  314. /**
  315. * @param unknown_type $FormPostValues
  316. * @param unknown_type $Insert
  317. * @return unknown
  318. * @todo add doc
  319. */
  320. public function Validate($FormPostValues, $Insert = FALSE) {
  321. $this->DefineSchema();
  322. return $this->Validation->Validate($FormPostValues, $Insert);
  323. }
  324. /**
  325. * Adds $this->InsertUserID and $this->DateInserted fields to an associative
  326. * array of fieldname/values if those fields exist on the table being
  327. * inserted.
  328. *
  329. * @param array $Fields The array of fields to add the values to.
  330. */
  331. protected function AddInsertFields(&$Fields) {
  332. $this->DefineSchema();
  333. if ($this->Schema->FieldExists($this->Name, $this->DateInserted)) {
  334. if (!isset($Fields[$this->DateInserted]))
  335. $Fields[$this->DateInserted] = Gdn_Format::ToDateTime();
  336. }
  337. $Session = Gdn::Session();
  338. if ($Session->UserID > 0 && $this->Schema->FieldExists($this->Name, $this->InsertUserID))
  339. if (!isset($Fields[$this->InsertUserID]))
  340. $Fields[$this->InsertUserID] = $Session->UserID;
  341. }
  342. /**
  343. * Adds $this->UpdateUserID and $this->DateUpdated fields to an associative
  344. * array of fieldname/values if those fields exist on the table being
  345. * updated.
  346. *
  347. * @param array $Fields The array of fields to add the values to.
  348. */
  349. protected function AddUpdateFields(&$Fields) {
  350. $this->DefineSchema();
  351. if ($this->Schema->FieldExists($this->Name, $this->DateUpdated)) {
  352. if (!isset($Fields[$this->DateUpdated]))
  353. $Fields[$this->DateUpdated] = Gdn_Format::ToDateTime();
  354. }
  355. $Session = Gdn::Session();
  356. if ($Session->UserID > 0 && $this->Schema->FieldExists($this->Name, $this->UpdateUserID))
  357. if (!isset($Fields[$this->UpdateUserID]))
  358. $Fields[$this->UpdateUserID] = $Session->UserID;
  359. }
  360. public function SaveToSerializedColumn($Column, $RowID, $Name, $Value = '') {
  361. if (!isset($this->Schema)) $this->DefineSchema();
  362. // TODO: need to be sure that $this->PrimaryKey is only one primary key
  363. $FieldName = $this->PrimaryKey;
  364. // Load the existing values
  365. $Row = $this->SQL
  366. ->Select($Column)
  367. ->From($this->Name)
  368. ->Where($FieldName, $RowID)
  369. ->Get()
  370. ->FirstRow();
  371. if(!$Row) throw new Exception(T('ErrorRecordNotFound'));
  372. $Values = Gdn_Format::Unserialize($Row->$Column);
  373. if (is_string($Values) && $Values != '')
  374. throw new Exception(T('Serialized column failed to be unserialized.'));
  375. if (!is_array($Values)) $Values = array();
  376. if (!is_array($Name)) $Name = array($Name => $Value); // Assign the new value(s)
  377. $Values = Gdn_Format::Serialize(array_merge($Values, $Name));
  378. // Save the values back to the db
  379. return $this->SQL
  380. ->From($this->Name)
  381. ->Where($FieldName, $RowID)
  382. ->Set($Column, $Values)
  383. ->Put();
  384. }
  385. public function SetProperty($RowID, $Property, $ForceValue = FALSE) {
  386. if (!isset($this->Schema)) $this->DefineSchema();
  387. $PrimaryKey = $this->PrimaryKey;
  388. if ($ForceValue !== FALSE) {
  389. $Value = $ForceValue;
  390. } else {
  391. $Row = $this->GetID($RowID);
  392. $Value = ($Row->$Property == '1' ? '0' : '1');
  393. }
  394. $this->SQL
  395. ->Update($this->Name)
  396. ->Set($Property, $Value)
  397. ->Where($PrimaryKey, $RowID)
  398. ->Put();
  399. return $Value;
  400. }
  401. }