PageRenderTime 26ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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

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