model.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. <?php namespace Laravel\Database\Eloquent;
  2. use Laravel\Str;
  3. use Laravel\Database;
  4. use Laravel\Database\Eloquent\Relationships\Has_Many_And_Belongs_To;
  5. abstract class Model {
  6. /**
  7. * All of the model's attributes.
  8. *
  9. * @var array
  10. */
  11. public $attributes = array();
  12. /**
  13. * The model's attributes in their original state.
  14. *
  15. * @var array
  16. */
  17. public $original = array();
  18. /**
  19. * The relationships that have been loaded for the query.
  20. *
  21. * @var array
  22. */
  23. public $relationships = array();
  24. /**
  25. * Indicates if the model exists in the database.
  26. *
  27. * @var bool
  28. */
  29. public $exists = false;
  30. /**
  31. * The relationships that should be eagerly loaded.
  32. *
  33. * @var array
  34. */
  35. public $includes = array();
  36. /**
  37. * The primary key for the model on the database table.
  38. *
  39. * @var string
  40. */
  41. public static $key = 'id';
  42. /**
  43. * The attributes that are accessible for mass assignment.
  44. *
  45. * @var array
  46. */
  47. public static $accessible;
  48. /**
  49. * Indicates if the model has update and creation timestamps.
  50. *
  51. * @var bool
  52. */
  53. public static $timestamps = true;
  54. /**
  55. * The name of the table associated with the model.
  56. *
  57. * @var string
  58. */
  59. public static $table;
  60. /**
  61. * The name of the database connection that should be used for the model.
  62. *
  63. * @var string
  64. */
  65. public static $connection;
  66. /**
  67. * The name of the sequence associated with the model.
  68. *
  69. * @var string
  70. */
  71. public static $sequence;
  72. /**
  73. * The default number of models to show per page when paginating.
  74. *
  75. * @var int
  76. */
  77. public static $per_page = 20;
  78. /**
  79. * Create a new Eloquent model instance.
  80. *
  81. * @param array $attributes
  82. * @param bool $exists
  83. * @return void
  84. */
  85. public function __construct($attributes = array(), $exists = false)
  86. {
  87. $this->exists = $exists;
  88. $this->fill($attributes);
  89. }
  90. /**
  91. * Hydrate the model with an array of attributes.
  92. *
  93. * @param array $attributes
  94. * @return Model
  95. */
  96. public function fill($attributes)
  97. {
  98. $attributes = (array) $attributes;
  99. foreach ($attributes as $key => $value)
  100. {
  101. // If the "accessible" property is an array, the developer is limiting the
  102. // attributes that may be mass assigned, and we need to verify that the
  103. // current attribute is included in that list of allowed attributes.
  104. if (is_array(static::$accessible))
  105. {
  106. if (in_array($key, static::$accessible))
  107. {
  108. $this->$key = $value;
  109. }
  110. }
  111. // If the "accessible" property is not an array, no attributes have been
  112. // white-listed and we are free to set the value of the attribute to
  113. // the value that has been passed into the method without a check.
  114. else
  115. {
  116. $this->$key = $value;
  117. }
  118. }
  119. // If the original attribute values have not been set, we will set
  120. // them to the values passed to this method allowing us to easily
  121. // check if the model has changed since hydration.
  122. if (count($this->original) === 0)
  123. {
  124. $this->original = $this->attributes;
  125. }
  126. return $this;
  127. }
  128. /**
  129. * Set the accessible attributes for the given model.
  130. *
  131. * @param array $attributes
  132. * @return void
  133. */
  134. public static function accessible($attributes)
  135. {
  136. static::$accessible = $attributes;
  137. }
  138. /**
  139. * Create a new model and store it in the database.
  140. *
  141. * If save is successful, the model will be returned, otherwise false.
  142. *
  143. * @param array $attributes
  144. * @return Model|false
  145. */
  146. public static function create($attributes)
  147. {
  148. $model = new static($attributes);
  149. $success = $model->save();
  150. return ($success) ? $model : false;
  151. }
  152. /**
  153. * Update a model instance in the database.
  154. *
  155. * @param mixed $id
  156. * @param array $attributes
  157. * @return int
  158. */
  159. public static function update($id, $attributes)
  160. {
  161. $model = new static(array(), true);
  162. if (static::$timestamps) $attributes['updated_at'] = $model->get_timestamp();
  163. return $model->query()->where($model->key(), '=', $id)->update($attributes);
  164. }
  165. /**
  166. * Find a model by its primary key.
  167. *
  168. * @param string $id
  169. * @param array $columns
  170. * @return Model
  171. */
  172. public static function find($id, $columns = array('*'))
  173. {
  174. $model = new static;
  175. return $model->query()->where(static::$key, '=', $id)->first($columns);
  176. }
  177. /**
  178. * Get all of the models in the database.
  179. *
  180. * @return array
  181. */
  182. public static function all()
  183. {
  184. return with(new static)->query()->get();
  185. }
  186. /**
  187. * The relationships that should be eagerly loaded by the query.
  188. *
  189. * @param array $includes
  190. * @return Model
  191. */
  192. public function _with($includes)
  193. {
  194. $this->includes = (array) $includes;
  195. return $this;
  196. }
  197. /**
  198. * Get the query for a one-to-one association.
  199. *
  200. * @param string $model
  201. * @param string $foreign
  202. * @return Relationship
  203. */
  204. public function has_one($model, $foreign = null)
  205. {
  206. return $this->has_one_or_many(__FUNCTION__, $model, $foreign);
  207. }
  208. /**
  209. * Get the query for a one-to-many association.
  210. *
  211. * @param string $model
  212. * @param string $foreign
  213. * @return Relationship
  214. */
  215. public function has_many($model, $foreign = null)
  216. {
  217. return $this->has_one_or_many(__FUNCTION__, $model, $foreign);
  218. }
  219. /**
  220. * Get the query for a one-to-one / many association.
  221. *
  222. * @param string $type
  223. * @param string $model
  224. * @param string $foreign
  225. * @return Relationship
  226. */
  227. protected function has_one_or_many($type, $model, $foreign)
  228. {
  229. if ($type == 'has_one')
  230. {
  231. return new Relationships\Has_One($this, $model, $foreign);
  232. }
  233. else
  234. {
  235. return new Relationships\Has_Many($this, $model, $foreign);
  236. }
  237. }
  238. /**
  239. * Get the query for a one-to-one (inverse) relationship.
  240. *
  241. * @param string $model
  242. * @param string $foreign
  243. * @return Relationship
  244. */
  245. public function belongs_to($model, $foreign = null)
  246. {
  247. // If no foreign key is specified for the relationship, we will assume that the
  248. // name of the calling function matches the foreign key. For example, if the
  249. // calling function is "manager", we'll assume the key is "manager_id".
  250. if (is_null($foreign))
  251. {
  252. list(, $caller) = debug_backtrace(false);
  253. $foreign = "{$caller['function']}_id";
  254. }
  255. return new Relationships\Belongs_To($this, $model, $foreign);
  256. }
  257. /**
  258. * Get the query for a many-to-many relationship.
  259. *
  260. * @param string $model
  261. * @param string $table
  262. * @param string $foreign
  263. * @param string $other
  264. * @return Relationship
  265. */
  266. public function has_many_and_belongs_to($model, $table = null, $foreign = null, $other = null)
  267. {
  268. return new Has_Many_And_Belongs_To($this, $model, $table, $foreign, $other);
  269. }
  270. /**
  271. * Save the model and all of its relations to the database.
  272. *
  273. * @return bool
  274. */
  275. public function push()
  276. {
  277. $this->save();
  278. // To sync all of the relationships to the database, we will simply spin through
  279. // the relationships, calling the "push" method on each of the models in that
  280. // given relationship, this should ensure that each model is saved.
  281. foreach ($this->relationships as $name => $models)
  282. {
  283. if ( ! is_array($models))
  284. {
  285. $models = array($models);
  286. }
  287. foreach ($models as $model)
  288. {
  289. $model->push();
  290. }
  291. }
  292. }
  293. /**
  294. * Save the model instance to the database.
  295. *
  296. * @return bool
  297. */
  298. public function save()
  299. {
  300. if ( ! $this->dirty()) return true;
  301. if (static::$timestamps)
  302. {
  303. $this->timestamp();
  304. }
  305. // If the model exists, we only need to update it in the database, and the update
  306. // will be considered successful if there is one affected row returned from the
  307. // fluent query instance. We'll set the where condition automatically.
  308. if ($this->exists)
  309. {
  310. $query = $this->query()->where(static::$key, '=', $this->get_key());
  311. $result = $query->update($this->get_dirty()) === 1;
  312. }
  313. // If the model does not exist, we will insert the record and retrieve the last
  314. // insert ID that is associated with the model. If the ID returned is numeric
  315. // then we can consider the insert successful.
  316. else
  317. {
  318. $id = $this->query()->insert_get_id($this->attributes, $this->sequence());
  319. $this->set_key($id);
  320. $this->exists = $result = is_numeric($this->get_key());
  321. }
  322. // After the model has been "saved", we will set the original attributes to
  323. // match the current attributes so the model will not be viewed as being
  324. // dirty and subsequent calls won't hit the database.
  325. $this->original = $this->attributes;
  326. return $result;
  327. }
  328. /**
  329. * Set the update and creation timestamps on the model.
  330. *
  331. * @return void
  332. */
  333. protected function timestamp()
  334. {
  335. $this->updated_at = $this->get_timestamp();
  336. if ( ! $this->exists) $this->created_at = $this->updated_at;
  337. }
  338. /**
  339. * Get the current timestamp in its storable form.
  340. *
  341. * @return mixed
  342. */
  343. public function get_timestamp()
  344. {
  345. return date('Y-m-d H:i:s');
  346. }
  347. /**
  348. * Get a new fluent query builder instance for the model.
  349. *
  350. * @return Query
  351. */
  352. protected function query()
  353. {
  354. return new Query($this);
  355. }
  356. /**
  357. * Sync the original attributes with the current attributes.
  358. *
  359. * @return bool
  360. */
  361. final public function sync()
  362. {
  363. $this->original = $this->attributes;
  364. return true;
  365. }
  366. /**
  367. * Determine if a given attribute has changed from its original state.
  368. *
  369. * @param string $attribute
  370. * @return bool
  371. */
  372. public function changed($attribute)
  373. {
  374. return array_get($this->attributes, $attribute) !== array_get($this->original, $attribute);
  375. }
  376. /**
  377. * Determine if the model has been changed from its original state.
  378. *
  379. * Models that haven't been persisted to storage are always considered dirty.
  380. *
  381. * @return bool
  382. */
  383. public function dirty()
  384. {
  385. return ! $this->exists or count($this->get_dirty()) > 0;
  386. }
  387. /**
  388. * Get the name of the table associated with the model.
  389. *
  390. * @return string
  391. */
  392. public function table()
  393. {
  394. return static::$table ?: strtolower(Str::plural(basename(get_class($this))));
  395. }
  396. /**
  397. * Get the dirty attributes for the model.
  398. *
  399. * @return array
  400. */
  401. public function get_dirty()
  402. {
  403. return array_diff_assoc($this->attributes, $this->original);
  404. }
  405. /**
  406. * Get the value of the primary key for the model.
  407. *
  408. * @return int
  409. */
  410. public function get_key()
  411. {
  412. return $this->get_attribute(static::$key);
  413. }
  414. /**
  415. * Set the value of the primary key for the model.
  416. *
  417. * @param int $value
  418. * @return void
  419. */
  420. public function set_key($value)
  421. {
  422. return $this->set_attribute(static::$key, $value);
  423. }
  424. /**
  425. * Get a given attribute from the model.
  426. *
  427. * @param string $key
  428. */
  429. public function get_attribute($key)
  430. {
  431. return array_get($this->attributes, $key);
  432. }
  433. /**
  434. * Set an attribute's value on the model.
  435. *
  436. * @param string $key
  437. * @param mixed $value
  438. * @return void
  439. */
  440. public function set_attribute($key, $value)
  441. {
  442. $this->attributes[$key] = $value;
  443. }
  444. /**
  445. * Remove an attribute from the model.
  446. *
  447. * @param string $key
  448. */
  449. final public function purge($key)
  450. {
  451. unset($this->original[$key]);
  452. unset($this->attributes[$key]);
  453. }
  454. /**
  455. * Handle the dynamic retrieval of attributes and associations.
  456. *
  457. * @param string $key
  458. * @return mixed
  459. */
  460. public function __get($key)
  461. {
  462. // First we will check to see if the requested key is an already loaded
  463. // relationship and return it if it is. All relationships are stored
  464. // in the special relationships array so they are not persisted.
  465. if (array_key_exists($key, $this->relationships))
  466. {
  467. return $this->relationships[$key];
  468. }
  469. // Next we'll check if the requested key is in the array of attributes
  470. // for the model. These are simply regular properties that typically
  471. // correspond to a single column on the database for the model.
  472. elseif (array_key_exists($key, $this->attributes))
  473. {
  474. return $this->{"get_{$key}"}();
  475. }
  476. // If the item is not a loaded relationship, it may be a relationship
  477. // that hasn't been loaded yet. If it is, we will lazy load it and
  478. // set the value of the relationship in the relationship array.
  479. elseif (method_exists($this, $key))
  480. {
  481. return $this->relationships[$key] = $this->$key()->results();
  482. }
  483. // Finally we will just assume the requested key is just a regular
  484. // attribute and attempt to call the getter method for it, which
  485. // will fall into the __call method if one doesn't exist.
  486. else
  487. {
  488. return $this->{"get_{$key}"}();
  489. }
  490. }
  491. /**
  492. * Handle the dynamic setting of attributes.
  493. *
  494. * @param string $key
  495. * @param mixed $value
  496. * @return void
  497. */
  498. public function __set($key, $value)
  499. {
  500. $this->{"set_{$key}"}($value);
  501. }
  502. /**
  503. * Determine if an attribute exists on the model.
  504. *
  505. * @param string $key
  506. * @return bool
  507. */
  508. public function __isset($key)
  509. {
  510. foreach (array('attributes', 'relationships') as $source)
  511. {
  512. if (array_key_exists($key, $this->$source)) return true;
  513. }
  514. }
  515. /**
  516. * Remove an attribute from the model.
  517. *
  518. * @param string $key
  519. * @return void
  520. */
  521. public function __unset($key)
  522. {
  523. foreach (array('attributes', 'relationships') as $source)
  524. {
  525. unset($this->$source[$key]);
  526. }
  527. }
  528. /**
  529. * Handle dynamic method calls on the model.
  530. *
  531. * @param string $method
  532. * @param array $parameters
  533. * @return mixed
  534. */
  535. public function __call($method, $parameters)
  536. {
  537. // If the method is actually the name of a static property on the model we'll
  538. // return the value of the static property. This makes it convenient for
  539. // relationships to access these values off of the instances.
  540. if (in_array($method, array('key', 'table', 'connection', 'sequence', 'per_page')))
  541. {
  542. return static::$$method;
  543. }
  544. // Some methods need to be accessed both staticly and non-staticly so we'll
  545. // keep underscored methods of those methods and intercept calls to them
  546. // here so they can be called either way on the model instance.
  547. if (in_array($method, array('with')))
  548. {
  549. return call_user_func_array(array($this, '_'.$method), $parameters);
  550. }
  551. // First we want to see if the method is a getter / setter for an attribute.
  552. // If it is, we'll call the basic getter and setter method for the model
  553. // to perform the appropriate action based on the method.
  554. if (starts_with($method, 'get_'))
  555. {
  556. return $this->attributes[substr($method, 4)];
  557. }
  558. elseif (starts_with($method, 'set_'))
  559. {
  560. $this->attributes[substr($method, 4)] = $parameters[0];
  561. }
  562. // Finally we will assume that the method is actually the beginning of a
  563. // query, such as "where", and will create a new query instance and
  564. // call the method on the query instance, returning it after.
  565. else
  566. {
  567. return call_user_func_array(array($this->query(), $method), $parameters);
  568. }
  569. }
  570. /**
  571. * Dynamically handle static method calls on the model.
  572. *
  573. * @param string $method
  574. * @param array $parameters
  575. * @return mixed
  576. */
  577. public static function __callStatic($method, $parameters)
  578. {
  579. $model = get_called_class();
  580. return call_user_func_array(array(new $model, $method), $parameters);
  581. }
  582. }