model.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. <?php namespace Laravel\Database\Eloquent;
  2. use Laravel\Str;
  3. use Laravel\Inflector;
  4. use Laravel\Paginator;
  5. use Laravel\Database\Manager as DB;
  6. abstract class Model {
  7. /**
  8. * The connection that should be used for the model.
  9. *
  10. * @var string
  11. */
  12. public static $connection;
  13. /**
  14. * Indicates if the model has creation and update timestamps.
  15. *
  16. * @var bool
  17. */
  18. public static $timestamps = false;
  19. /**
  20. * The name of the auto-incrementing sequence associated with the model.
  21. *
  22. * @var string
  23. */
  24. public static $sequence = null;
  25. /**
  26. * The model query instance.
  27. *
  28. * @var Query
  29. */
  30. public $query;
  31. /**
  32. * Indicates if the model exists in the database.
  33. *
  34. * @var bool
  35. */
  36. public $exists = false;
  37. /**
  38. * The model's attributes.
  39. *
  40. * Typically, a model has an attribute for each column on the table.
  41. *
  42. * @var array
  43. */
  44. public $attributes = array();
  45. /**
  46. * The model's dirty attributes.
  47. *
  48. * @var array
  49. */
  50. public $dirty = array();
  51. /**
  52. * The model's ignored attributes.
  53. *
  54. * Ignored attributes will not be saved to the database, and are
  55. * primarily used to hold relationships.
  56. *
  57. * @var array
  58. */
  59. public $ignore = array();
  60. /**
  61. * The relationships that should be eagerly loaded.
  62. *
  63. * @var array
  64. */
  65. public $includes = array();
  66. /**
  67. * The relationship type the model is currently resolving.
  68. *
  69. * @var string
  70. */
  71. public $relating;
  72. /**
  73. * The foreign key of the "relating" relationship.
  74. *
  75. * @var string
  76. */
  77. public $relating_key;
  78. /**
  79. * The table name of the model being resolved.
  80. *
  81. * This is used during many-to-many eager loading.
  82. *
  83. * @var string
  84. */
  85. public $relating_table;
  86. /**
  87. * Create a new Eloquent model instance.
  88. *
  89. * @param array $attributes
  90. * @return void
  91. */
  92. public function __construct($attributes = array())
  93. {
  94. $this->fill($attributes);
  95. }
  96. /**
  97. * Set the attributes of the model using an array.
  98. *
  99. * @param array $attributes
  100. * @return Model
  101. */
  102. public function fill($attributes)
  103. {
  104. foreach ($attributes as $key => $value)
  105. {
  106. $this->$key = $value;
  107. }
  108. return $this;
  109. }
  110. /**
  111. * Set the eagerly loaded models on the queryable model.
  112. *
  113. * @return Model
  114. */
  115. private function _with()
  116. {
  117. $this->includes = func_get_args();
  118. return $this;
  119. }
  120. /**
  121. * Factory for creating queryable Eloquent model instances.
  122. *
  123. * @param string $class
  124. * @return object
  125. */
  126. public static function query($class)
  127. {
  128. $model = new $class;
  129. // Since this method is only used for instantiating models for querying
  130. // purposes, we will go ahead and set the Query instance on the model.
  131. $model->query = DB::connection(static::$connection)->table(static::table($class));
  132. return $model;
  133. }
  134. /**
  135. * Get the table name for a model.
  136. *
  137. * @param string $class
  138. * @return string
  139. */
  140. public static function table($class)
  141. {
  142. if (property_exists($class, 'table')) return $class::$table;
  143. return strtolower(Inflector::plural(static::model_name($class)));
  144. }
  145. /**
  146. * Get an Eloquent model name without any namespaces.
  147. *
  148. * @param string|Model $model
  149. * @return string
  150. */
  151. public static function model_name($model)
  152. {
  153. $class = (is_object($model)) ? get_class($model) : $model;
  154. $segments = array_reverse(explode('\\', $class));
  155. return $segments[0];
  156. }
  157. /**
  158. * Get all of the models from the database.
  159. *
  160. * @return array
  161. */
  162. public static function all()
  163. {
  164. return Hydrator::hydrate(static::query(get_called_class()));
  165. }
  166. /**
  167. * Get a model by the primary key.
  168. *
  169. * @param int $id
  170. * @return mixed
  171. */
  172. public static function find($id)
  173. {
  174. return static::query(get_called_class())->where('id', '=', $id)->first();
  175. }
  176. /**
  177. * Get an array of models from the database.
  178. *
  179. * @return array
  180. */
  181. private function _get()
  182. {
  183. return Hydrator::hydrate($this);
  184. }
  185. /**
  186. * Get the first model result
  187. *
  188. * @return mixed
  189. */
  190. private function _first()
  191. {
  192. return (count($results = $this->take(1)->_get()) > 0) ? reset($results) : null;
  193. }
  194. /**
  195. * Get paginated model results as a Paginator instance.
  196. *
  197. * @param int $per_page
  198. * @return Paginator
  199. */
  200. private function _paginate($per_page = null)
  201. {
  202. $total = $this->query->count();
  203. // The number of models to show per page may be specified as a static property
  204. // on the model. The models shown per page may also be overriden for the model
  205. // by passing the number into this method. If the models to show per page is
  206. // not available via either of these avenues, a default number will be shown.
  207. if (is_null($per_page))
  208. {
  209. $per_page = (property_exists(get_class($this), 'per_page')) ? static::$per_page : 20;
  210. }
  211. return Paginator::make($this->for_page(Paginator::page($total, $per_page), $per_page)->get(), $total, $per_page);
  212. }
  213. /**
  214. * Retrieve the query for a 1:1 relationship.
  215. *
  216. * @param string $model
  217. * @param string $foreign_key
  218. * @return mixed
  219. */
  220. public function has_one($model, $foreign_key = null)
  221. {
  222. $this->relating = __FUNCTION__;
  223. return $this->has_one_or_many($model, $foreign_key);
  224. }
  225. /**
  226. * Retrieve the query for a 1:* relationship.
  227. *
  228. * @param string $model
  229. * @param string $foreign_key
  230. * @return mixed
  231. */
  232. public function has_many($model, $foreign_key = null)
  233. {
  234. $this->relating = __FUNCTION__;
  235. return $this->has_one_or_many($model, $foreign_key);
  236. }
  237. /**
  238. * Retrieve the query for a 1:1 or 1:* relationship.
  239. *
  240. * The default foreign key for has one and has many relationships is the name
  241. * of the model with an appended _id. For example, the foreign key for a
  242. * User model would be user_id. Photo would be photo_id, etc.
  243. *
  244. * @param string $model
  245. * @param string $foreign_key
  246. * @return mixed
  247. */
  248. private function has_one_or_many($model, $foreign_key)
  249. {
  250. $this->relating_key = (is_null($foreign_key)) ? strtolower(static::model_name($this)).'_id' : $foreign_key;
  251. return static::query($model)->where($this->relating_key, '=', $this->id);
  252. }
  253. /**
  254. * Retrieve the query for a 1:1 belonging relationship.
  255. *
  256. * The default foreign key for belonging relationships is the name of the
  257. * relationship method name with _id. So, if a model has a "manager" method
  258. * returning a belongs_to relationship, the key would be manager_id.
  259. *
  260. * @param string $model
  261. * @param string $foreign_key
  262. * @return mixed
  263. */
  264. public function belongs_to($model, $foreign_key = null)
  265. {
  266. $this->relating = __FUNCTION__;
  267. if ( ! is_null($foreign_key))
  268. {
  269. $this->relating_key = $foreign_key;
  270. }
  271. else
  272. {
  273. list(, $caller) = debug_backtrace(false);
  274. $this->relating_key = $caller['function'].'_id';
  275. }
  276. return static::query($model)->where('id', '=', $this->attributes[$this->relating_key]);
  277. }
  278. /**
  279. * Retrieve the query for a *:* relationship.
  280. *
  281. * The default foreign key for many-to-many relations is the name of the model
  282. * with an appended _id. This is the same convention as has_one and has_many.
  283. *
  284. * @param string $model
  285. * @param string $table
  286. * @param string $foreign_key
  287. * @param string $associated_key
  288. * @return mixed
  289. */
  290. public function has_and_belongs_to_many($model, $table = null, $foreign_key = null, $associated_key = null)
  291. {
  292. $this->relating = __FUNCTION__;
  293. $this->relating_table = (is_null($table)) ? $this->intermediate_table($model) : $table;
  294. // Allowing the overriding of the foreign and associated keys provides
  295. // the flexibility for self-referential many-to-many relationships.
  296. $this->relating_key = (is_null($foreign_key)) ? strtolower(static::model_name($this)).'_id' : $foreign_key;
  297. // The associated key is the foreign key name of the related model.
  298. // If the related model is "Role", the key would be "role_id".
  299. $associated_key = (is_null($associated_key)) ? strtolower(static::model_name($model)).'_id' : $associated_key;
  300. return static::query($model)
  301. ->select(array(static::table($model).'.*'))
  302. ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.$associated_key)
  303. ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id);
  304. }
  305. /**
  306. * Determine the intermediate table name for a given model.
  307. *
  308. * By default, the intermediate table name is the plural names of the models
  309. * arranged alphabetically and concatenated with an underscore.
  310. *
  311. * @param string $model
  312. * @return string
  313. */
  314. private function intermediate_table($model)
  315. {
  316. $models = array(Inflector::plural(static::model_name($model)), Inflector::plural(static::model_name($this)));
  317. sort($models);
  318. return strtolower($models[0].'_'.$models[1]);
  319. }
  320. /**
  321. * Save the model to the database.
  322. *
  323. * @return bool
  324. */
  325. public function save()
  326. {
  327. // If the model does not have any dirty attributes, there is no reason
  328. // to save it to the database.
  329. if ($this->exists and count($this->dirty) == 0) return true;
  330. $model = get_class($this);
  331. // Since the model was instantiated using "new", a query instance has not been set.
  332. // Only models being used for querying have their query instances set by default.
  333. $this->query = DB::connection(static::$connection)->table(static::table($model));
  334. if (property_exists($model, 'timestamps') and $model::$timestamps)
  335. {
  336. $this->timestamp();
  337. }
  338. // If the model already exists in the database, we will just update it.
  339. // Otherwise, we will insert the model and set the ID attribute.
  340. if ($this->exists)
  341. {
  342. $success = ($this->query->where_id($this->attributes['id'])->update($this->dirty) === 1);
  343. }
  344. else
  345. {
  346. $success = is_numeric($this->attributes['id'] = $this->query->insert_get_id($this->attributes, static::$sequence));
  347. }
  348. ($this->exists = true) and $this->dirty = array();
  349. return $success;
  350. }
  351. /**
  352. * Set the creation and update timestamps on the model.
  353. *
  354. * @return void
  355. */
  356. private function timestamp()
  357. {
  358. $this->updated_at = date('Y-m-d H:i:s');
  359. if ( ! $this->exists) $this->created_at = $this->updated_at;
  360. }
  361. /**
  362. * Delete a model from the database.
  363. *
  364. * @param int $id
  365. * @return int
  366. */
  367. public function delete($id = null)
  368. {
  369. // If the delete method is being called on an existing model, we only want to delete
  370. // that model. If it is being called from an Eloquent query model, it is probably
  371. // the developer's intention to delete more than one model, so we will pass the
  372. // delete statement to the query instance.
  373. if ( ! $this->exists) return $this->query->delete();
  374. $table = static::table(get_class($this));
  375. return DB::connection(static::$connection)->table($table)->delete($this->id);
  376. }
  377. /**
  378. * Magic method for retrieving model attributes.
  379. */
  380. public function __get($key)
  381. {
  382. if (array_key_exists($key, $this->attributes))
  383. {
  384. return $this->attributes[$key];
  385. }
  386. // Is the requested item a model relationship that has already been loaded?
  387. // All of the loaded relationships are stored in the "ignore" array.
  388. elseif (array_key_exists($key, $this->ignore))
  389. {
  390. return $this->ignore[$key];
  391. }
  392. // Is the requested item a model relationship? If it is, we will dynamically
  393. // load it and return the results of the relationship query.
  394. elseif (method_exists($this, $key))
  395. {
  396. $query = $this->$key();
  397. return $this->ignore[$key] = (in_array($this->relating, array('has_one', 'belongs_to'))) ? $query->first() : $query->get();
  398. }
  399. }
  400. /**
  401. * Magic Method for setting model attributes.
  402. */
  403. public function __set($key, $value)
  404. {
  405. // If the key is a relationship, add it to the ignored attributes.
  406. // Ignored attributes are not stored in the database.
  407. if (method_exists($this, $key))
  408. {
  409. $this->ignore[$key] = $value;
  410. }
  411. else
  412. {
  413. $this->attributes[$key] = $value;
  414. $this->dirty[$key] = $value;
  415. }
  416. }
  417. /**
  418. * Magic Method for determining if a model attribute is set.
  419. */
  420. public function __isset($key)
  421. {
  422. return (array_key_exists($key, $this->attributes) or array_key_exists($key, $this->ignore));
  423. }
  424. /**
  425. * Magic Method for unsetting model attributes.
  426. */
  427. public function __unset($key)
  428. {
  429. unset($this->attributes[$key], $this->ignore[$key], $this->dirty[$key]);
  430. }
  431. /**
  432. * Magic Method for handling dynamic method calls.
  433. */
  434. public function __call($method, $parameters)
  435. {
  436. // To allow the "with", "get", "first", and "paginate" methods to be called both
  437. // staticly and on an instance, we need to have private, underscored versions
  438. // of the methods and handle them dynamically.
  439. if (in_array($method, array('with', 'get', 'first', 'paginate')))
  440. {
  441. return call_user_func_array(array($this, '_'.$method), $parameters);
  442. }
  443. // All of the aggregate and persistance functions can be passed directly to the query
  444. // instance. For these functions, we can simply return the response of the query.
  445. if (in_array($method, array('insert', 'update', 'increment', 'decrement', 'abs', 'count', 'sum', 'min', 'max', 'avg')))
  446. {
  447. return call_user_func_array(array($this->query, $method), $parameters);
  448. }
  449. // Pass the method to the query instance. This allows the chaining of methods
  450. // from the query builder, providing the same convenient query API as the
  451. // query builder itself.
  452. call_user_func_array(array($this->query, $method), $parameters);
  453. return $this;
  454. }
  455. /**
  456. * Magic Method for handling dynamic static method calls.
  457. */
  458. public static function __callStatic($method, $parameters)
  459. {
  460. // Just pass the method to a model instance and let the __call method take care of it.
  461. return call_user_func_array(array(static::query(get_called_class()), $method), $parameters);
  462. }
  463. }