PageRenderTime 76ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/orm/classes/kohana/orm.php

https://bitbucket.org/thomasfortier/kohana_base
PHP | 2176 lines | 1088 code | 314 blank | 774 comment | 65 complexity | 6796190d744e89a9297f0928a0932714 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * [Object Relational Mapping][ref-orm] (ORM) is a method of abstracting database
  4. * access to standard PHP calls. All table rows are represented as model objects,
  5. * with object properties representing row data. ORM in Kohana generally follows
  6. * the [Active Record][ref-act] pattern.
  7. *
  8. * [ref-orm]: http://wikipedia.org/wiki/Object-relational_mapping
  9. * [ref-act]: http://wikipedia.org/wiki/Active_record
  10. *
  11. * @package Kohana/ORM
  12. * @author Kohana Team
  13. * @copyright (c) 2007-2010 Kohana Team
  14. * @license http://kohanaframework.org/license
  15. */
  16. class Kohana_ORM extends Model implements serializable {
  17. /**
  18. * Stores column information for ORM models
  19. * @var array
  20. */
  21. protected static $_column_cache = array();
  22. /**
  23. * Creates and returns a new model.
  24. *
  25. * @chainable
  26. * @param string $model Model name
  27. * @param mixed $id Parameter for find()
  28. * @return ORM
  29. */
  30. public static function factory($model, $id = NULL)
  31. {
  32. // Set class name
  33. $model = 'Model_'.ucfirst($model);
  34. return new $model($id);
  35. }
  36. /**
  37. * "Has one" relationships
  38. * @var array
  39. */
  40. protected $_has_one = array();
  41. /**
  42. * "Belongs to" relationships
  43. * @var array
  44. */
  45. protected $_belongs_to = array();
  46. /**
  47. * "Has many" relationships
  48. * @var array
  49. */
  50. protected $_has_many = array();
  51. /**
  52. * Relationships that should always be joined
  53. * @var array
  54. */
  55. protected $_load_with = array();
  56. /**
  57. * Validation object created before saving/updating
  58. * @var Validation
  59. */
  60. protected $_validation = NULL;
  61. /**
  62. * Current object
  63. * @var array
  64. */
  65. protected $_object = array();
  66. /**
  67. * @var array
  68. */
  69. protected $_changed = array();
  70. /**
  71. * @var array
  72. */
  73. protected $_original_values = array();
  74. /**
  75. * @var array
  76. */
  77. protected $_related = array();
  78. /**
  79. * @var bool
  80. */
  81. protected $_valid = FALSE;
  82. /**
  83. * @var bool
  84. */
  85. protected $_loaded = FALSE;
  86. /**
  87. * @var bool
  88. */
  89. protected $_saved = FALSE;
  90. /**
  91. * @var array
  92. */
  93. protected $_sorting;
  94. /**
  95. * Foreign key suffix
  96. * @var string
  97. */
  98. protected $_foreign_key_suffix = '_id';
  99. /**
  100. * Model name
  101. * @var string
  102. */
  103. protected $_object_name;
  104. /**
  105. * Plural model name
  106. * @var string
  107. */
  108. protected $_object_plural;
  109. /**
  110. * Table name
  111. * @var string
  112. */
  113. protected $_table_name;
  114. /**
  115. * Table columns
  116. * @var array
  117. */
  118. protected $_table_columns;
  119. /**
  120. * Auto-update columns for updates
  121. * @var string
  122. */
  123. protected $_updated_column = NULL;
  124. /**
  125. * Auto-update columns for creation
  126. * @var string
  127. */
  128. protected $_created_column = NULL;
  129. /**
  130. * Auto-serialize and unserialize columns on get/set
  131. * @var array
  132. */
  133. protected $_serialize_columns = array();
  134. /**
  135. * Table primary key
  136. * @var string
  137. */
  138. protected $_primary_key = 'id';
  139. /**
  140. * Primary key value
  141. * @var mixed
  142. */
  143. protected $_primary_key_value;
  144. /**
  145. * Model configuration, table names plural?
  146. * @var bool
  147. */
  148. protected $_table_names_plural = TRUE;
  149. /**
  150. * Model configuration, reload on wakeup?
  151. * @var bool
  152. */
  153. protected $_reload_on_wakeup = TRUE;
  154. /**
  155. * Database Object
  156. * @var Database
  157. */
  158. protected $_db = NULL;
  159. /**
  160. * Database config group
  161. * @var String
  162. */
  163. protected $_db_group = NULL;
  164. /**
  165. * Database methods applied
  166. * @var array
  167. */
  168. protected $_db_applied = array();
  169. /**
  170. * Database methods pending
  171. * @var array
  172. */
  173. protected $_db_pending = array();
  174. /**
  175. * Reset builder
  176. * @var bool
  177. */
  178. protected $_db_reset = TRUE;
  179. /**
  180. * Database query builder
  181. * @var Database_Query_Builder_Where
  182. */
  183. protected $_db_builder;
  184. /**
  185. * With calls already applied
  186. * @var array
  187. */
  188. protected $_with_applied = array();
  189. /**
  190. * Data to be loaded into the model from a database call cast
  191. * @var array
  192. */
  193. protected $_cast_data = array();
  194. /**
  195. * The message filename used for validation errors.
  196. * Defaults to ORM::$_object_name
  197. * @var string
  198. */
  199. protected $_errors_filename = NULL;
  200. /**
  201. * Constructs a new model and loads a record if given
  202. *
  203. * @param mixed $id Parameter for find or object to load
  204. * @return void
  205. */
  206. public function __construct($id = NULL)
  207. {
  208. $this->_initialize();
  209. if ($id !== NULL)
  210. {
  211. if (is_array($id))
  212. {
  213. foreach ($id as $column => $value)
  214. {
  215. // Passing an array of column => values
  216. $this->where($column, '=', $value);
  217. }
  218. $this->find();
  219. }
  220. else
  221. {
  222. // Passing the primary key
  223. $this->where($this->_object_name.'.'.$this->_primary_key, '=', $id)->find();
  224. }
  225. }
  226. elseif ( ! empty($this->_cast_data))
  227. {
  228. // Load preloaded data from a database call cast
  229. $this->_load_values($this->_cast_data);
  230. $this->_cast_data = array();
  231. }
  232. }
  233. /**
  234. * Prepares the model database connection, determines the table name,
  235. * and loads column information.
  236. *
  237. * @return void
  238. */
  239. protected function _initialize()
  240. {
  241. // Set the object name and plural name
  242. $this->_object_name = strtolower(substr(get_class($this), 6));
  243. $this->_object_plural = Inflector::plural($this->_object_name);
  244. if ( ! $this->_errors_filename)
  245. {
  246. $this->_errors_filename = $this->_object_name;
  247. }
  248. if ( ! is_object($this->_db))
  249. {
  250. // Get database instance
  251. $this->_db = Database::instance($this->_db_group);
  252. }
  253. if (empty($this->_table_name))
  254. {
  255. // Table name is the same as the object name
  256. $this->_table_name = $this->_object_name;
  257. if ($this->_table_names_plural === TRUE)
  258. {
  259. // Make the table name plural
  260. $this->_table_name = Inflector::plural($this->_table_name);
  261. }
  262. }
  263. foreach ($this->_belongs_to as $alias => $details)
  264. {
  265. $defaults['model'] = $alias;
  266. $defaults['foreign_key'] = $alias.$this->_foreign_key_suffix;
  267. $this->_belongs_to[$alias] = array_merge($defaults, $details);
  268. }
  269. foreach ($this->_has_one as $alias => $details)
  270. {
  271. $defaults['model'] = $alias;
  272. $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix;
  273. $this->_has_one[$alias] = array_merge($defaults, $details);
  274. }
  275. foreach ($this->_has_many as $alias => $details)
  276. {
  277. $defaults['model'] = Inflector::singular($alias);
  278. $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix;
  279. $defaults['through'] = NULL;
  280. $defaults['far_key'] = Inflector::singular($alias).$this->_foreign_key_suffix;
  281. $this->_has_many[$alias] = array_merge($defaults, $details);
  282. }
  283. // Load column information
  284. $this->reload_columns();
  285. // Clear initial model state
  286. $this->clear();
  287. }
  288. /**
  289. * Initializes validation rules, and labels
  290. *
  291. * @return void
  292. */
  293. protected function _validation()
  294. {
  295. // Build the validation object with its rules
  296. $this->_validation = Validation::factory($this->_object)
  297. ->bind(':model', $this)
  298. ->bind(':original_values', $this->_original_values)
  299. ->bind(':changed', $this->_changed);
  300. foreach ($this->rules() as $field => $rules)
  301. {
  302. $this->_validation->rules($field, $rules);
  303. }
  304. // Use column names by default for labels
  305. $columns = array_keys($this->_table_columns);
  306. // Merge user-defined labels
  307. $labels = array_merge(array_combine($columns, $columns), $this->labels());
  308. foreach ($labels as $field => $label)
  309. {
  310. $this->_validation->label($field, $label);
  311. }
  312. }
  313. /**
  314. * Reload column definitions.
  315. *
  316. * @chainable
  317. * @param boolean $force Force reloading
  318. * @return ORM
  319. */
  320. public function reload_columns($force = FALSE)
  321. {
  322. if ($force === TRUE OR empty($this->_table_columns))
  323. {
  324. if (isset(ORM::$_column_cache[$this->_object_name]))
  325. {
  326. // Use cached column information
  327. $this->_table_columns = ORM::$_column_cache[$this->_object_name];
  328. }
  329. else
  330. {
  331. // Grab column information from database
  332. $this->_table_columns = $this->list_columns(TRUE);
  333. // Load column cache
  334. ORM::$_column_cache[$this->_object_name] = $this->_table_columns;
  335. }
  336. }
  337. return $this;
  338. }
  339. /**
  340. * Unloads the current object and clears the status.
  341. *
  342. * @chainable
  343. * @return ORM
  344. */
  345. public function clear()
  346. {
  347. // Create an array with all the columns set to NULL
  348. $values = array_combine(array_keys($this->_table_columns), array_fill(0, count($this->_table_columns), NULL));
  349. // Replace the object and reset the object status
  350. $this->_object = $this->_changed = $this->_related = $this->_original_values = array();
  351. // Replace the current object with an empty one
  352. $this->_load_values($values);
  353. // Reset primary key
  354. $this->_primary_key_value = NULL;
  355. $this->reset();
  356. return $this;
  357. }
  358. /**
  359. * Reloads the current object from the database.
  360. *
  361. * @chainable
  362. * @return ORM
  363. */
  364. public function reload()
  365. {
  366. $primary_key = $this->pk();
  367. // Replace the object and reset the object status
  368. $this->_object = $this->_changed = $this->_related = $this->_original_values = array();
  369. // Only reload the object if we have one to reload
  370. if ($this->_loaded)
  371. return $this->clear()
  372. ->where($this->_object_name.'.'.$this->_primary_key, '=', $primary_key)
  373. ->find();
  374. else
  375. return $this->clear();
  376. }
  377. /**
  378. * Checks if object data is set.
  379. *
  380. * @param string $column Column name
  381. * @return boolean
  382. */
  383. public function __isset($column)
  384. {
  385. return (isset($this->_object[$column]) OR
  386. isset($this->_related[$column]) OR
  387. isset($this->_has_one[$column]) OR
  388. isset($this->_belongs_to[$column]) OR
  389. isset($this->_has_many[$column]));
  390. }
  391. /**
  392. * Unsets object data.
  393. *
  394. * @param string $column Column name
  395. * @return void
  396. */
  397. public function __unset($column)
  398. {
  399. unset($this->_object[$column], $this->_changed[$column], $this->_related[$column]);
  400. }
  401. /**
  402. * Displays the primary key of a model when it is converted to a string.
  403. *
  404. * @return string
  405. */
  406. public function __toString()
  407. {
  408. return (string) $this->pk();
  409. }
  410. /**
  411. * Allows serialization of only the object data and state, to prevent
  412. * "stale" objects being unserialized, which also requires less memory.
  413. *
  414. * @return array
  415. */
  416. public function serialize()
  417. {
  418. // Store only information about the object
  419. foreach (array('_primary_key_value', '_object', '_changed', '_loaded', '_saved', '_sorting', '_original_values') as $var)
  420. {
  421. $data[$var] = $this->{$var};
  422. }
  423. return serialize($data);
  424. }
  425. /**
  426. * Check whether the model data has been modified.
  427. * If $field is specified, checks whether that field was modified.
  428. *
  429. * @param string field to check for changes
  430. * @return bool Whether or not the field has changed
  431. */
  432. public function changed($field = NULL)
  433. {
  434. return ($field === NULL)
  435. ? $this->_changed
  436. : Arr::get($this->_changed, $field);
  437. }
  438. /**
  439. * Prepares the database connection and reloads the object.
  440. *
  441. * @param string $data String for unserialization
  442. * @return void
  443. */
  444. public function unserialize($data)
  445. {
  446. // Initialize model
  447. $this->_initialize();
  448. foreach (unserialize($data) as $name => $var)
  449. {
  450. $this->{$name} = $var;
  451. }
  452. if ($this->_reload_on_wakeup === TRUE)
  453. {
  454. // Reload the object
  455. $this->reload();
  456. }
  457. }
  458. /**
  459. * Handles retrieval of all model values, relationships, and metadata.
  460. *
  461. * @param string $column Column name
  462. * @return mixed
  463. */
  464. public function __get($column)
  465. {
  466. if (array_key_exists($column, $this->_object))
  467. {
  468. return (in_array($column, $this->_serialize_columns))
  469. ? $this->_unserialize_value($this->_object[$column])
  470. : $this->_object[$column];
  471. }
  472. elseif (isset($this->_related[$column]))
  473. {
  474. // Return related model that has already been fetched
  475. return $this->_related[$column];
  476. }
  477. elseif (isset($this->_belongs_to[$column]))
  478. {
  479. $model = $this->_related($column);
  480. // Use this model's column and foreign model's primary key
  481. $col = $model->_object_name.'.'.$model->_primary_key;
  482. $val = $this->_object[$this->_belongs_to[$column]['foreign_key']];
  483. $model->where($col, '=', $val)->find();
  484. return $this->_related[$column] = $model;
  485. }
  486. elseif (isset($this->_has_one[$column]))
  487. {
  488. $model = $this->_related($column);
  489. // Use this model's primary key value and foreign model's column
  490. $col = $model->_object_name.'.'.$this->_has_one[$column]['foreign_key'];
  491. $val = $this->pk();
  492. $model->where($col, '=', $val)->find();
  493. return $this->_related[$column] = $model;
  494. }
  495. elseif (isset($this->_has_many[$column]))
  496. {
  497. $model = ORM::factory($this->_has_many[$column]['model']);
  498. if (isset($this->_has_many[$column]['through']))
  499. {
  500. // Grab has_many "through" relationship table
  501. $through = $this->_has_many[$column]['through'];
  502. // Join on through model's target foreign key (far_key) and target model's primary key
  503. $join_col1 = $through.'.'.$this->_has_many[$column]['far_key'];
  504. $join_col2 = $model->_object_name.'.'.$model->_primary_key;
  505. $model->join($through)->on($join_col1, '=', $join_col2);
  506. // Through table's source foreign key (foreign_key) should be this model's primary key
  507. $col = $through.'.'.$this->_has_many[$column]['foreign_key'];
  508. $val = $this->pk();
  509. }
  510. else
  511. {
  512. // Simple has_many relationship, search where target model's foreign key is this model's primary key
  513. $col = $model->_object_name.'.'.$this->_has_many[$column]['foreign_key'];
  514. $val = $this->pk();
  515. }
  516. return $model->where($col, '=', $val);
  517. }
  518. else
  519. {
  520. throw new Kohana_Exception('The :property property does not exist in the :class class',
  521. array(':property' => $column, ':class' => get_class($this)));
  522. }
  523. }
  524. /**
  525. * Base set method - this should not be overridden.
  526. *
  527. * @param string $column Column name
  528. * @param mixed $value Column value
  529. * @return void
  530. */
  531. public function __set($column, $value)
  532. {
  533. if ( ! isset($this->_object_name))
  534. {
  535. // Object not yet constructed, so we're loading data from a database call cast
  536. $this->_cast_data[$column] = $value;
  537. }
  538. else
  539. {
  540. // Set the model's column to given value
  541. $this->set($column, $value);
  542. }
  543. }
  544. /**
  545. * Handles setting of column
  546. *
  547. * @param string $column Column name
  548. * @param mixed $value Column value
  549. * @return void
  550. */
  551. public function set($column, $value)
  552. {
  553. if (in_array($column, $this->_serialize_columns))
  554. {
  555. $value = $this->_serialize_value($value);
  556. }
  557. if (array_key_exists($column, $this->_object))
  558. {
  559. // Filter the data
  560. $value = $this->run_filter($column, $value);
  561. // See if the data really changed
  562. if ($value !== $this->_object[$column])
  563. {
  564. $this->_object[$column] = $value;
  565. // Data has changed
  566. $this->_changed[$column] = $column;
  567. // Object is no longer saved or valid
  568. $this->_saved = $this->_valid = FALSE;
  569. }
  570. }
  571. elseif (isset($this->_belongs_to[$column]))
  572. {
  573. // Update related object itself
  574. $this->_related[$column] = $value;
  575. // Update the foreign key of this model
  576. $this->_object[$this->_belongs_to[$column]['foreign_key']] = $value->pk();
  577. $this->_changed[$column] = $this->_belongs_to[$column]['foreign_key'];
  578. }
  579. else
  580. {
  581. throw new Kohana_Exception('The :property: property does not exist in the :class: class',
  582. array(':property:' => $column, ':class:' => get_class($this)));
  583. }
  584. return $this;
  585. }
  586. /**
  587. * Set values from an array with support for one-one relationships. This method should be used
  588. * for loading in post data, etc.
  589. *
  590. * @param array $values Array of column => val
  591. * @param array $expected Array of keys to take from $values
  592. * @return ORM
  593. */
  594. public function values(array $values, array $expected = NULL)
  595. {
  596. // Default to expecting everything except the primary key
  597. if ($expected === NULL)
  598. {
  599. $expected = array_keys($this->_table_columns);
  600. // Don't set the primary key by default
  601. unset($values[$this->_primary_key]);
  602. }
  603. foreach ($expected as $key => $column)
  604. {
  605. if (is_string($key))
  606. {
  607. // isset() fails when the value is NULL (we want it to pass)
  608. if ( ! array_key_exists($key, $values))
  609. continue;
  610. // Try to set values to a related model
  611. $this->{$key}->values($values[$key], $column);
  612. }
  613. else
  614. {
  615. // isset() fails when the value is NULL (we want it to pass)
  616. if ( ! array_key_exists($column, $values))
  617. continue;
  618. // Update the column, respects __set()
  619. $this->$column = $values[$column];
  620. }
  621. }
  622. return $this;
  623. }
  624. /**
  625. * Returns the values of this object as an array, including any related one-one
  626. * models that have already been loaded using with()
  627. *
  628. * @return array
  629. */
  630. public function as_array()
  631. {
  632. $object = array();
  633. foreach ($this->_object as $column => $value)
  634. {
  635. // Call __get for any user processing
  636. $object[$column] = $this->__get($column);
  637. }
  638. foreach ($this->_related as $column => $model)
  639. {
  640. // Include any related objects that are already loaded
  641. $object[$column] = $model->as_array();
  642. }
  643. return $object;
  644. }
  645. /**
  646. * Binds another one-to-one object to this model. One-to-one objects
  647. * can be nested using 'object1:object2' syntax
  648. *
  649. * @param string $target_path Target model to bind to
  650. * @return void
  651. */
  652. public function with($target_path)
  653. {
  654. if (isset($this->_with_applied[$target_path]))
  655. {
  656. // Don't join anything already joined
  657. return $this;
  658. }
  659. // Split object parts
  660. $aliases = explode(':', $target_path);
  661. $target = $this;
  662. foreach ($aliases as $alias)
  663. {
  664. // Go down the line of objects to find the given target
  665. $parent = $target;
  666. $target = $parent->_related($alias);
  667. if ( ! $target)
  668. {
  669. // Can't find related object
  670. return $this;
  671. }
  672. }
  673. // Target alias is at the end
  674. $target_alias = $alias;
  675. // Pop-off top alias to get the parent path (user:photo:tag becomes user:photo - the parent table prefix)
  676. array_pop($aliases);
  677. $parent_path = implode(':', $aliases);
  678. if (empty($parent_path))
  679. {
  680. // Use this table name itself for the parent path
  681. $parent_path = $this->_object_name;
  682. }
  683. else
  684. {
  685. if ( ! isset($this->_with_applied[$parent_path]))
  686. {
  687. // If the parent path hasn't been joined yet, do it first (otherwise LEFT JOINs fail)
  688. $this->with($parent_path);
  689. }
  690. }
  691. // Add to with_applied to prevent duplicate joins
  692. $this->_with_applied[$target_path] = TRUE;
  693. // Use the keys of the empty object to determine the columns
  694. foreach (array_keys($target->_object) as $column)
  695. {
  696. $name = $target_path.'.'.$column;
  697. $alias = $target_path.':'.$column;
  698. // Add the prefix so that load_result can determine the relationship
  699. $this->select(array($name, $alias));
  700. }
  701. if (isset($parent->_belongs_to[$target_alias]))
  702. {
  703. // Parent belongs_to target, use target's primary key and parent's foreign key
  704. $join_col1 = $target_path.'.'.$target->_primary_key;
  705. $join_col2 = $parent_path.'.'.$parent->_belongs_to[$target_alias]['foreign_key'];
  706. }
  707. else
  708. {
  709. // Parent has_one target, use parent's primary key as target's foreign key
  710. $join_col1 = $parent_path.'.'.$parent->_primary_key;
  711. $join_col2 = $target_path.'.'.$parent->_has_one[$target_alias]['foreign_key'];
  712. }
  713. // Join the related object into the result
  714. $this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2);
  715. return $this;
  716. }
  717. /**
  718. * Initializes the Database Builder to given query type
  719. *
  720. * @param integer $type Type of Database query
  721. * @return ORM
  722. */
  723. protected function _build($type)
  724. {
  725. // Construct new builder object based on query type
  726. switch ($type)
  727. {
  728. case Database::SELECT:
  729. $this->_db_builder = DB::select();
  730. break;
  731. case Database::UPDATE:
  732. $this->_db_builder = DB::update(array($this->_table_name, $this->_object_name));
  733. break;
  734. case Database::DELETE:
  735. $this->_db_builder = DB::delete(array($this->_table_name, $this->_object_name));
  736. }
  737. // Process pending database method calls
  738. foreach ($this->_db_pending as $method)
  739. {
  740. $name = $method['name'];
  741. $args = $method['args'];
  742. $this->_db_applied[$name] = $name;
  743. call_user_func_array(array($this->_db_builder, $name), $args);
  744. }
  745. return $this;
  746. }
  747. /**
  748. * Finds and loads a single database row into the object.
  749. *
  750. * @chainable
  751. * @return ORM
  752. */
  753. public function find()
  754. {
  755. if ($this->_loaded)
  756. throw new Kohana_Exception('Method find() cannot be called on loaded objects');
  757. if ( ! empty($this->_load_with))
  758. {
  759. foreach ($this->_load_with as $alias)
  760. {
  761. // Bind auto relationships
  762. $this->with($alias);
  763. }
  764. }
  765. $this->_build(Database::SELECT);
  766. return $this->_load_result(FALSE);
  767. }
  768. /**
  769. * Finds multiple database rows and returns an iterator of the rows found.
  770. *
  771. * @return Database_Result
  772. */
  773. public function find_all()
  774. {
  775. if ($this->_loaded)
  776. throw new Kohana_Exception('Method find_all() cannot be called on loaded objects');
  777. if ( ! empty($this->_load_with))
  778. {
  779. foreach ($this->_load_with as $alias)
  780. {
  781. // Bind auto relationships
  782. $this->with($alias);
  783. }
  784. }
  785. $this->_build(Database::SELECT);
  786. return $this->_load_result(TRUE);
  787. }
  788. /**
  789. * Loads a database result, either as a new record for this model, or as
  790. * an iterator for multiple rows.
  791. *
  792. * @chainable
  793. * @param bool $multiple Return an iterator or load a single row
  794. * @return ORM|Database_Result
  795. */
  796. protected function _load_result($multiple = FALSE)
  797. {
  798. $this->_db_builder->from(array($this->_table_name, $this->_object_name));
  799. if ($multiple === FALSE)
  800. {
  801. // Only fetch 1 record
  802. $this->_db_builder->limit(1);
  803. }
  804. // Select all columns by default
  805. $this->_db_builder->select($this->_object_name.'.*');
  806. if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting))
  807. {
  808. foreach ($this->_sorting as $column => $direction)
  809. {
  810. if (strpos($column, '.') === FALSE)
  811. {
  812. // Sorting column for use in JOINs
  813. $column = $this->_object_name.'.'.$column;
  814. }
  815. $this->_db_builder->order_by($column, $direction);
  816. }
  817. }
  818. if ($multiple === TRUE)
  819. {
  820. // Return database iterator casting to this object type
  821. $result = $this->_db_builder->as_object(get_class($this))->execute($this->_db);
  822. $this->reset();
  823. return $result;
  824. }
  825. else
  826. {
  827. // Load the result as an associative array
  828. $result = $this->_db_builder->as_assoc()->execute($this->_db);
  829. $this->reset();
  830. if ($result->count() === 1)
  831. {
  832. // Load object values
  833. $this->_load_values($result->current());
  834. }
  835. else
  836. {
  837. // Clear the object, nothing was found
  838. $this->clear();
  839. }
  840. return $this;
  841. }
  842. }
  843. /**
  844. * Loads an array of values into into the current object.
  845. *
  846. * @chainable
  847. * @param array $values Values to load
  848. * @return ORM
  849. */
  850. protected function _load_values(array $values)
  851. {
  852. if (array_key_exists($this->_primary_key, $values))
  853. {
  854. if ($values[$this->_primary_key] !== NULL)
  855. {
  856. // Flag as loaded and valid
  857. $this->_loaded = $this->_valid = TRUE;
  858. // Store primary key
  859. $this->_primary_key_value = $values[$this->_primary_key];
  860. }
  861. else
  862. {
  863. // Not loaded or valid
  864. $this->_loaded = $this->_valid = FALSE;
  865. }
  866. }
  867. // Related objects
  868. $related = array();
  869. foreach ($values as $column => $value)
  870. {
  871. if (strpos($column, ':') === FALSE)
  872. {
  873. // Load the value to this model
  874. $this->_object[$column] = $value;
  875. }
  876. else
  877. {
  878. // Column belongs to a related model
  879. list ($prefix, $column) = explode(':', $column, 2);
  880. $related[$prefix][$column] = $value;
  881. }
  882. }
  883. if ( ! empty($related))
  884. {
  885. foreach ($related as $object => $values)
  886. {
  887. // Load the related objects with the values in the result
  888. $this->_related($object)->_load_values($values);
  889. }
  890. }
  891. if ($this->_loaded)
  892. {
  893. // Store the object in its original state
  894. $this->_original_values = $this->_object;
  895. }
  896. return $this;
  897. }
  898. /**
  899. * Rule definitions for validation
  900. *
  901. * @return array
  902. */
  903. public function rules()
  904. {
  905. return array();
  906. }
  907. /**
  908. * Filters a value for a specific column
  909. *
  910. * @param string $field The column name
  911. * @param string $value The value to filter
  912. * @return string
  913. */
  914. protected function run_filter($field, $value)
  915. {
  916. $filters = $this->filters();
  917. // Get the filters for this column
  918. $wildcards = empty($filters[TRUE]) ? array() : $filters[TRUE];
  919. // Merge in the wildcards
  920. $filters = empty($filters[$field]) ? $wildcards : array_merge($wildcards, $filters[$field]);
  921. // Bind the field name and model so they can be used in the filter method
  922. $_bound = array
  923. (
  924. ':field' => $field,
  925. ':model' => $this,
  926. );
  927. foreach ($filters as $array)
  928. {
  929. // Value needs to be bound inside the loop so we are always using the
  930. // version that was modified by the filters that already ran
  931. $_bound[':value'] = $value;
  932. // Filters are defined as array($filter, $params)
  933. $filter = $array[0];
  934. $params = Arr::get($array, 1, array(':value'));
  935. foreach ($params as $key => $param)
  936. {
  937. if (is_string($param) AND array_key_exists($param, $_bound))
  938. {
  939. // Replace with bound value
  940. $params[$key] = $_bound[$param];
  941. }
  942. }
  943. if (is_array($filter) OR ! is_string($filter))
  944. {
  945. // This is either a callback as an array or a lambda
  946. $value = call_user_func_array($filter, $params);
  947. }
  948. elseif (strpos($filter, '::') === FALSE)
  949. {
  950. // Use a function call
  951. $function = new ReflectionFunction($filter);
  952. // Call $function($this[$field], $param, ...) with Reflection
  953. $value = $function->invokeArgs($params);
  954. }
  955. else
  956. {
  957. // Split the class and method of the rule
  958. list($class, $method) = explode('::', $filter, 2);
  959. // Use a static method call
  960. $method = new ReflectionMethod($class, $method);
  961. // Call $Class::$method($this[$field], $param, ...) with Reflection
  962. $value = $method->invokeArgs(NULL, $params);
  963. }
  964. }
  965. return $value;
  966. }
  967. /**
  968. * Filter definitions for validation
  969. *
  970. * @return array
  971. */
  972. public function filters()
  973. {
  974. return array();
  975. }
  976. /**
  977. * Label definitions for validation
  978. *
  979. * @return array
  980. */
  981. public function labels()
  982. {
  983. return array();
  984. }
  985. /**
  986. * Validates the current model's data
  987. *
  988. * @param Validation $extra_validation Validation object
  989. * @return ORM
  990. */
  991. public function check(Validation $extra_validation = NULL)
  992. {
  993. // Determine if any external validation failed
  994. $extra_errors = ($extra_validation AND ! $extra_validation->check());
  995. // Always build a new validation object
  996. $this->_validation();
  997. $array = $this->_validation;
  998. if (($this->_valid = $array->check()) === FALSE OR $extra_errors)
  999. {
  1000. $exception = new ORM_Validation_Exception($this->errors_filename(), $array);
  1001. if ($extra_errors)
  1002. {
  1003. // Merge any possible errors from the external object
  1004. $exception->add_object('_external', $extra_validation);
  1005. }
  1006. throw $exception;
  1007. }
  1008. return $this;
  1009. }
  1010. /**
  1011. * Insert a new object to the database
  1012. * @param Validation $validation Validation object
  1013. * @return ORM
  1014. */
  1015. public function create(Validation $validation = NULL)
  1016. {
  1017. if ($this->_loaded)
  1018. throw new Kohana_Exception('Cannot create :model model because it is already loaded.', array(':model' => $this->_object_name));
  1019. // Require model validation before saving
  1020. if ( ! $this->_valid)
  1021. {
  1022. $this->check($validation);
  1023. }
  1024. $data = array();
  1025. foreach ($this->_changed as $column)
  1026. {
  1027. // Generate list of column => values
  1028. $data[$column] = $this->_object[$column];
  1029. }
  1030. if (is_array($this->_created_column))
  1031. {
  1032. // Fill the created column
  1033. $column = $this->_created_column['column'];
  1034. $format = $this->_created_column['format'];
  1035. $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format);
  1036. }
  1037. $result = DB::insert($this->_table_name)
  1038. ->columns(array_keys($data))
  1039. ->values(array_values($data))
  1040. ->execute($this->_db);
  1041. if ( ! array_key_exists($this->_primary_key, $data))
  1042. {
  1043. // Load the insert id as the primary key if it was left out
  1044. $this->_object[$this->_primary_key] = $this->_primary_key_value = $result[0];
  1045. }
  1046. // Object is now loaded and saved
  1047. $this->_loaded = $this->_saved = TRUE;
  1048. // All changes have been saved
  1049. $this->_changed = array();
  1050. $this->_original_values = $this->_object;
  1051. return $this;
  1052. }
  1053. /**
  1054. * Updates a single record or multiple records
  1055. *
  1056. * @chainable
  1057. * @param Validation $validation Validation object
  1058. * @return ORM
  1059. */
  1060. public function update(Validation $validation = NULL)
  1061. {
  1062. if ( ! $this->_loaded)
  1063. throw new Kohana_Exception('Cannot update :model model because it is not loaded.', array(':model' => $this->_object_name));
  1064. if (empty($this->_changed))
  1065. {
  1066. // Nothing to update
  1067. return $this;
  1068. }
  1069. // Require model validation before saving
  1070. if ( ! $this->_valid)
  1071. {
  1072. $this->check($validation);
  1073. }
  1074. $data = array();
  1075. foreach ($this->_changed as $column)
  1076. {
  1077. // Compile changed data
  1078. $data[$column] = $this->_object[$column];
  1079. }
  1080. if (is_array($this->_updated_column))
  1081. {
  1082. // Fill the updated column
  1083. $column = $this->_updated_column['column'];
  1084. $format = $this->_updated_column['format'];
  1085. $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format);
  1086. }
  1087. // Use primary key value
  1088. $id = $this->pk();
  1089. // Update a single record
  1090. DB::update($this->_table_name)
  1091. ->set($data)
  1092. ->where($this->_primary_key, '=', $id)
  1093. ->execute($this->_db);
  1094. if (isset($data[$this->_primary_key]))
  1095. {
  1096. // Primary key was changed, reflect it
  1097. $this->_primary_key_value = $data[$this->_primary_key];
  1098. }
  1099. // Object has been saved
  1100. $this->_saved = TRUE;
  1101. // All changes have been saved
  1102. $this->_changed = array();
  1103. $this->_original_values = $this->_object;
  1104. return $this;
  1105. }
  1106. /**
  1107. * Updates or Creates the record depending on loaded()
  1108. *
  1109. * @chainable
  1110. * @param Validation $validation Validation object
  1111. * @return ORM
  1112. */
  1113. public function save(Validation $validation = NULL)
  1114. {
  1115. return $this->loaded() ? $this->update($validation) : $this->create($validation);
  1116. }
  1117. /**
  1118. * Deletes a single record or multiple records, ignoring relationships.
  1119. *
  1120. * @chainable
  1121. * @return ORM
  1122. */
  1123. public function delete()
  1124. {
  1125. if ( ! $this->_loaded)
  1126. throw new Kohana_Exception('Cannot delete :model model because it is not loaded.', array(':model' => $this->_object_name));
  1127. // Use primary key value
  1128. $id = $this->pk();
  1129. // Delete the object
  1130. DB::delete($this->_table_name)
  1131. ->where($this->_primary_key, '=', $id)
  1132. ->execute($this->_db);
  1133. return $this->clear();
  1134. }
  1135. /**
  1136. * Tests if this object has a relationship to a different model,
  1137. * or an array of different models.
  1138. *
  1139. * // Check if $model has the login role
  1140. * $model->has('roles', ORM::factory('role', array('name' => 'login')));
  1141. * // Check for the login role if you know the roles.id is 5
  1142. * $model->has('roles', 5);
  1143. * // Check for all of the following roles
  1144. * $model->has('roles', array(1, 2, 3, 4));
  1145. * // Check if $model has any roles
  1146. * $model->has('roles')
  1147. *
  1148. * @param string $alias Alias of the has_many "through" relationship
  1149. * @param mixed $far_keys Related model, primary key, or an array of primary keys
  1150. * @return Database_Result
  1151. */
  1152. public function has($alias, $far_keys = NULL)
  1153. {
  1154. if ($far_keys === NULL)
  1155. {
  1156. return (bool) DB::select(array('COUNT("*")', 'records_found'))
  1157. ->from($this->_has_many[$alias]['through'])
  1158. ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk())
  1159. ->execute($this->_db)->get('records_found');
  1160. }
  1161. $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys;
  1162. // We need an array to simplify the logic
  1163. $far_keys = (array) $far_keys;
  1164. // Nothing to check if the model isn't loaded or we don't have any far_keys
  1165. if ( ! $far_keys OR ! $this->_loaded)
  1166. return FALSE;
  1167. $count = (int) DB::select(array('COUNT("*")', 'records_found'))
  1168. ->from($this->_has_many[$alias]['through'])
  1169. ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk())
  1170. ->where($this->_has_many[$alias]['far_key'], 'IN', $far_keys)
  1171. ->execute($this->_db)->get('records_found');
  1172. // Rows found need to match the rows searched
  1173. return $count === count($far_keys);
  1174. }
  1175. /**
  1176. * Adds a new relationship to between this model and another.
  1177. *
  1178. * // Add the login role using a model instance
  1179. * $model->add('roles', ORM::factory('role', array('name' => 'login')));
  1180. * // Add the login role if you know the roles.id is 5
  1181. * $model->add('roles', 5);
  1182. * // Add multiple roles (for example, from checkboxes on a form)
  1183. * $model->add('roles', array(1, 2, 3, 4));
  1184. *
  1185. * @param string $alias Alias of the has_many "through" relationship
  1186. * @param mixed $far_keys Related model, primary key, or an array of primary keys
  1187. * @return ORM
  1188. */
  1189. public function add($alias, $far_keys)
  1190. {
  1191. $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys;
  1192. $columns = array($this->_has_many[$alias]['foreign_key'], $this->_has_many[$alias]['far_key']);
  1193. $foreign_key = $this->pk();
  1194. $query = DB::insert($this->_has_many[$alias]['through'], $columns);
  1195. foreach ( (array) $far_keys as $key)
  1196. {
  1197. $query->values(array($foreign_key, $key));
  1198. }
  1199. $query->execute($this->_db);
  1200. return $this;
  1201. }
  1202. /**
  1203. * Removes a relationship between this model and another.
  1204. *
  1205. * // Remove a role using a model instance
  1206. * $model->remove('roles', ORM::factory('role', array('name' => 'login')));
  1207. * // Remove the role knowing the primary key
  1208. * $model->remove('roles', 5);
  1209. * // Remove multiple roles (for example, from checkboxes on a form)
  1210. * $model->remove('roles', array(1, 2, 3, 4));
  1211. * // Remove all related roles
  1212. * $model->remove('roles');
  1213. *
  1214. * @param string $alias Alias of the has_many "through" relationship
  1215. * @param mixed $far_keys Related model, primary key, or an array of primary keys
  1216. * @return ORM
  1217. */
  1218. public function remove($alias, $far_keys = NULL)
  1219. {
  1220. $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys;
  1221. $query = DB::delete($this->_has_many[$alias]['through'])
  1222. ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk());
  1223. if ($far_keys !== NULL)
  1224. {
  1225. // Remove all the relationships in the array
  1226. $query->where($this->_has_many[$alias]['far_key'], 'IN', (array) $far_keys);
  1227. }
  1228. $query->execute($this->_db);
  1229. return $this;
  1230. }
  1231. /**
  1232. * Count the number of records in the table.
  1233. *
  1234. * @return integer
  1235. */
  1236. public function count_all()
  1237. {
  1238. $selects = array();
  1239. foreach ($this->_db_pending as $key => $method)
  1240. {
  1241. if ($method['name'] == 'select')
  1242. {
  1243. // Ignore any selected columns for now
  1244. $selects[] = $method;
  1245. unset($this->_db_pending[$key]);
  1246. }
  1247. }
  1248. if ( ! empty($this->_load_with))
  1249. {
  1250. foreach ($this->_load_with as $alias)
  1251. {
  1252. // Bind relationship
  1253. $this->with($alias);
  1254. }
  1255. }
  1256. $this->_build(Database::SELECT);
  1257. $records = $this->_db_builder->from(array($this->_table_name, $this->_object_name))
  1258. ->select(array('COUNT("*")', 'records_found'))
  1259. ->execute($this->_db)
  1260. ->get('records_found');
  1261. // Add back in selected columns
  1262. $this->_db_pending += $selects;
  1263. $this->reset();
  1264. // Return the total number of records in a table
  1265. return $records;
  1266. }
  1267. /**
  1268. * Proxy method to Database list_columns.
  1269. *
  1270. * @return array
  1271. */
  1272. public function list_columns()
  1273. {
  1274. // Proxy to database
  1275. return $this->_db->list_columns($this->_table_name);
  1276. }
  1277. /**
  1278. * Returns an ORM model for the given one-one related alias
  1279. *
  1280. * @param string $alias Alias name
  1281. * @return ORM
  1282. */
  1283. protected function _related($alias)
  1284. {
  1285. if (isset($this->_related[$alias]))
  1286. {
  1287. return $this->_related[$alias];
  1288. }
  1289. elseif (isset($this->_has_one[$alias]))
  1290. {
  1291. return $this->_related[$alias] = ORM::factory($this->_has_one[$alias]['model']);
  1292. }
  1293. elseif (isset($this->_belongs_to[$alias]))
  1294. {
  1295. return $this->_related[$alias] = ORM::factory($this->_belongs_to[$alias]['model']);
  1296. }
  1297. else
  1298. {
  1299. return FALSE;
  1300. }
  1301. }
  1302. /**
  1303. * Returns the value of the primary key
  1304. *
  1305. * @return mixed Primary key
  1306. */
  1307. public function pk()
  1308. {
  1309. return $this->_primary_key_value;
  1310. }
  1311. /**
  1312. * Returns last executed query
  1313. *
  1314. * @return string
  1315. */
  1316. public function last_query()
  1317. {
  1318. return $this->_db->last_query;
  1319. }
  1320. /**
  1321. * Clears query builder. Passing FALSE is useful to keep the existing
  1322. * query conditions for another query.
  1323. *
  1324. * @param bool $next Pass FALSE to avoid resetting on the next call
  1325. * @return ORM
  1326. */
  1327. public function reset($next = TRUE)
  1328. {
  1329. if ($next AND $this->_db_reset)
  1330. {
  1331. $this->_db_pending = array();
  1332. $this->_db_applied = array();
  1333. $this->_db_builder = NULL;
  1334. $this->_with_applied = array();
  1335. }
  1336. // Reset on the next call?
  1337. $this->_db_reset = $next;
  1338. return $this;
  1339. }
  1340. protected function _serialize_value($value)
  1341. {
  1342. return json_encode($value);
  1343. }
  1344. protected function _unserialize_value($value)
  1345. {
  1346. return json_decode($value);
  1347. }
  1348. public function object_name()
  1349. {
  1350. return $this->_object_name;
  1351. }
  1352. public function object_plural()
  1353. {
  1354. return $this->_object_plural;
  1355. }
  1356. public function loaded()
  1357. {
  1358. return $this->_loaded;
  1359. }
  1360. public function saved()
  1361. {
  1362. return $this->_saved;
  1363. }
  1364. public function primary_key()
  1365. {
  1366. return $this->_primary_key;
  1367. }
  1368. public function table_name()
  1369. {
  1370. return $this->_table_name;
  1371. }
  1372. public function table_columns()
  1373. {
  1374. return $this->_table_columns;
  1375. }
  1376. public function has_one()
  1377. {
  1378. return $this->_has_one;
  1379. }
  1380. public function belongs_to()
  1381. {
  1382. return $this->_belongs_to;
  1383. }
  1384. public function has_many()
  1385. {
  1386. return $this->_has_many;
  1387. }
  1388. public function load_with()
  1389. {
  1390. return $this->_load_with;
  1391. }
  1392. public function original_values()
  1393. {
  1394. return $this->_original_values;
  1395. }
  1396. public function created_column()
  1397. {
  1398. return $this->_created_column;
  1399. }
  1400. public function updated_column()
  1401. {
  1402. return $this->_updated_column;
  1403. }
  1404. public function validation()
  1405. {
  1406. if ( ! isset($this->_validation))
  1407. {
  1408. // Initialize the validation object
  1409. $this->_validation();
  1410. }
  1411. return $this->_validation;
  1412. }
  1413. public function object()
  1414. {
  1415. return $this->_object;
  1416. }
  1417. public function errors_filename()
  1418. {
  1419. return $this->_errors_filename;
  1420. }
  1421. /**
  1422. * Alias of and_where()
  1423. *
  1424. * @param mixed column name or array($column, $alias) or object
  1425. * @param string logic operator
  1426. * @param mixed column value
  1427. * @return $this
  1428. */
  1429. public function where($column, $op, $value)
  1430. {
  1431. // Add pending database call which is executed after query type is determined
  1432. $this->_db_pending[] = array(
  1433. 'name' => 'where',
  1434. 'args' => array($column, $op, $value),
  1435. );
  1436. return $this;
  1437. }
  1438. /**
  1439. * Creates a new "AND WHERE" condition for the query.
  1440. *
  1441. * @param mixed column name or array($column, $alias) or object
  1442. * @param string logic operator
  1443. * @param mixed column value
  1444. * @return $this
  1445. */
  1446. public function and_where($column, $op, $value)
  1447. {
  1448. // Add pending database call which is executed after query type is determined
  1449. $this->_db_pending[] = array(
  1450. 'name' => 'and_where',
  1451. 'args' => array($column, $op, $value),
  1452. );
  1453. return $this;
  1454. }
  1455. /**
  1456. * Creates a new "OR WHERE" condition for the query.
  1457. *
  1458. * @param mixed column name or array($column, $alias) or object
  1459. * @param string logic operator
  1460. * @param mixed column value
  1461. * @return $this
  1462. */
  1463. public function or_where($column, $op, $value)
  1464. {
  1465. // Add pending database call which is executed after query type is determined
  1466. $this->_db_pending[] = array(
  1467. 'name' => 'or_where',
  1468. 'args' => array($column, $op, $value),
  1469. );
  1470. return $this;
  1471. }
  1472. /**
  1473. * Alias of and_where_open()
  1474. *
  1475. * @return $this
  1476. */
  1477. public function where_open()
  1478. {
  1479. return $this->and_where_open();
  1480. }
  1481. /**
  1482. * Opens a new "AND WHERE (...)" grouping.
  1483. *
  1484. * @return $this
  1485. */
  1486. public function and_where_open()
  1487. {
  1488. // Add pending database call which is executed after query type is determined
  1489. $this->_db_pending[] = array(
  1490. 'name' => 'and_where_open',
  1491. 'args' => array(),
  1492. );
  1493. return $this;
  1494. }
  1495. /**
  1496. * Opens a new "OR WHERE (...)" grouping.
  1497. *
  1498. * @return $this
  1499. */
  1500. public function or_where_open()
  1501. {
  1502. // Add pending database call which is executed after query type is determined
  1503. $this->_db_pending[] = array(
  1504. 'name' => 'or_where_open',
  1505. 'args' => array(),
  1506. );
  1507. return $this;
  1508. }
  1509. /**
  1510. * Closes an open "AND WHERE (...)" grouping.
  1511. *
  1512. * @return $this
  1513. */
  1514. public function where_close()
  1515. {
  1516. return $this->and_where_close();
  1517. }
  1518. /**
  1519. * Closes an open "AND WHERE (...)" grouping.
  1520. *
  1521. * @return $this
  1522. */
  1523. public function and_where_close()
  1524. {
  1525. // Add pending database call which is executed after query type is determined
  1526. $this->_db_pending[] = array(
  1527. 'name' => 'and_where_close',
  1528. 'args' => array(),
  1529. );
  1530. return $this;
  1531. }
  1532. /**
  1533. * Closes an open "OR WHERE (...)" grouping.
  1534. *
  1535. * @return $this
  1536. */
  1537. public function or_where_close()
  1538. {
  1539. // Add pending database call which is executed after query type is determined
  1540. $this->_db_pending[] = array(
  1541. 'name' => 'or_where_close',
  1542. 'args' => array(),
  1543. );
  1544. return $this;
  1545. }
  1546. /**
  1547. * Applies sorting with "ORDER BY ..."
  1548. *
  1549. * @param mixed column name or array($column, $alias) or object
  1550. * @param string direction of sorting
  1551. * @return $this
  1552. */
  1553. public function order_by($column, $direction = NULL)
  1554. {
  1555. // Add pending database call which is executed after query type is determined
  1556. $this->_db_pending[] = array(
  1557. 'name' => 'order_by',
  1558. 'args' => array($column, $direction),
  1559. );
  1560. return $this;
  1561. }
  1562. /**
  1563. * Return up to "LIMIT ..." results
  1564. *
  1565. * @param integer maximum results to return
  1566. * @return $this
  1567. */
  1568. public function limit($number)
  1569. {
  1570. // Add pending database call which is executed after query type is determined
  1571. $this->_db_pending[] = array(
  1572. 'name' => 'limit',
  1573. 'args' => array($number),
  1574. );
  1575. return $this;
  1576. }
  1577. /**
  1578. * Enables or disables selecting only unique columns using "SELECT DISTINCT"
  1579. *
  1580. * @param boolean enable or disable distinct columns
  1581. * @return $this
  1582. */
  1583. public function distinct($value)
  1584. {
  1585. // Add pending database call which is executed after query type is determined
  1586. $this->_db_pending[] = array(
  1587. 'name' => 'distinct',
  1588. 'args' => array($value),
  1589. );
  1590. return $this;
  1591. }
  1592. /**
  1593. * Choose the columns to select from.
  1594. *
  1595. * @param mixed column name or array($column, $alias) or object
  1596. * @param ...
  1597. * @return $this
  1598. */
  1599. public function select($columns = NULL)
  1600. {
  1601. $columns = func_get_args();
  1602. // Add pending database call which is executed after query type is determined
  1603. $this->_db_pending[] = array(
  1604. 'name' => 'select',
  1605. 'args' => $columns,
  1606. );
  1607. return $this;
  1608. }
  1609. /**
  1610. * Choose the tables to select "FROM ..."
  1611. *
  1612. * @param mixed table name or array($table, $alias) or object
  1613. * @param ...
  1614. * @return $this
  1615. */
  1616. public function from($tables)
  1617. {
  1618. $tables = func_get_args();
  1619. // Add pending database call which is executed after query type is determined
  1620. $this->_db_pending[] = array(
  1621. 'name' => 'from',
  1622. 'args' => $tables,
  1623. );
  1624. return $this;
  1625. }
  1626. /**
  1627. * Adds addition tables to "JOIN ...".
  1628. *
  1629. * @param mixed column name or array($column, $alias) or object
  1630. * @param string join type (LEFT, RIGHT, INNER, etc)
  1631. * @return $this
  1632. */
  1633. public function join($table, $type = NULL)
  1634. {
  1635. // Add pending database call which is executed after query type is determined
  1636. $this->_db_pending[] = array(
  1637. 'name' => 'join',
  1638. 'args' => array($table, $type),
  1639. );
  1640. return $this;
  1641. }
  1642. /**
  1643. * Adds "ON ..." conditions for the last created JOIN statement.
  1644. *
  1645. * @param mixed column name or array($column, $alias) or object
  1646. * @param string logic operator
  1647. * @param mixed column name or array($column, $alias) or object
  1648. * @return $this
  1649. */
  1650. public function on($c1, $op, $c2)
  1651. {
  1652. // Add pending database call which is executed after query type is determined
  1653. $this->_db_pending[] = array(
  1654. 'name' => 'on',
  1655. 'args' => array($c1, $op, $c2),
  1656. );
  1657. return $this;
  1658. }
  1659. /**
  1660. * Creates a "GROUP BY ..." filter.
  1661. *
  1662. * @param mixed column name or array($column, $alias) or object
  1663. * @param ...
  1664. * @return $this
  1665. */
  1666. public function group_by($columns)
  1667. {
  1668. $columns = func_get_args();
  1669. // Add pending database call which is executed after query type is determined
  1670. $this->_db_pending[] = array(
  1671. 'name' => 'group_by',
  1672. 'args' => $columns,
  1673. );
  1674. return $this;
  1675. }
  1676. /**
  1677. * Alias of and_having()
  1678. *
  1679. * @param mixed column name or array($column, $alias) or object
  1680. * @param string logic operator
  1681. * @param mixed column value
  1682. * @return $this
  1683. */
  1684. public function having($column, $op, $value = NULL)
  1685. {
  1686. return $this->and_having($column, $op, $value);
  1687. }
  1688. /**
  1689. * Creates a new "AND HAVING" condition for the query.
  1690. *
  1691. * @param mixed column name or array($column, $alias) or object
  1692. * @param string logic operator
  1693. * @param mixed column value
  1694. * @return $this
  1695. */
  1696. public function and_having($column, $op, $value = NULL)
  1697. {
  1698. // Add pending database call which is executed after query type is determined
  1699. $this->_db_pending[] = array(
  1700. 'name' => 'and_having',
  1701. 'args' => array($column, $op, $value),
  1702. );
  1703. return $this;
  1704. }
  1705. /**
  1706. * Creates a new "OR HAVING" condition for the query.
  1707. *
  1708. * @param mixed column name or array($column, $alias) or object
  1709. * @param string logic operator
  1710. * @param mixed column value
  1711. * @return $this
  1712. */
  1713. public function or_having($column, $op, $value = NULL)
  1714. {
  1715. // Add pending database call which is executed after query type is determined
  1716. $this->_db_pending[] = array(
  1717. 'name' => 'or_having',
  1718. 'args' => array($column, $op, $value),
  1719. );
  1720. return $this;
  1721. }
  1722. /**
  1723. * Alias of and_having_open()
  1724. *
  1725. * @return $this
  1726. */
  1727. public function having_open()
  1728. {
  1729. return $this->and_having_open();
  1730. }
  1731. /**
  1732. * Opens a new "AND HAVING (...)" grouping.
  1733. *
  1734. * @return $this
  1735. */
  1736. public function and_having_open()
  1737. {
  1738. // Add pending database call which is executed after query type is determined
  1739. $this->_db_pending[] = array(
  1740. 'name' => 'and_having_open',
  1741. 'args' => array(),
  1742. );
  1743. return $this;
  1744. }
  1745. /**
  1746. * Opens a new "OR HAVING (...)" grouping.
  1747. *
  1748. * @return $this
  1749. */
  1750. public function or_having_open()
  1751. {
  1752. // Add pending database call which is executed after query type is determined
  1753. $this->_db_pending[] = array(
  1754. 'name' => 'or_having_open',
  1755. 'args' => array(),
  1756. );
  1757. return $this;
  1758. }
  1759. /**
  1760. * Closes an open "AND HAVING (...)" grouping.
  1761. *
  1762. * @return $this
  1763. */
  1764. public function having_close()
  1765. {
  1766. return $this->and_having_close();
  1767. }
  1768. /**
  1769. * Closes an open "AND HAVING (...)" grouping.
  1770. *
  1771. * @return $this
  1772. */
  1773. public function and_having_close()
  1774. {
  1775. // Add pending database call which is executed after query type is determined
  1776. $this->_db_pending[] = array(
  1777. 'name' => 'and_having_close',
  1778. 'args' => array(),
  1779. );
  1780. return $this;
  1781. }
  1782. /**
  1783. * Closes an open "OR HAVING (...)" grouping.
  1784. *
  1785. * @return $this
  1786. */
  1787. public function or_having_close()
  1788. {
  1789. // Add pending database call which is executed after query type is determined
  1790. $this->_db_pending[] = array(
  1791. 'name' => 'or_having_close',
  1792. 'args' => array(),
  1793. );
  1794. return $this;
  1795. }
  1796. /**
  1797. * Start returning results after "OFFSET ..."
  1798. *
  1799. * @param integer starting result number
  1800. * @return $this
  1801. */
  1802. public function offset($number)
  1803. {
  1804. // Add pending database call which is executed after query type is determined
  1805. $this->_db_pending[] = array(
  1806. 'name' => 'offset',
  1807. 'args' => array($number),
  1808. );
  1809. return $this;
  1810. }
  1811. /**
  1812. * Enables the query to be cached for a specified amount of time.
  1813. *
  1814. * @param integer number of seconds to cache
  1815. * @return $this
  1816. * @uses Kohana::$cache_life
  1817. */
  1818. public function cached($lifetime = NULL)
  1819. {
  1820. // Add pending database call which is executed after query type is determined
  1821. $this->_db_pending[] = array(
  1822. 'name' => 'cached',
  1823. 'args' => array($lifetime),
  1824. );
  1825. return $this;
  1826. }
  1827. /**
  1828. * Set the value of a parameter in the query.
  1829. *
  1830. * @param string parameter key to replace
  1831. * @param mixed value to use
  1832. * @return $this
  1833. */
  1834. public function param($param, $value)
  1835. {
  1836. // Add pending database call which is executed after query type is determined
  1837. $this->_db_pending[] = array(
  1838. 'name' => 'param',
  1839. 'args' => array($param, $value),
  1840. );
  1841. return $this;
  1842. }
  1843. /**
  1844. * Checks whether a column value is unique.
  1845. * Excludes itself if loaded.
  1846. *
  1847. * @param string the field to check for uniqueness
  1848. * @param mixed the value to check for uniqueness
  1849. * @return bool whteher the value is unique
  1850. */
  1851. public function unique($field, $value)
  1852. {
  1853. $model = ORM::factory($this->object_name())
  1854. ->where($field, '=', $value)
  1855. ->find();
  1856. if ($this->loaded())
  1857. {
  1858. return ( ! ($model->loaded() AND $model->pk() != $this->pk()));
  1859. }
  1860. return ( ! $model->loaded());
  1861. }
  1862. } // End ORM