Skip to content

Commit 19c58cf

Browse files
committed
Implement withRaw, withSum, withAvg, withMin, withMax
See laravel/framework#16815
1 parent c17dcdd commit 19c58cf

File tree

3 files changed

+137
-2
lines changed

3 files changed

+137
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Change Log
22

3+
## v4.1.14-alpha
4+
- Implement withRaw, withSum, withAvg, withMin, withMax (see https://github.com/laravel/framework/pull/16815)
5+
36
## v4.1.13-alpha
47
- `ufTable`: Implement `rowTemplate` for customizing how rows are rendered (#787)
58
- `ufTable`: Support for passing callbacks for column templates instead of Handlebars templates

app/defines.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace UserFrosting;
44

55
// Some standard defines
6-
define('UserFrosting\VERSION', '4.1.13-alpha');
6+
define('UserFrosting\VERSION', '4.1.14-alpha');
77
define('UserFrosting\DS', '/');
88
define('UserFrosting\PHP_MIN_VERSION', '5.6');
99
define('UserFrosting\DEBUG_CONFIG', false);

app/sprinkles/core/src/Database/Builder.php

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99

1010
use Illuminate\Database\Capsule\Manager as DB;
1111
use Illuminate\Database\Query\Builder as LaravelBuilder;
12+
use Illuminate\Database\Query\Expression;
1213

1314
/**
14-
* UFBuilder Class
15+
* UserFrosting's custom Builder Class
1516
*
1617
* The base Eloquent data model, from which all UserFrosting data classes extend.
1718
* @author Alex Weissman (https://alexanderweissman.com)
@@ -81,6 +82,137 @@ public function orLike($field, $value)
8182
return $this->orWhere($field, 'LIKE', "%$value%");
8283
}
8384

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+
84216
/**
85217
* Execute the query as a "select" statement.
86218
*

0 commit comments

Comments
 (0)