PageRenderTime 239ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php

https://gitlab.com/judielsm/Handora
PHP | 408 lines | 168 code | 63 blank | 177 comment | 12 complexity | 8ec07eebce22b38583879409cd5dbf81 MD5 | raw file
  1. <?php
  2. namespace Illuminate\Database\Eloquent\Relations;
  3. use Illuminate\Database\Eloquent\Model;
  4. use Illuminate\Database\Eloquent\Builder;
  5. use Illuminate\Database\Query\Expression;
  6. use Illuminate\Database\Eloquent\Collection;
  7. use Illuminate\Database\Eloquent\ModelNotFoundException;
  8. class HasManyThrough extends Relation
  9. {
  10. /**
  11. * The distance parent model instance.
  12. *
  13. * @var \Illuminate\Database\Eloquent\Model
  14. */
  15. protected $farParent;
  16. /**
  17. * The near key on the relationship.
  18. *
  19. * @var string
  20. */
  21. protected $firstKey;
  22. /**
  23. * The far key on the relationship.
  24. *
  25. * @var string
  26. */
  27. protected $secondKey;
  28. /**
  29. * The local key on the relationship.
  30. *
  31. * @var string
  32. */
  33. protected $localKey;
  34. /**
  35. * Create a new has many through relationship instance.
  36. *
  37. * @param \Illuminate\Database\Eloquent\Builder $query
  38. * @param \Illuminate\Database\Eloquent\Model $farParent
  39. * @param \Illuminate\Database\Eloquent\Model $parent
  40. * @param string $firstKey
  41. * @param string $secondKey
  42. * @param string $localKey
  43. * @return void
  44. */
  45. public function __construct(Builder $query, Model $farParent, Model $parent, $firstKey, $secondKey, $localKey)
  46. {
  47. $this->localKey = $localKey;
  48. $this->firstKey = $firstKey;
  49. $this->secondKey = $secondKey;
  50. $this->farParent = $farParent;
  51. parent::__construct($query, $parent);
  52. }
  53. /**
  54. * Set the base constraints on the relation query.
  55. *
  56. * @return void
  57. */
  58. public function addConstraints()
  59. {
  60. $parentTable = $this->parent->getTable();
  61. $localValue = $this->farParent[$this->localKey];
  62. $this->setJoin();
  63. if (static::$constraints) {
  64. $this->query->where($parentTable.'.'.$this->firstKey, '=', $localValue);
  65. }
  66. }
  67. /**
  68. * Add the constraints for a relationship count query.
  69. *
  70. * @param \Illuminate\Database\Eloquent\Builder $query
  71. * @param \Illuminate\Database\Eloquent\Builder $parent
  72. * @return \Illuminate\Database\Eloquent\Builder
  73. */
  74. public function getRelationCountQuery(Builder $query, Builder $parent)
  75. {
  76. $parentTable = $this->parent->getTable();
  77. $this->setJoin($query);
  78. $query->select(new Expression('count(*)'));
  79. $key = $this->wrap($parentTable.'.'.$this->firstKey);
  80. return $query->where($this->getHasCompareKey(), '=', new Expression($key));
  81. }
  82. /**
  83. * Set the join clause on the query.
  84. *
  85. * @param \Illuminate\Database\Eloquent\Builder|null $query
  86. * @return void
  87. */
  88. protected function setJoin(Builder $query = null)
  89. {
  90. $query = $query ?: $this->query;
  91. $foreignKey = $this->related->getTable().'.'.$this->secondKey;
  92. $query->join($this->parent->getTable(), $this->getQualifiedParentKeyName(), '=', $foreignKey);
  93. if ($this->parentSoftDeletes()) {
  94. $query->whereNull($this->parent->getQualifiedDeletedAtColumn());
  95. }
  96. }
  97. /**
  98. * Determine whether close parent of the relation uses Soft Deletes.
  99. *
  100. * @return bool
  101. */
  102. public function parentSoftDeletes()
  103. {
  104. return in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses_recursive(get_class($this->parent)));
  105. }
  106. /**
  107. * Set the constraints for an eager load of the relation.
  108. *
  109. * @param array $models
  110. * @return void
  111. */
  112. public function addEagerConstraints(array $models)
  113. {
  114. $table = $this->parent->getTable();
  115. $this->query->whereIn($table.'.'.$this->firstKey, $this->getKeys($models));
  116. }
  117. /**
  118. * Initialize the relation on a set of models.
  119. *
  120. * @param array $models
  121. * @param string $relation
  122. * @return array
  123. */
  124. public function initRelation(array $models, $relation)
  125. {
  126. foreach ($models as $model) {
  127. $model->setRelation($relation, $this->related->newCollection());
  128. }
  129. return $models;
  130. }
  131. /**
  132. * Match the eagerly loaded results to their parents.
  133. *
  134. * @param array $models
  135. * @param \Illuminate\Database\Eloquent\Collection $results
  136. * @param string $relation
  137. * @return array
  138. */
  139. public function match(array $models, Collection $results, $relation)
  140. {
  141. $dictionary = $this->buildDictionary($results);
  142. // Once we have the dictionary we can simply spin through the parent models to
  143. // link them up with their children using the keyed dictionary to make the
  144. // matching very convenient and easy work. Then we'll just return them.
  145. foreach ($models as $model) {
  146. $key = $model->getKey();
  147. if (isset($dictionary[$key])) {
  148. $value = $this->related->newCollection($dictionary[$key]);
  149. $model->setRelation($relation, $value);
  150. }
  151. }
  152. return $models;
  153. }
  154. /**
  155. * Build model dictionary keyed by the relation's foreign key.
  156. *
  157. * @param \Illuminate\Database\Eloquent\Collection $results
  158. * @return array
  159. */
  160. protected function buildDictionary(Collection $results)
  161. {
  162. $dictionary = [];
  163. $foreign = $this->firstKey;
  164. // First we will create a dictionary of models keyed by the foreign key of the
  165. // relationship as this will allow us to quickly access all of the related
  166. // models without having to do nested looping which will be quite slow.
  167. foreach ($results as $result) {
  168. $dictionary[$result->{$foreign}][] = $result;
  169. }
  170. return $dictionary;
  171. }
  172. /**
  173. * Get the results of the relationship.
  174. *
  175. * @return mixed
  176. */
  177. public function getResults()
  178. {
  179. return $this->get();
  180. }
  181. /**
  182. * Execute the query and get the first related model.
  183. *
  184. * @param array $columns
  185. * @return mixed
  186. */
  187. public function first($columns = ['*'])
  188. {
  189. $results = $this->take(1)->get($columns);
  190. return count($results) > 0 ? $results->first() : null;
  191. }
  192. /**
  193. * Execute the query and get the first result or throw an exception.
  194. *
  195. * @param array $columns
  196. * @return \Illuminate\Database\Eloquent\Model|static
  197. *
  198. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
  199. */
  200. public function firstOrFail($columns = ['*'])
  201. {
  202. if (! is_null($model = $this->first($columns))) {
  203. return $model;
  204. }
  205. throw new ModelNotFoundException;
  206. }
  207. /**
  208. * Find a related model by its primary key.
  209. *
  210. * @param mixed $id
  211. * @param array $columns
  212. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|null
  213. */
  214. public function find($id, $columns = ['*'])
  215. {
  216. if (is_array($id)) {
  217. return $this->findMany($id, $columns);
  218. }
  219. $this->where($this->getRelated()->getQualifiedKeyName(), '=', $id);
  220. return $this->first($columns);
  221. }
  222. /**
  223. * Find multiple related models by their primary keys.
  224. *
  225. * @param mixed $ids
  226. * @param array $columns
  227. * @return \Illuminate\Database\Eloquent\Collection
  228. */
  229. public function findMany($ids, $columns = ['*'])
  230. {
  231. if (empty($ids)) {
  232. return $this->getRelated()->newCollection();
  233. }
  234. $this->whereIn($this->getRelated()->getQualifiedKeyName(), $ids);
  235. return $this->get($columns);
  236. }
  237. /**
  238. * Find a related model by its primary key or throw an exception.
  239. *
  240. * @param mixed $id
  241. * @param array $columns
  242. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
  243. *
  244. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
  245. */
  246. public function findOrFail($id, $columns = ['*'])
  247. {
  248. $result = $this->find($id, $columns);
  249. if (is_array($id)) {
  250. if (count($result) == count(array_unique($id))) {
  251. return $result;
  252. }
  253. } elseif (! is_null($result)) {
  254. return $result;
  255. }
  256. throw (new ModelNotFoundException)->setModel(get_class($this->parent));
  257. }
  258. /**
  259. * Execute the query as a "select" statement.
  260. *
  261. * @param array $columns
  262. * @return \Illuminate\Database\Eloquent\Collection
  263. */
  264. public function get($columns = ['*'])
  265. {
  266. // First we'll add the proper select columns onto the query so it is run with
  267. // the proper columns. Then, we will get the results and hydrate out pivot
  268. // models with the result of those columns as a separate model relation.
  269. $columns = $this->query->getQuery()->columns ? [] : $columns;
  270. $select = $this->getSelectColumns($columns);
  271. $models = $this->query->addSelect($select)->getModels();
  272. // If we actually found models we will also eager load any relationships that
  273. // have been specified as needing to be eager loaded. This will solve the
  274. // n + 1 query problem for the developer and also increase performance.
  275. if (count($models) > 0) {
  276. $models = $this->query->eagerLoadRelations($models);
  277. }
  278. return $this->related->newCollection($models);
  279. }
  280. /**
  281. * Set the select clause for the relation query.
  282. *
  283. * @param array $columns
  284. * @return array
  285. */
  286. protected function getSelectColumns(array $columns = ['*'])
  287. {
  288. if ($columns == ['*']) {
  289. $columns = [$this->related->getTable().'.*'];
  290. }
  291. return array_merge($columns, [$this->parent->getTable().'.'.$this->firstKey]);
  292. }
  293. /**
  294. * Get a paginator for the "select" statement.
  295. *
  296. * @param int $perPage
  297. * @param array $columns
  298. * @param string $pageName
  299. * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
  300. */
  301. public function paginate($perPage = null, $columns = ['*'], $pageName = 'page')
  302. {
  303. $this->query->addSelect($this->getSelectColumns($columns));
  304. return $this->query->paginate($perPage, $columns, $pageName);
  305. }
  306. /**
  307. * Paginate the given query into a simple paginator.
  308. *
  309. * @param int $perPage
  310. * @param array $columns
  311. * @return \Illuminate\Contracts\Pagination\Paginator
  312. */
  313. public function simplePaginate($perPage = null, $columns = ['*'])
  314. {
  315. $this->query->addSelect($this->getSelectColumns($columns));
  316. return $this->query->simplePaginate($perPage, $columns);
  317. }
  318. /**
  319. * Get the key for comparing against the parent key in "has" query.
  320. *
  321. * @return string
  322. */
  323. public function getHasCompareKey()
  324. {
  325. return $this->farParent->getQualifiedKeyName();
  326. }
  327. /**
  328. * Get the qualified foreign key on the related model.
  329. *
  330. * @return string
  331. */
  332. public function getForeignKey()
  333. {
  334. return $this->related->getTable().'.'.$this->secondKey;
  335. }
  336. /**
  337. * Get the qualified foreign key on the "through" model.
  338. *
  339. * @return string
  340. */
  341. public function getThroughKey()
  342. {
  343. return $this->parent->getTable().'.'.$this->firstKey;
  344. }
  345. }