Anonymous 3D Imageboard http://cyberia.digital/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
11KB

  1. <?php
  2. namespace Lazer\Classes;
  3. use Lazer\Classes\Helpers\Validate;
  4. use Lazer\Classes\Helpers\Config;
  5. use Lazer\Classes\Database;
  6. use Lazer\Classes\LazerException;
  7. /**
  8. * Relation class of LAZER project.
  9. *
  10. * @category Core
  11. * @author Grzegorz Kuźnik
  12. * @copyright (c) 2013, Grzegorz Kuźnik
  13. * @license http://opensource.org/licenses/MIT The MIT License
  14. * @link https://github.com/Greg0/Lazer-Database GitHub Repository
  15. */
  16. abstract class Core_Relation {
  17. /**
  18. * Tables names
  19. * @var array tables
  20. */
  21. protected $tables = array(
  22. 'local' => null,
  23. 'foreign' => null
  24. );
  25. /**
  26. * Relation keys names
  27. * @var array keys
  28. */
  29. protected $keys = array(
  30. 'local' => null,
  31. 'foreign' => null
  32. );
  33. /**
  34. * Current relation type
  35. * @var string
  36. */
  37. protected $relationType;
  38. /**
  39. * All relations types
  40. * @var array
  41. */
  42. protected static $relations = array('belongsTo', 'hasMany', 'hasAndBelongsToMany');
  43. /**
  44. * Factory method
  45. * @param string $name Name of table
  46. * @return \Lazer\Classes\Relation
  47. */
  48. public static function table($name)
  49. {
  50. Validate::table($name)->exists();
  51. $self = new Relation;
  52. $self->tables['local'] = $name;
  53. return $self;
  54. }
  55. /**
  56. * Getter of junction table name in many2many relation
  57. * @return boolean|string Name of junction table or false
  58. */
  59. public function getJunction()
  60. {
  61. if ($this->relationType == 'hasAndBelongsToMany')
  62. {
  63. $tables = $this->tables;
  64. sort($tables);
  65. return implode('_', $tables);
  66. }
  67. return false;
  68. }
  69. /**
  70. * Set relation type to field
  71. * @param string $relation Name of relation
  72. */
  73. protected function setRelationType($relation)
  74. {
  75. Validate::relationType($relation);
  76. $this->relationType = $relation;
  77. }
  78. /**
  79. * Set table name
  80. * @param string $type local or foreign
  81. * @param string $name table name
  82. */
  83. protected function setTable($type, $name)
  84. {
  85. Validate::table($name)->exists();
  86. $this->tables[$type] = $name;
  87. }
  88. /**
  89. * Set key name
  90. * @param string $type local or foreign
  91. * @param string $key key name
  92. * @return \Lazer\Classes\Core_Relation
  93. * @throws LazerException First you must define tables name
  94. */
  95. protected function setKey($type, $key)
  96. {
  97. if (!in_array(null, $this->tables))
  98. {
  99. Validate::table($this->tables[$type])->field($key);
  100. $this->keys[$type] = $key;
  101. return $this;
  102. }
  103. throw new LazerException('First you must define tables name');
  104. }
  105. /**
  106. * Set local key name
  107. * @param string $key key name
  108. * @return \Lazer\Classes\Core_Relation
  109. * @throws LazerException First you must define tables name
  110. */
  111. public function localKey($key)
  112. {
  113. return $this->setKey('local', $key);
  114. }
  115. /**
  116. * Set foreign key name
  117. * @param string $key key name
  118. * @return \Lazer\Classes\Core_Relation
  119. * @throws LazerException First you must define tables name
  120. */
  121. public function foreignKey($key)
  122. {
  123. return $this->setKey('foreign', $key);
  124. }
  125. /**
  126. * Set relation one2many to table
  127. * @param string $table Table name
  128. * @return \Lazer\Classes\Core_Relation
  129. */
  130. public function belongsTo($table)
  131. {
  132. $this->setTable('foreign', $table);
  133. $this->setRelationType(__FUNCTION__);
  134. return $this;
  135. }
  136. /**
  137. * Set relation many2one to table
  138. * @param string $table Table name
  139. * @return \Lazer\Classes\Core_Relation
  140. */
  141. public function hasMany($table)
  142. {
  143. $this->setTable('foreign', $table);
  144. $this->setRelationType(__FUNCTION__);
  145. return $this;
  146. }
  147. /**
  148. * Set relation many2many to table
  149. * @param string $table Table name
  150. * @return \Lazer\Classes\Core_Relation
  151. */
  152. public function hasAndBelongsToMany($table)
  153. {
  154. $this->setTable('foreign', $table);
  155. $this->setRelationType(__FUNCTION__);
  156. return $this;
  157. }
  158. /**
  159. * Use relation to table
  160. * @param string $table Table name
  161. * @return \Lazer\Classes\Core_Relation
  162. */
  163. public function with($table)
  164. {
  165. Validate::relation($this->tables['local'], $table);
  166. $this->setTable('foreign', $table);
  167. $this->setRelationType(Config::table($this->tables['local'])->relations($this->tables['foreign'])->type);
  168. $this->setKey('local', Config::table($this->tables['local'])->relations($this->tables['foreign'])->keys->local);
  169. $this->setKey('foreign', Config::table($this->tables['local'])->relations($this->tables['foreign'])->keys->foreign);
  170. return $this;
  171. }
  172. /**
  173. * Set specified relation
  174. * @throws LazerException Tables names or keys missing
  175. */
  176. public function setRelation()
  177. {
  178. if (!in_array(null, $this->tables) && !in_array(null, $this->keys))
  179. {
  180. $this->addRelation();
  181. return true;
  182. }
  183. else
  184. {
  185. throw new LazerException('Tables names or keys missing');
  186. }
  187. }
  188. /**
  189. * Get relation information
  190. * @return array relation information
  191. */
  192. public function getRelation()
  193. {
  194. return array(
  195. 'tables' => $this->tables,
  196. 'keys' => $this->keys,
  197. 'type' => $this->relationType
  198. );
  199. }
  200. /**
  201. * Remove relation
  202. */
  203. public function removeRelation()
  204. {
  205. if ($this->relationType == 'hasAndBelongsToMany')
  206. {
  207. $junction = $this->getJunction();
  208. $this->deleteRelationData($junction, $this->tables['local']);
  209. $this->deleteRelationData($junction, $this->tables['foreign']);
  210. }
  211. $this->deleteRelationData($this->tables['local'], $this->tables['foreign']);
  212. }
  213. /**
  214. * Add data to configs and create all necessary files
  215. */
  216. protected function addRelation()
  217. {
  218. if ($this->relationType == 'hasAndBelongsToMany')
  219. {
  220. $junction = $this->getJunction();
  221. try
  222. {
  223. Validate::table($junction)->exists();
  224. }
  225. catch (LazerException $e)
  226. {
  227. Database::create($junction, array(
  228. $this->tables['local'] . '_id' => 'integer',
  229. $this->tables['foreign'] . '_id' => 'integer',
  230. ));
  231. $this->insertRelationData($junction, $this->tables['local'], 'hasMany', array(
  232. 'local' => $this->tables['local'] . '_id',
  233. 'foreign' => $this->keys['local']
  234. ));
  235. $this->insertRelationData($junction, $this->tables['foreign'], 'hasMany', array(
  236. 'local' => $this->tables['foreign'] . '_id',
  237. 'foreign' => $this->keys['foreign']
  238. ));
  239. }
  240. }
  241. $this->insertRelationData($this->tables['local'], $this->tables['foreign'], $this->relationType, $this->keys);
  242. }
  243. /**
  244. * Inserts relation data to config file
  245. * @param string $from Local table
  246. * @param string $to Related table
  247. * @param string $type Relation type
  248. * @param array $keys Relationed keys
  249. */
  250. protected function insertRelationData($from, $to, $type, array $keys)
  251. {
  252. $config = Config::table($from);
  253. $content = $config->get();
  254. $content->relations->{$to} = array(
  255. 'type' => $type,
  256. 'keys' => $keys,
  257. );
  258. $config->put($content);
  259. }
  260. /**
  261. * Inserts relation data to config file
  262. * @param string $from Local table
  263. * @param string $to Related table
  264. */
  265. protected function deleteRelationData($from, $to)
  266. {
  267. $config = Config::table($from);
  268. $content = $config->get();
  269. unset($content->relations->{$to});
  270. $config->put($content);
  271. }
  272. /**
  273. * Process query with joined data
  274. * @param object $row One row of data
  275. * @return Database
  276. */
  277. protected function join($row)
  278. {
  279. $keys['local'] = $this->keys['local'];
  280. $keys['foreign'] = $this->keys['foreign'];
  281. if ($this->relationType == 'hasAndBelongsToMany')
  282. {
  283. $join = Database::table($this->getJunction())
  284. ->groupBy($this->tables['local'] . '_id')
  285. ->where($this->tables['local'] . '_id', '=', $row->{$keys['local']})
  286. ->findAll()
  287. ->asArray(null, $this->tables['foreign'] . '_id');
  288. if (empty($join))
  289. return array();
  290. return Database::table($this->tables['foreign'])
  291. ->where($keys['foreign'], 'IN', $join[$row->{$keys['local']}]);
  292. }
  293. return Database::table($this->tables['foreign'])
  294. ->where($keys['foreign'], '=', $row->{$keys['local']});
  295. }
  296. /**
  297. *
  298. * @param array $array
  299. * @param string $part
  300. * @return array
  301. */
  302. public function build(array $array, $part)
  303. {
  304. $return = array();
  305. foreach ($array as $key => $row)
  306. {
  307. if (is_object($row))
  308. {
  309. if ($row instanceof \stdClass)
  310. {
  311. $part = ucfirst($part);
  312. if (!isset($row->{$part}))
  313. {
  314. $query = $this->join($row);
  315. if ($this->relationType == 'belongsTo')
  316. {
  317. $query = $query->findAll();
  318. $query = reset($query)[0];
  319. }
  320. $row->{$part} = $query;
  321. }
  322. $array[$key] = $row->{$part};
  323. $return[] = $row->{$part};
  324. }
  325. else
  326. {
  327. $row->with($part);
  328. }
  329. }
  330. else
  331. {
  332. $return = array_merge($return, $this->build($row, $part));
  333. }
  334. }
  335. return $return;
  336. }
  337. /**
  338. * Get relations types
  339. * @return array
  340. */
  341. public static function relations()
  342. {
  343. return self::$relations;
  344. }
  345. }