ORM.php 57 KB

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