PageRenderTime 29ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/php-activerecord/php-activerecord/lib/Table.php

https://gitlab.com/sulistiana/api-mrbn
PHP | 555 lines | 402 code | 78 blank | 75 comment | 46 complexity | 4402c088615e3b4492cfd226d387cec9 MD5 | raw file
  1. <?php
  2. /**
  3. * @package ActiveRecord
  4. */
  5. namespace ActiveRecord;
  6. /**
  7. * Manages reading and writing to a database table.
  8. *
  9. * This class manages a database table and is used by the Model class for
  10. * reading and writing to its database table. There is one instance of Table
  11. * for every table you have a model for.
  12. *
  13. * @package ActiveRecord
  14. */
  15. class Table
  16. {
  17. private static $cache = array();
  18. public $class;
  19. public $conn;
  20. public $pk;
  21. public $last_sql;
  22. // Name/value pairs of columns in this table
  23. public $columns = array();
  24. /**
  25. * Name of the table.
  26. */
  27. public $table;
  28. /**
  29. * Name of the database (optional)
  30. */
  31. public $db_name;
  32. /**
  33. * Name of the sequence for this table (optional). Defaults to {$table}_seq
  34. */
  35. public $sequence;
  36. /**
  37. * A instance of CallBack for this model/table
  38. * @static
  39. * @var object ActiveRecord\CallBack
  40. */
  41. public $callback;
  42. /**
  43. * List of relationships for this table.
  44. */
  45. private $relationships = array();
  46. public static function load($model_class_name)
  47. {
  48. if (!isset(self::$cache[$model_class_name]))
  49. {
  50. /* do not place set_assoc in constructor..it will lead to infinite loop due to
  51. relationships requesting the model's table, but the cache hasn't been set yet */
  52. self::$cache[$model_class_name] = new Table($model_class_name);
  53. self::$cache[$model_class_name]->set_associations();
  54. }
  55. return self::$cache[$model_class_name];
  56. }
  57. public static function clear_cache($model_class_name=null)
  58. {
  59. if ($model_class_name && array_key_exists($model_class_name,self::$cache))
  60. unset(self::$cache[$model_class_name]);
  61. else
  62. self::$cache = array();
  63. }
  64. public function __construct($class_name)
  65. {
  66. $this->class = Reflections::instance()->add($class_name)->get($class_name);
  67. $this->reestablish_connection(false);
  68. $this->set_table_name();
  69. $this->get_meta_data();
  70. $this->set_primary_key();
  71. $this->set_sequence_name();
  72. $this->set_delegates();
  73. $this->set_setters_and_getters();
  74. $this->callback = new CallBack($class_name);
  75. $this->callback->register('before_save', function(Model $model) { $model->set_timestamps(); }, array('prepend' => true));
  76. $this->callback->register('after_save', function(Model $model) { $model->reset_dirty(); }, array('prepend' => true));
  77. }
  78. public function reestablish_connection($close=true)
  79. {
  80. // if connection name property is null the connection manager will use the default connection
  81. $connection = $this->class->getStaticPropertyValue('connection',null);
  82. if ($close)
  83. {
  84. ConnectionManager::drop_connection($connection);
  85. static::clear_cache();
  86. }
  87. return ($this->conn = ConnectionManager::get_connection($connection));
  88. }
  89. public function create_joins($joins)
  90. {
  91. if (!is_array($joins))
  92. return $joins;
  93. $self = $this->table;
  94. $ret = $space = '';
  95. $existing_tables = array();
  96. foreach ($joins as $value)
  97. {
  98. $ret .= $space;
  99. if (stripos($value,'JOIN ') === false)
  100. {
  101. if (array_key_exists($value, $this->relationships))
  102. {
  103. $rel = $this->get_relationship($value);
  104. // if there is more than 1 join for a given table we need to alias the table names
  105. if (array_key_exists($rel->class_name, $existing_tables))
  106. {
  107. $alias = $value;
  108. $existing_tables[$rel->class_name]++;
  109. }
  110. else
  111. {
  112. $existing_tables[$rel->class_name] = true;
  113. $alias = null;
  114. }
  115. $ret .= $rel->construct_inner_join_sql($this, false, $alias);
  116. }
  117. else
  118. throw new RelationshipException("Relationship named $value has not been declared for class: {$this->class->getName()}");
  119. }
  120. else
  121. $ret .= $value;
  122. $space = ' ';
  123. }
  124. return $ret;
  125. }
  126. public function options_to_sql($options)
  127. {
  128. $table = array_key_exists('from', $options) ? $options['from'] : $this->get_fully_qualified_table_name();
  129. $sql = new SQLBuilder($this->conn, $table);
  130. if (array_key_exists('joins',$options))
  131. {
  132. $sql->joins($this->create_joins($options['joins']));
  133. // by default, an inner join will not fetch the fields from the joined table
  134. if (!array_key_exists('select', $options))
  135. $options['select'] = $this->get_fully_qualified_table_name() . '.*';
  136. }
  137. if (array_key_exists('select',$options))
  138. $sql->select($options['select']);
  139. if (array_key_exists('conditions',$options))
  140. {
  141. if (!is_hash($options['conditions']))
  142. {
  143. if (is_string($options['conditions']))
  144. $options['conditions'] = array($options['conditions']);
  145. call_user_func_array(array($sql,'where'),$options['conditions']);
  146. }
  147. else
  148. {
  149. if (!empty($options['mapped_names']))
  150. $options['conditions'] = $this->map_names($options['conditions'],$options['mapped_names']);
  151. $sql->where($options['conditions']);
  152. }
  153. }
  154. if (array_key_exists('order',$options))
  155. $sql->order($options['order']);
  156. if (array_key_exists('limit',$options))
  157. $sql->limit($options['limit']);
  158. if (array_key_exists('offset',$options))
  159. $sql->offset($options['offset']);
  160. if (array_key_exists('group',$options))
  161. $sql->group($options['group']);
  162. if (array_key_exists('having',$options))
  163. $sql->having($options['having']);
  164. return $sql;
  165. }
  166. public function find($options)
  167. {
  168. $sql = $this->options_to_sql($options);
  169. $readonly = (array_key_exists('readonly',$options) && $options['readonly']) ? true : false;
  170. $eager_load = array_key_exists('include',$options) ? $options['include'] : null;
  171. return $this->find_by_sql($sql->to_s(),$sql->get_where_values(), $readonly, $eager_load);
  172. }
  173. public function find_by_sql($sql, $values=null, $readonly=false, $includes=null)
  174. {
  175. $this->last_sql = $sql;
  176. $collect_attrs_for_includes = is_null($includes) ? false : true;
  177. $list = $attrs = array();
  178. $sth = $this->conn->query($sql,$this->process_data($values));
  179. while (($row = $sth->fetch()))
  180. {
  181. $model = new $this->class->name($row,false,true,false);
  182. if ($readonly)
  183. $model->readonly();
  184. if ($collect_attrs_for_includes)
  185. $attrs[] = $model->attributes();
  186. $list[] = $model;
  187. }
  188. if ($collect_attrs_for_includes && !empty($list))
  189. $this->execute_eager_load($list, $attrs, $includes);
  190. return $list;
  191. }
  192. /**
  193. * Executes an eager load of a given named relationship for this table.
  194. *
  195. * @param $models array found modesl for this table
  196. * @param $attrs array of attrs from $models
  197. * @param $includes array eager load directives
  198. * @return void
  199. */
  200. private function execute_eager_load($models=array(), $attrs=array(), $includes=array())
  201. {
  202. if (!is_array($includes))
  203. $includes = array($includes);
  204. foreach ($includes as $index => $name)
  205. {
  206. // nested include
  207. if (is_array($name))
  208. {
  209. $nested_includes = count($name) > 0 ? $name : $name[0];
  210. $name = $index;
  211. }
  212. else
  213. $nested_includes = array();
  214. $rel = $this->get_relationship($name, true);
  215. $rel->load_eagerly($models, $attrs, $nested_includes, $this);
  216. }
  217. }
  218. public function get_column_by_inflected_name($inflected_name)
  219. {
  220. foreach ($this->columns as $raw_name => $column)
  221. {
  222. if ($column->inflected_name == $inflected_name)
  223. return $column;
  224. }
  225. return null;
  226. }
  227. public function get_fully_qualified_table_name($quote_name=true)
  228. {
  229. $table = $quote_name ? $this->conn->quote_name($this->table) : $this->table;
  230. if ($this->db_name)
  231. $table = $this->conn->quote_name($this->db_name) . ".$table";
  232. return $table;
  233. }
  234. /**
  235. * Retrieve a relationship object for this table. Strict as true will throw an error
  236. * if the relationship name does not exist.
  237. *
  238. * @param $name string name of Relationship
  239. * @param $strict bool
  240. * @throws RelationshipException
  241. * @return Relationship or null
  242. */
  243. public function get_relationship($name, $strict=false)
  244. {
  245. if ($this->has_relationship($name))
  246. return $this->relationships[$name];
  247. if ($strict)
  248. throw new RelationshipException("Relationship named $name has not been declared for class: {$this->class->getName()}");
  249. return null;
  250. }
  251. /**
  252. * Does a given relationship exist?
  253. *
  254. * @param $name string name of Relationship
  255. * @return bool
  256. */
  257. public function has_relationship($name)
  258. {
  259. return array_key_exists($name, $this->relationships);
  260. }
  261. public function insert(&$data, $pk=null, $sequence_name=null)
  262. {
  263. $data = $this->process_data($data);
  264. $sql = new SQLBuilder($this->conn,$this->get_fully_qualified_table_name());
  265. $sql->insert($data,$pk,$sequence_name);
  266. $values = array_values($data);
  267. return $this->conn->query(($this->last_sql = $sql->to_s()),$values);
  268. }
  269. public function update(&$data, $where)
  270. {
  271. $data = $this->process_data($data);
  272. $sql = new SQLBuilder($this->conn,$this->get_fully_qualified_table_name());
  273. $sql->update($data)->where($where);
  274. $values = $sql->bind_values();
  275. return $this->conn->query(($this->last_sql = $sql->to_s()),$values);
  276. }
  277. public function delete($data)
  278. {
  279. $data = $this->process_data($data);
  280. $sql = new SQLBuilder($this->conn,$this->get_fully_qualified_table_name());
  281. $sql->delete($data);
  282. $values = $sql->bind_values();
  283. return $this->conn->query(($this->last_sql = $sql->to_s()),$values);
  284. }
  285. /**
  286. * Add a relationship.
  287. *
  288. * @param Relationship $relationship a Relationship object
  289. */
  290. private function add_relationship($relationship)
  291. {
  292. $this->relationships[$relationship->attribute_name] = $relationship;
  293. }
  294. private function get_meta_data()
  295. {
  296. // as more adapters are added probably want to do this a better way
  297. // than using instanceof but gud enuff for now
  298. $quote_name = !($this->conn instanceof PgsqlAdapter);
  299. $table_name = $this->get_fully_qualified_table_name($quote_name);
  300. $conn = $this->conn;
  301. $this->columns = Cache::get("get_meta_data-$table_name", function() use ($conn, $table_name) { return $conn->columns($table_name); });
  302. }
  303. /**
  304. * Replaces any aliases used in a hash based condition.
  305. *
  306. * @param $hash array A hash
  307. * @param $map array Hash of used_name => real_name
  308. * @return array Array with any aliases replaced with their read field name
  309. */
  310. private function map_names(&$hash, &$map)
  311. {
  312. $ret = array();
  313. foreach ($hash as $name => &$value)
  314. {
  315. if (array_key_exists($name,$map))
  316. $name = $map[$name];
  317. $ret[$name] = $value;
  318. }
  319. return $ret;
  320. }
  321. private function &process_data($hash)
  322. {
  323. if (!$hash)
  324. return $hash;
  325. foreach ($hash as $name => &$value)
  326. {
  327. if ($value instanceof \DateTime)
  328. {
  329. if (isset($this->columns[$name]) && $this->columns[$name]->type == Column::DATE)
  330. $hash[$name] = $this->conn->date_to_string($value);
  331. else
  332. $hash[$name] = $this->conn->datetime_to_string($value);
  333. }
  334. else
  335. $hash[$name] = $value;
  336. }
  337. return $hash;
  338. }
  339. private function set_primary_key()
  340. {
  341. if (($pk = $this->class->getStaticPropertyValue('pk',null)) || ($pk = $this->class->getStaticPropertyValue('primary_key',null)))
  342. $this->pk = is_array($pk) ? $pk : array($pk);
  343. else
  344. {
  345. $this->pk = array();
  346. foreach ($this->columns as $c)
  347. {
  348. if ($c->pk)
  349. $this->pk[] = $c->inflected_name;
  350. }
  351. }
  352. }
  353. private function set_table_name()
  354. {
  355. if (($table = $this->class->getStaticPropertyValue('table',null)) || ($table = $this->class->getStaticPropertyValue('table_name',null)))
  356. $this->table = $table;
  357. else
  358. {
  359. // infer table name from the class name
  360. $this->table = Inflector::instance()->tableize($this->class->getName());
  361. // strip namespaces from the table name if any
  362. $parts = explode('\\',$this->table);
  363. $this->table = $parts[count($parts)-1];
  364. }
  365. if(($db = $this->class->getStaticPropertyValue('db',null)) || ($db = $this->class->getStaticPropertyValue('db_name',null)))
  366. $this->db_name = $db;
  367. }
  368. private function set_sequence_name()
  369. {
  370. if (!$this->conn->supports_sequences())
  371. return;
  372. if (!($this->sequence = $this->class->getStaticPropertyValue('sequence')))
  373. $this->sequence = $this->conn->get_sequence_name($this->table,$this->pk[0]);
  374. }
  375. private function set_associations()
  376. {
  377. require_once 'Relationship.php';
  378. $namespace = $this->class->getNamespaceName();
  379. foreach ($this->class->getStaticProperties() as $name => $definitions)
  380. {
  381. if (!$definitions)# || !is_array($definitions))
  382. continue;
  383. foreach (wrap_strings_in_arrays($definitions) as $definition)
  384. {
  385. $relationship = null;
  386. $definition += compact('namespace');
  387. switch ($name)
  388. {
  389. case 'has_many':
  390. $relationship = new HasMany($definition);
  391. break;
  392. case 'has_one':
  393. $relationship = new HasOne($definition);
  394. break;
  395. case 'belongs_to':
  396. $relationship = new BelongsTo($definition);
  397. break;
  398. case 'has_and_belongs_to_many':
  399. $relationship = new HasAndBelongsToMany($definition);
  400. break;
  401. }
  402. if ($relationship)
  403. $this->add_relationship($relationship);
  404. }
  405. }
  406. }
  407. /**
  408. * Rebuild the delegates array into format that we can more easily work with in Model.
  409. * Will end up consisting of array of:
  410. *
  411. * array('delegate' => array('field1','field2',...),
  412. * 'to' => 'delegate_to_relationship',
  413. * 'prefix' => 'prefix')
  414. */
  415. private function set_delegates()
  416. {
  417. $delegates = $this->class->getStaticPropertyValue('delegate',array());
  418. $new = array();
  419. if (!array_key_exists('processed', $delegates))
  420. $delegates['processed'] = false;
  421. if (!empty($delegates) && !$delegates['processed'])
  422. {
  423. foreach ($delegates as &$delegate)
  424. {
  425. if (!is_array($delegate) || !isset($delegate['to']))
  426. continue;
  427. if (!isset($delegate['prefix']))
  428. $delegate['prefix'] = null;
  429. $new_delegate = array(
  430. 'to' => $delegate['to'],
  431. 'prefix' => $delegate['prefix'],
  432. 'delegate' => array());
  433. foreach ($delegate as $name => $value)
  434. {
  435. if (is_numeric($name))
  436. $new_delegate['delegate'][] = $value;
  437. }
  438. $new[] = $new_delegate;
  439. }
  440. $new['processed'] = true;
  441. $this->class->setStaticPropertyValue('delegate',$new);
  442. }
  443. }
  444. /**
  445. * @deprecated Model.php now checks for get|set_ methods via method_exists so there is no need for declaring static g|setters.
  446. */
  447. private function set_setters_and_getters()
  448. {
  449. $getters = $this->class->getStaticPropertyValue('getters', array());
  450. $setters = $this->class->getStaticPropertyValue('setters', array());
  451. if (!empty($getters) || !empty($setters))
  452. trigger_error('static::$getters and static::$setters are deprecated. Please define your setters and getters by declaring methods in your model prefixed with get_ or set_. See
  453. http://www.phpactiverecord.org/projects/main/wiki/Utilities#attribute-setters and http://www.phpactiverecord.org/projects/main/wiki/Utilities#attribute-getters on how to make use of this option.', E_USER_DEPRECATED);
  454. }
  455. };
  456. ?>