|
9 | 9 |
|
10 | 10 | use Illuminate\Database\Capsule\Manager as DB; |
11 | 11 | use Illuminate\Database\Query\Builder as LaravelBuilder; |
| 12 | +use Illuminate\Database\Query\Expression; |
12 | 13 |
|
13 | 14 | /** |
14 | | - * UFBuilder Class |
| 15 | + * UserFrosting's custom Builder Class |
15 | 16 | * |
16 | 17 | * The base Eloquent data model, from which all UserFrosting data classes extend. |
17 | 18 | * @author Alex Weissman (https://alexanderweissman.com) |
@@ -81,6 +82,137 @@ public function orLike($field, $value) |
81 | 82 | return $this->orWhere($field, 'LIKE', "%$value%"); |
82 | 83 | } |
83 | 84 |
|
| 85 | + /** |
| 86 | + * Add subselect queries to average a column value of a relation. |
| 87 | + * |
| 88 | + * @param mixed $relation |
| 89 | + * @param string $column |
| 90 | + * @return $this |
| 91 | + */ |
| 92 | + public function withAvg($relation, $column) |
| 93 | + { |
| 94 | + return $this->withAggregate('avg', $relation, $column); |
| 95 | + } |
| 96 | + |
| 97 | + /** |
| 98 | + * Add subselect queries to count the relations. |
| 99 | + * |
| 100 | + * @param mixed $relations |
| 101 | + * @return $this |
| 102 | + */ |
| 103 | + public function withCount($relations) |
| 104 | + { |
| 105 | + $relations = is_array($relations) ? $relations : func_get_args(); |
| 106 | + |
| 107 | + return $this->withRaw($relations, new Expression('count(*)'), 'count'); |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + * Add subselect queries to get max column value of a relation. |
| 112 | + * |
| 113 | + * @param mixed $relation |
| 114 | + * @param string $column |
| 115 | + * @return $this |
| 116 | + */ |
| 117 | + public function withMax($relation, $column) |
| 118 | + { |
| 119 | + return $this->withAggregate('max', $relation, $column); |
| 120 | + } |
| 121 | + |
| 122 | + /** |
| 123 | + * Add subselect queries to get min column value of a relation. |
| 124 | + * |
| 125 | + * @param mixed $relation |
| 126 | + * @param string $column |
| 127 | + * @return $this |
| 128 | + */ |
| 129 | + public function withMin($relation, $column) |
| 130 | + { |
| 131 | + return $this->withAggregate('min', $relation, $column); |
| 132 | + } |
| 133 | + |
| 134 | + /** |
| 135 | + * Add subselect queries to sum a column value of a relation. |
| 136 | + * |
| 137 | + * @param mixed $relation |
| 138 | + * @param string $column |
| 139 | + * @return $this |
| 140 | + */ |
| 141 | + public function withSum($relation, $column) |
| 142 | + { |
| 143 | + return $this->withAggregate('sum', $relation, $column); |
| 144 | + } |
| 145 | + |
| 146 | + /** |
| 147 | + * Add subselect queries to aggregate a column value of a relation. |
| 148 | + * |
| 149 | + * @param string $aggregate |
| 150 | + * @param mixed $relation |
| 151 | + * @param string $column |
| 152 | + * @return $this |
| 153 | + */ |
| 154 | + protected function withAggregate($aggregate, $relation, $column) |
| 155 | + { |
| 156 | + return $this->withRaw($relation, new Expression($aggregate.'('.$this->query->getGrammar()->wrap($column).')'), $aggregate); |
| 157 | + } |
| 158 | + |
| 159 | + /** |
| 160 | + * Add subselect queries to aggregate all rows or a column value of a relation. |
| 161 | + * |
| 162 | + * @param array|mixed $relations |
| 163 | + * @param mixed $expression |
| 164 | + * @param string $suffix |
| 165 | + * @return $this |
| 166 | + */ |
| 167 | + public function withRaw($relations, $expression, $suffix = null) |
| 168 | + { |
| 169 | + if (is_null($this->query->columns)) { |
| 170 | + $this->query->select(['*']); |
| 171 | + } |
| 172 | + |
| 173 | + $relations = is_array($relations) ? $relations : [$relations]; |
| 174 | + |
| 175 | + foreach ($this->parseWithRelations($relations) as $name => $constraints) { |
| 176 | + // First we will determine if the name has been aliased using an "as" clause on the name |
| 177 | + // and if it has we will extract the actual relationship name and the desired name of |
| 178 | + // the resulting column. This allows multiple counts on the same relationship name. |
| 179 | + $segments = explode(' ', $name); |
| 180 | + |
| 181 | + if (count($segments) == 3 && Str::lower($segments[1]) == 'as') { |
| 182 | + list($name, $alias) = [$segments[0], $segments[2]]; |
| 183 | + } |
| 184 | + |
| 185 | + $relation = $this->getHasRelationQuery($name); |
| 186 | + |
| 187 | + // Here we will get the relationship query and prepare to add it to the main query |
| 188 | + // as a sub-select. First, we'll get the "has" query and use that to get the raw relation |
| 189 | + // query. |
| 190 | + $query = $relation->getRelationQuery( |
| 191 | + $relation->getRelated()->newQuery(), $this, $expression |
| 192 | + ); |
| 193 | + |
| 194 | + $query->callScope($constraints); |
| 195 | + |
| 196 | + $query->mergeModelDefinedRelationConstraints($relation->getQuery()); |
| 197 | + |
| 198 | + // Finally we will add the proper result column alias to the query and run the subselect |
| 199 | + // statement against the query builder. If the alias has not been set, we will normalize |
| 200 | + // the relation name then append _$suffix as the name. Then we will return the builder |
| 201 | + // instance back to the developer for further constraint chaining that needs to take |
| 202 | + // place on it. |
| 203 | + if (isset($alias)) { |
| 204 | + $column = snake_case($alias); |
| 205 | + unset($alias); |
| 206 | + } else { |
| 207 | + $column = snake_case($name.'_'.(is_null($suffix) ? 'aggregate' : $suffix)); |
| 208 | + } |
| 209 | + |
| 210 | + $this->selectSub($query->toBase(), $column); |
| 211 | + } |
| 212 | + |
| 213 | + return $this; |
| 214 | + } |
| 215 | + |
84 | 216 | /** |
85 | 217 | * Execute the query as a "select" statement. |
86 | 218 | * |
|
0 commit comments