Aucune description

paris.php 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. <?php
  2. namespace MailPoetVendor\Paris;
  3. if (!defined('ABSPATH')) exit;
  4. use Exception;
  5. use MailPoetVendor\Idiorm\ORM;
  6. /**
  7. *
  8. * Paris
  9. *
  10. * http://github.com/j4mie/paris/
  11. *
  12. * A simple Active Record implementation built on top of Idiorm
  13. * ( http://github.com/j4mie/idiorm/ ).
  14. *
  15. * You should include Idiorm before you include this file:
  16. * require_once 'your/path/to/idiorm.php';
  17. *
  18. * BSD Licensed.
  19. *
  20. * Copyright (c) 2010, Jamie Matthews
  21. * All rights reserved.
  22. *
  23. * Redistribution and use in source and binary forms, with or without
  24. * modification, are permitted provided that the following conditions are met:
  25. *
  26. * * Redistributions of source code must retain the above copyright notice, this
  27. * list of conditions and the following disclaimer.
  28. *
  29. * * Redistributions in binary form must reproduce the above copyright notice,
  30. * this list of conditions and the following disclaimer in the documentation
  31. * and/or other materials provided with the distribution.
  32. *
  33. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  34. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  35. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  36. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  37. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  38. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  39. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  41. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  42. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. *
  44. */
  45. /**
  46. * Subclass of Idiorm's ORM class that supports
  47. * returning instances of a specified class rather
  48. * than raw instances of the ORM class.
  49. *
  50. * You shouldn't need to interact with this class
  51. * directly. It is used internally by the Model base
  52. * class.
  53. *
  54. *
  55. * The methods documented below are magic methods that conform to PSR-1.
  56. * This documentation exposes these methods to doc generators and IDEs.
  57. * @see http://www.php-fig.org/psr/psr-1/
  58. *
  59. * @method void setClassName($class_name)
  60. * @method static \ORMWrapper forTable($table_name, $connection_name = parent::DEFAULT_CONNECTION)
  61. * @method \Model findOne($id=null)
  62. * @method Array|\MailPoetVendor\Idiorm\IdiormResultSet findMany()
  63. */
  64. class ORMWrapper extends ORM {
  65. /**
  66. * The wrapped find_one and find_many classes will
  67. * return an instance or instances of this class.
  68. *
  69. * @var string $_class_name
  70. */
  71. protected $_class_name;
  72. /**
  73. * Set the name of the class which the wrapped
  74. * methods should return instances of.
  75. *
  76. * @param string $class_name
  77. * @return void
  78. */
  79. public function set_class_name($class_name) {
  80. $this->_class_name = $class_name;
  81. }
  82. /**
  83. * Add a custom filter to the method chain specified on the
  84. * model class. This allows custom queries to be added
  85. * to models. The filter should take an instance of the
  86. * ORM wrapper as its first argument and return an instance
  87. * of the ORM wrapper. Any arguments passed to this method
  88. * after the name of the filter will be passed to the called
  89. * filter function as arguments after the ORM class.
  90. *
  91. * @return ORMWrapper
  92. */
  93. public function filter() {
  94. $args = func_get_args();
  95. $filter_function = array_shift($args);
  96. array_unshift($args, $this);
  97. if (method_exists($this->_class_name, $filter_function)) {
  98. return call_user_func_array(array($this->_class_name, $filter_function), $args);
  99. }
  100. }
  101. /**
  102. * Factory method, return an instance of this
  103. * class bound to the supplied table name.
  104. *
  105. * A repeat of content in parent::for_table, so that
  106. * created class is ORMWrapper, not ORM
  107. *
  108. * @param string $table_name
  109. * @param string $connection_name
  110. * @return ORMWrapper
  111. */
  112. public static function for_table($table_name, $connection_name = parent::DEFAULT_CONNECTION) {
  113. self::_setup_db($connection_name);
  114. return new self($table_name, array(), $connection_name);
  115. }
  116. /**
  117. * Method to create an instance of the model class
  118. * associated with this wrapper and populate
  119. * it with the supplied Idiorm instance.
  120. *
  121. * @param ORM $orm
  122. * @return bool|Model
  123. */
  124. protected function _create_model_instance($orm) {
  125. if ($orm === false) {
  126. return false;
  127. }
  128. $model = new $this->_class_name();
  129. $model->set_orm($orm);
  130. return $model;
  131. }
  132. /**
  133. * Wrap Idiorm's find_one method to return
  134. * an instance of the class associated with
  135. * this wrapper instead of the raw ORM class.
  136. *
  137. * @param null|integer $id
  138. * @return Model
  139. */
  140. public function find_one($id=null) {
  141. return $this->_create_model_instance(parent::find_one($id));
  142. }
  143. /**
  144. * Wrap Idiorm's find_many method to return
  145. * an array of instances of the class associated
  146. * with this wrapper instead of the raw ORM class.
  147. *
  148. * @return Array
  149. */
  150. public function find_many() {
  151. $results = parent::find_many();
  152. foreach($results as $key => $result) {
  153. $results[$key] = $this->_create_model_instance($result);
  154. }
  155. return $results;
  156. }
  157. /**
  158. * Wrap Idiorm's create method to return an
  159. * empty instance of the class associated with
  160. * this wrapper instead of the raw ORM class.
  161. *
  162. * @return ORMWrapper|bool
  163. */
  164. public function create($data=null) {
  165. return $this->_create_model_instance(parent::create($data));
  166. }
  167. }
  168. /**
  169. * Model base class. Your model objects should extend
  170. * this class. A minimal subclass would look like:
  171. *
  172. * class Widget extends Model {
  173. * }
  174. *
  175. *
  176. * The methods documented below are magic methods that conform to PSR-1.
  177. * This documentation exposes these methods to doc generators and IDEs.
  178. * @see http://www.php-fig.org/psr/psr-1/
  179. *
  180. * @method void setOrm($orm)
  181. * @method $this setExpr($property, $value = null)
  182. * @method bool isDirty($property)
  183. * @method bool isNew()
  184. * @method Array asArray()
  185. */
  186. class Model {
  187. // Default ID column for all models. Can be overridden by adding
  188. // a public static _id_column property to your model classes.
  189. const DEFAULT_ID_COLUMN = 'id';
  190. // Default foreign key suffix used by relationship methods
  191. const DEFAULT_FOREIGN_KEY_SUFFIX = '_id';
  192. /**
  193. * Set a prefix for model names. This can be a namespace or any other
  194. * abitrary prefix such as the PEAR naming convention.
  195. *
  196. * @example Model::$auto_prefix_models = 'MyProject_MyModels_'; //PEAR
  197. * @example Model::$auto_prefix_models = '\MyProject\MyModels\'; //Namespaces
  198. *
  199. * @var string $auto_prefix_models
  200. */
  201. public static $auto_prefix_models = null;
  202. /**
  203. * Set true to to ignore namespace information when computing table names
  204. * from class names.
  205. *
  206. * @example Model::$short_table_names = true;
  207. * @example Model::$short_table_names = false; // default
  208. *
  209. * @var bool $short_table_names
  210. */
  211. public static $short_table_names = false;
  212. /**
  213. * The ORM instance used by this model
  214. * instance to communicate with the database.
  215. *
  216. * @var ORM $orm
  217. */
  218. public $orm;
  219. /**
  220. * Retrieve the value of a static property on a class. If the
  221. * class or the property does not exist, returns the default
  222. * value supplied as the third argument (which defaults to null).
  223. *
  224. * @param string $class_name
  225. * @param string $property
  226. * @param null|string $default
  227. * @return string
  228. */
  229. protected static function _get_static_property($class_name, $property, $default=null) {
  230. if (!class_exists($class_name) || !property_exists($class_name, $property)) {
  231. return $default;
  232. }
  233. $properties = get_class_vars($class_name);
  234. return $properties[$property];
  235. }
  236. /**
  237. * Static method to get a table name given a class name.
  238. * If the supplied class has a public static property
  239. * named $_table, the value of this property will be
  240. * returned.
  241. *
  242. * If not, the class name will be converted using
  243. * the _class_name_to_table_name method method.
  244. *
  245. * If Model::$short_table_names == true or public static
  246. * property $_table_use_short_name == true then $class_name passed
  247. * to _class_name_to_table_name is stripped of namespace information.
  248. *
  249. * @param string $class_name
  250. *
  251. *@return string
  252. */
  253. protected static function _get_table_name($class_name) {
  254. $specified_table_name = self::_get_static_property($class_name, '_table');
  255. $use_short_class_name = self::_use_short_table_name($class_name);
  256. if ($use_short_class_name) {
  257. $exploded_class_name = explode('\\', $class_name);
  258. $class_name = end($exploded_class_name);
  259. }
  260. if (is_null($specified_table_name)) {
  261. return self::_class_name_to_table_name($class_name);
  262. }
  263. return $specified_table_name;
  264. }
  265. /**
  266. * Should short table names, disregarding class namespaces, be computed?
  267. *
  268. * $class_property overrides $global_option, unless $class_property is null
  269. *
  270. * @param string $class_name
  271. * @return bool
  272. */
  273. protected static function _use_short_table_name($class_name) {
  274. $global_option = self::$short_table_names;
  275. $class_property = self::_get_static_property($class_name, '_table_use_short_name');
  276. return is_null($class_property) ? $global_option : $class_property;
  277. }
  278. /**
  279. * Convert a namespace to the standard PEAR underscore format.
  280. *
  281. * Then convert a class name in CapWords to a table name in
  282. * lowercase_with_underscores.
  283. *
  284. * Finally strip doubled up underscores
  285. *
  286. * For example, CarTyre would be converted to car_tyre. And
  287. * Project\Models\CarTyre would be project_models_car_tyre.
  288. *
  289. * @param string $class_name
  290. * @return string
  291. */
  292. protected static function _class_name_to_table_name($class_name) {
  293. return strtolower(preg_replace(
  294. array('/\\\\/', '/(?<=[a-z])([A-Z])/', '/__/'),
  295. array('_', '_$1', '_'),
  296. ltrim($class_name, '\\')
  297. ));
  298. }
  299. /**
  300. * Return the ID column name to use for this class. If it is
  301. * not set on the class, returns null.
  302. *
  303. * @param string $class_name
  304. * @return string|null
  305. */
  306. protected static function _get_id_column_name($class_name) {
  307. return self::_get_static_property($class_name, '_id_column', self::DEFAULT_ID_COLUMN);
  308. }
  309. /**
  310. * Build a foreign key based on a table name. If the first argument
  311. * (the specified foreign key column name) is null, returns the second
  312. * argument (the name of the table) with the default foreign key column
  313. * suffix appended.
  314. *
  315. * @param string $specified_foreign_key_name
  316. * @param string $table_name
  317. * @return string
  318. */
  319. protected static function _build_foreign_key_name($specified_foreign_key_name, $table_name) {
  320. if (!is_null($specified_foreign_key_name)) {
  321. return $specified_foreign_key_name;
  322. }
  323. return $table_name . self::DEFAULT_FOREIGN_KEY_SUFFIX;
  324. }
  325. /**
  326. * Factory method used to acquire instances of the given class.
  327. * The class name should be supplied as a string, and the class
  328. * should already have been loaded by PHP (or a suitable autoloader
  329. * should exist). This method actually returns a wrapped ORM object
  330. * which allows a database query to be built. The wrapped ORM object is
  331. * responsible for returning instances of the correct class when
  332. * its find_one or find_many methods are called.
  333. *
  334. * @param string $class_name
  335. * @param null|string $connection_name
  336. * @return ORMWrapper
  337. */
  338. public static function factory($class_name, $connection_name = null) {
  339. $class_name = self::$auto_prefix_models . $class_name;
  340. $table_name = self::_get_table_name($class_name);
  341. if ($connection_name == null) {
  342. $connection_name = self::_get_static_property(
  343. $class_name,
  344. '_connection_name',
  345. ORMWrapper::DEFAULT_CONNECTION
  346. );
  347. }
  348. $wrapper = ORMWrapper::for_table($table_name, $connection_name);
  349. $wrapper->set_class_name($class_name);
  350. $wrapper->use_id_column(self::_get_id_column_name($class_name));
  351. return $wrapper;
  352. }
  353. /**
  354. * Internal method to construct the queries for both the has_one and
  355. * has_many methods. These two types of association are identical; the
  356. * only difference is whether find_one or find_many is used to complete
  357. * the method chain.
  358. *
  359. * @param string $associated_class_name
  360. * @param null|string $foreign_key_name
  361. * @param null|string $foreign_key_name_in_current_models_table
  362. * @param null|string $connection_name
  363. * @return ORMWrapper
  364. */
  365. protected function _has_one_or_many($associated_class_name, $foreign_key_name=null, $foreign_key_name_in_current_models_table=null, $connection_name=null) {
  366. $base_table_name = self::_get_table_name(get_class($this));
  367. $foreign_key_name = self::_build_foreign_key_name($foreign_key_name, $base_table_name);
  368. $where_value = ''; //Value of foreign_table.{$foreign_key_name} we're
  369. //looking for. Where foreign_table is the actual
  370. //database table in the associated model.
  371. if(is_null($foreign_key_name_in_current_models_table)) {
  372. //Match foreign_table.{$foreign_key_name} with the value of
  373. //{$this->_table}.{$this->id()}
  374. $where_value = $this->id();
  375. } else {
  376. //Match foreign_table.{$foreign_key_name} with the value of
  377. //{$this->_table}.{$foreign_key_name_in_current_models_table}
  378. $where_value = $this->$foreign_key_name_in_current_models_table;
  379. }
  380. return self::factory($associated_class_name, $connection_name)->where($foreign_key_name, $where_value);
  381. }
  382. /**
  383. * Helper method to manage one-to-one relations where the foreign
  384. * key is on the associated table.
  385. *
  386. * @param string $associated_class_name
  387. * @param null|string $foreign_key_name
  388. * @param null|string $foreign_key_name_in_current_models_table
  389. * @param null|string $connection_name
  390. * @return ORMWrapper
  391. */
  392. protected function has_one($associated_class_name, $foreign_key_name=null, $foreign_key_name_in_current_models_table=null, $connection_name=null) {
  393. return $this->_has_one_or_many($associated_class_name, $foreign_key_name, $foreign_key_name_in_current_models_table, $connection_name);
  394. }
  395. /**
  396. * Helper method to manage one-to-many relations where the foreign
  397. * key is on the associated table.
  398. *
  399. * @param string $associated_class_name
  400. * @param null|string $foreign_key_name
  401. * @param null|string $foreign_key_name_in_current_models_table
  402. * @param null|string $connection_name
  403. * @return ORMWrapper
  404. */
  405. protected function has_many($associated_class_name, $foreign_key_name=null, $foreign_key_name_in_current_models_table=null, $connection_name=null) {
  406. return $this->_has_one_or_many($associated_class_name, $foreign_key_name, $foreign_key_name_in_current_models_table, $connection_name);
  407. }
  408. /**
  409. * Helper method to manage one-to-one and one-to-many relations where
  410. * the foreign key is on the base table.
  411. *
  412. * @param string $associated_class_name
  413. * @param null|string $foreign_key_name
  414. * @param null|string $foreign_key_name_in_associated_models_table
  415. * @param null|string $connection_name
  416. * @return $this|null
  417. */
  418. protected function belongs_to($associated_class_name, $foreign_key_name=null, $foreign_key_name_in_associated_models_table=null, $connection_name=null) {
  419. $associated_table_name = self::_get_table_name(self::$auto_prefix_models . $associated_class_name);
  420. $foreign_key_name = self::_build_foreign_key_name($foreign_key_name, $associated_table_name);
  421. $associated_object_id = $this->$foreign_key_name;
  422. $desired_record = null;
  423. if( is_null($foreign_key_name_in_associated_models_table) ) {
  424. //"{$associated_table_name}.primary_key = {$associated_object_id}"
  425. //NOTE: primary_key is a placeholder for the actual primary key column's name
  426. //in $associated_table_name
  427. $desired_record = self::factory($associated_class_name, $connection_name)->where_id_is($associated_object_id);
  428. } else {
  429. //"{$associated_table_name}.{$foreign_key_name_in_associated_models_table} = {$associated_object_id}"
  430. $desired_record = self::factory($associated_class_name, $connection_name)->where($foreign_key_name_in_associated_models_table, $associated_object_id);
  431. }
  432. return $desired_record;
  433. }
  434. /**
  435. * Helper method to manage many-to-many relationships via an intermediate model. See
  436. * README for a full explanation of the parameters.
  437. *
  438. * @param string $associated_class_name
  439. * @param null|string $join_class_name
  440. * @param null|string $key_to_base_table
  441. * @param null|string $key_to_associated_table
  442. * @param null|string $key_in_base_table
  443. * @param null|string $key_in_associated_table
  444. * @param null|string $connection_name
  445. * @return ORMWrapper
  446. */
  447. protected function has_many_through($associated_class_name, $join_class_name=null, $key_to_base_table=null, $key_to_associated_table=null, $key_in_base_table=null, $key_in_associated_table=null, $connection_name=null) {
  448. $base_class_name = get_class($this);
  449. // The class name of the join model, if not supplied, is
  450. // formed by concatenating the names of the base class
  451. // and the associated class, in alphabetical order.
  452. if (is_null($join_class_name)) {
  453. $base_model = explode('\\', $base_class_name);
  454. $base_model_name = end($base_model);
  455. if (substr($base_model_name, 0, strlen(self::$auto_prefix_models)) == self::$auto_prefix_models) {
  456. $base_model_name = substr($base_model_name, strlen(self::$auto_prefix_models), strlen($base_model_name));
  457. }
  458. // Paris wasn't checking the name settings for the associated class.
  459. $associated_model = explode('\\', $associated_class_name);
  460. $associated_model_name = end($associated_model);
  461. if (substr($associated_model_name, 0, strlen(self::$auto_prefix_models)) == self::$auto_prefix_models) {
  462. $associated_model_name = substr($associated_model_name, strlen(self::$auto_prefix_models), strlen($associated_model_name));
  463. }
  464. $class_names = array($base_model_name, $associated_model_name);
  465. sort($class_names, SORT_STRING);
  466. $join_class_name = implode('', $class_names);
  467. }
  468. // Get table names for each class
  469. $base_table_name = self::_get_table_name($base_class_name);
  470. $associated_table_name = self::_get_table_name(self::$auto_prefix_models . $associated_class_name);
  471. $join_table_name = self::_get_table_name(self::$auto_prefix_models . $join_class_name);
  472. // Get ID column names
  473. $base_table_id_column = (is_null($key_in_base_table)) ?
  474. self::_get_id_column_name($base_class_name) :
  475. $key_in_base_table;
  476. $associated_table_id_column = (is_null($key_in_associated_table)) ?
  477. self::_get_id_column_name(self::$auto_prefix_models . $associated_class_name) :
  478. $key_in_associated_table;
  479. // Get the column names for each side of the join table
  480. $key_to_base_table = self::_build_foreign_key_name($key_to_base_table, $base_table_name);
  481. $key_to_associated_table = self::_build_foreign_key_name($key_to_associated_table, $associated_table_name);
  482. /*
  483. " SELECT {$associated_table_name}.*
  484. FROM {$associated_table_name} JOIN {$join_table_name}
  485. ON {$associated_table_name}.{$associated_table_id_column} = {$join_table_name}.{$key_to_associated_table}
  486. WHERE {$join_table_name}.{$key_to_base_table} = {$this->$base_table_id_column} ;"
  487. */
  488. return self::factory($associated_class_name, $connection_name)
  489. ->select("{$associated_table_name}.*")
  490. ->join($join_table_name, array("{$associated_table_name}.{$associated_table_id_column}", '=', "{$join_table_name}.{$key_to_associated_table}"))
  491. ->where("{$join_table_name}.{$key_to_base_table}", $this->$base_table_id_column); ;
  492. }
  493. /**
  494. * Set the wrapped ORM instance associated with this Model instance.
  495. *
  496. * @param ORM $orm
  497. * @return void
  498. */
  499. public function set_orm($orm) {
  500. $this->orm = $orm;
  501. }
  502. /**
  503. * Magic getter method, allows $model->property access to data.
  504. *
  505. * @param string $property
  506. * @return null|string
  507. */
  508. public function __get($property) {
  509. return $this->orm->get($property);
  510. }
  511. /**
  512. * Magic setter method, allows $model->property = 'value' access to data.
  513. *
  514. * @param string $property
  515. * @param string $value
  516. * @return void
  517. */
  518. public function __set($property, $value) {
  519. $this->orm->set($property, $value);
  520. }
  521. /**
  522. * Magic unset method, allows unset($model->property)
  523. *
  524. * @param string $property
  525. * @return void
  526. */
  527. public function __unset($property) {
  528. $this->orm->__unset($property);
  529. }
  530. /**
  531. * Magic isset method, allows isset($model->property) to work correctly.
  532. *
  533. * @param string $property
  534. * @return bool
  535. */
  536. public function __isset($property) {
  537. return $this->orm->__isset($property);
  538. }
  539. /**
  540. * Getter method, allows $model->get('property') access to data
  541. *
  542. * @param string $property
  543. * @return string
  544. */
  545. public function get($property) {
  546. return $this->orm->get($property);
  547. }
  548. /**
  549. * Setter method, allows $model->set('property', 'value') access to data.
  550. *
  551. * @param string|array $property
  552. * @param string|null $value
  553. * @return Model
  554. */
  555. public function set($property, $value = null) {
  556. $this->orm->set($property, $value);
  557. return $this;
  558. }
  559. /**
  560. * Setter method, allows $model->set_expr('property', 'value') access to data.
  561. *
  562. * @param string|array $property
  563. * @param string|null $value
  564. * @return Model
  565. */
  566. public function set_expr($property, $value = null) {
  567. $this->orm->set_expr($property, $value);
  568. return $this;
  569. }
  570. /**
  571. * Check whether the given field has changed since the object was created or saved
  572. *
  573. * @param string $property
  574. * @return bool
  575. */
  576. public function is_dirty($property) {
  577. return $this->orm->is_dirty($property);
  578. }
  579. /**
  580. * Check whether the model was the result of a call to create() or not
  581. *
  582. * @return bool
  583. */
  584. public function is_new() {
  585. return $this->orm->is_new();
  586. }
  587. /**
  588. * Wrapper for Idiorm's as_array method.
  589. *
  590. * @return Array
  591. */
  592. public function as_array() {
  593. $args = func_get_args();
  594. return call_user_func_array(array($this->orm, 'as_array'), $args);
  595. }
  596. /**
  597. * Save the data associated with this model instance to the database.
  598. *
  599. * @return null
  600. */
  601. public function save() {
  602. return $this->orm->save();
  603. }
  604. /**
  605. * Delete the database row associated with this model instance.
  606. *
  607. * @return null
  608. */
  609. public function delete() {
  610. return $this->orm->delete();
  611. }
  612. /**
  613. * Get the database ID of this model instance.
  614. *
  615. * @return integer
  616. */
  617. public function id() {
  618. return $this->orm->id();
  619. }
  620. /**
  621. * Hydrate this model instance with an associative array of data.
  622. * WARNING: The keys in the array MUST match with columns in the
  623. * corresponding database table. If any keys are supplied which
  624. * do not match up with columns, the database will throw an error.
  625. *
  626. * @param Array $data
  627. * @return void
  628. */
  629. public function hydrate($data) {
  630. $this->orm->hydrate($data)->force_all_dirty();
  631. }
  632. /**
  633. * Calls static methods directly on the ORMWrapper
  634. *
  635. * @param string $method
  636. * @param Array $parameters
  637. * @return Array
  638. */
  639. public static function __callStatic($method, $parameters) {
  640. if(function_exists('get_called_class')) {
  641. $model = self::factory(get_called_class());
  642. return call_user_func_array(array($model, $method), $parameters);
  643. }
  644. }
  645. /**
  646. * Magic method to capture calls to undefined class methods.
  647. * In this case we are attempting to convert camel case formatted
  648. * methods into underscore formatted methods.
  649. *
  650. * This allows us to call methods using camel case and remain
  651. * backwards compatible.
  652. *
  653. * @param string $name
  654. * @param array $arguments
  655. * @throws ParisMethodMissingException
  656. * @return bool|ORMWrapper
  657. */
  658. public function __call($name, $arguments) {
  659. $method = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
  660. if (method_exists($this, $method)) {
  661. return call_user_func_array(array($this, $method), $arguments);
  662. } else {
  663. throw new ParisMethodMissingException("Method $name() does not exist in class " . get_class($this));
  664. }
  665. }
  666. }
  667. class ParisMethodMissingException extends Exception {}