Skip to content

Commit 1095874

Browse files
committed
Merge remote-tracking branch 'upstream/master' into ignore_getters
2 parents fe97ca6 + cb61786 commit 1095874

File tree

6 files changed

+171
-11
lines changed

6 files changed

+171
-11
lines changed

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88

99
### [Unreleased]
1010

11+
### [v10.4.4] - 2023-06-27
12+
13+
- feat: Optimize countQuery with complex select #3008
14+
- fix: phpstan #3022
15+
16+
### [v10.4.3] - 2023-06-07
17+
18+
- Fix: Prevent the filteredCount() query if no filter is applied to the initial query #3007
19+
1120
### [v10.4.2] - 2023-05-31
1221

1322
- Fix return type for setTransformer() and setSerializer() #3003
@@ -126,7 +135,9 @@
126135
- Drop support for `ApiResourceDataTable`
127136
- PHP8 syntax / method signature changed
128137

129-
[Unreleased]: https://github.com/yajra/laravel-datatables/compare/v10.3.1...10.x
138+
[Unreleased]: https://github.com/yajra/laravel-datatables/compare/v10.4.4...10.x
139+
[v10.4.4]: https://github.com/yajra/laravel-datatables/compare/v10.4.4...v10.4.3
140+
[v10.3.1]: https://github.com/yajra/laravel-datatables/compare/v10.3.1...v10.3.0
130141
[v10.3.1]: https://github.com/yajra/laravel-datatables/compare/v10.3.1...v10.3.0
131142
[v10.3.0]: https://github.com/yajra/laravel-datatables/compare/v10.3.0...v10.2.3
132143
[v10.2.3]: https://github.com/yajra/laravel-datatables/compare/v10.2.3...v10.2.2

phpstan.neon.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ parameters:
1010

1111
ignoreErrors:
1212
- '#Unsafe usage of new static\(\).#'
13+
- '#Negated boolean expression is always false.#'
1314

1415
excludePaths:
1516
- src/helper.php

src/QueryDataTable.php

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Database\Query\Expression;
99
use Illuminate\Http\JsonResponse;
1010
use Illuminate\Support\Collection;
11+
use Illuminate\Support\Facades\DB;
1112
use Illuminate\Support\Str;
1213
use Yajra\DataTables\Utilities\Helper;
1314

@@ -48,6 +49,13 @@ class QueryDataTable extends DataTableAbstract
4849
*/
4950
protected bool $keepSelectBindings = false;
5051

52+
/**
53+
* Flag to ignore the selects in count query.
54+
*
55+
* @var bool
56+
*/
57+
protected bool $ignoreSelectInCountQuery = false;
58+
5159
/**
5260
* @param QueryBuilder $builder
5361
*/
@@ -156,10 +164,20 @@ public function prepareCountQuery(): QueryBuilder
156164
$builder = clone $this->query;
157165

158166
if ($this->isComplexQuery($builder)) {
167+
$builder->select(DB::raw('1'));
168+
if ($this->ignoreSelectInCountQuery || ! $this->isComplexQuery($builder)) {
169+
return $this->getConnection()
170+
->query()
171+
->fromRaw('('.$builder->toSql().') count_row_table')
172+
->setBindings($builder->getBindings());
173+
}
174+
175+
$builder = clone $this->query;
176+
159177
return $this->getConnection()
160-
->query()
161-
->fromRaw('('.$builder->toSql().') count_row_table')
162-
->setBindings($builder->getBindings());
178+
->query()
179+
->fromRaw('('.$builder->toSql().') count_row_table')
180+
->setBindings($builder->getBindings());
163181
}
164182

165183
$row_count = $this->wrap('row_count');
@@ -205,6 +223,35 @@ public function keepSelectBindings(): static
205223
return $this;
206224
}
207225

226+
/**
227+
* Perform column search.
228+
*
229+
* @return void
230+
*/
231+
protected function filterRecords(): void
232+
{
233+
$initialQuery = clone $this->query;
234+
235+
if ($this->autoFilter && $this->request->isSearchable()) {
236+
$this->filtering();
237+
}
238+
239+
if (is_callable($this->filterCallback)) {
240+
call_user_func($this->filterCallback, $this->resolveCallbackParameter());
241+
}
242+
243+
$this->columnSearch();
244+
$this->searchPanesSearch();
245+
246+
// If no modification between the original query and the filtered one has been made
247+
// the filteredRecords equals the totalRecords
248+
if ($this->query == $initialQuery) {
249+
$this->filteredRecords ??= $this->totalRecords;
250+
} else {
251+
$this->filteredCount();
252+
}
253+
}
254+
208255
/**
209256
* Perform column search.
210257
*
@@ -789,4 +836,16 @@ public function getFilteredQuery(): QueryBuilder
789836

790837
return $this->getQuery();
791838
}
839+
840+
/**
841+
* Ignore the selects in count query.
842+
*
843+
* @return $this
844+
*/
845+
public function ignoreSelectsInCountQuery(): static
846+
{
847+
$this->ignoreSelectInCountQuery = true;
848+
849+
return $this;
850+
}
792851
}

src/Utilities/Request.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@ public function getBaseRequest(): BaseRequest
254254
*/
255255
public function start(): int
256256
{
257-
return intval($this->request->input('start', 0));
257+
$start = $this->request->input('start', 0);
258+
259+
return is_numeric($start) ? intval($start) : 0;
258260
}
259261

260262
/**
@@ -264,7 +266,9 @@ public function start(): int
264266
*/
265267
public function length(): int
266268
{
267-
return intval($this->request->input('length', 10));
269+
$length = $this->request->input('length', 10);
270+
271+
return is_numeric($length) ? intval($length) : 10;
268272
}
269273

270274
/**
@@ -274,6 +278,8 @@ public function length(): int
274278
*/
275279
public function draw(): int
276280
{
277-
return intval($this->request->input('draw', 0));
281+
$draw = $this->request->input('draw', 0);
282+
283+
return is_numeric($draw) ? intval($draw) : 0;
278284
}
279285
}

tests/Integration/QueryDataTableTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function it_can_set_total_records()
2525
$crawler->assertJson([
2626
'draw' => 0,
2727
'recordsTotal' => 10,
28-
'recordsFiltered' => 20,
28+
'recordsFiltered' => 10,
2929
]);
3030
}
3131

@@ -36,7 +36,7 @@ public function it_can_set_zero_total_records()
3636
$crawler->assertJson([
3737
'draw' => 0,
3838
'recordsTotal' => 0,
39-
'recordsFiltered' => 20,
39+
'recordsFiltered' => 0,
4040
]);
4141
}
4242

tests/Unit/QueryDataTableTest.php

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,69 @@ public function test_complex_query_are_wrapped_and_countable()
2828
);
2929

3030
$this->assertQueryWrapped(true, $dataTable->prepareCountQuery());
31+
$this->assertQueryHasNoSelect(false, $dataTable->prepareCountQuery());
3132
$this->assertEquals(60, $dataTable->count());
3233
}
3334

35+
public function test_complex_query_use_select_in_count()
36+
{
37+
/** @var \Yajra\DataTables\QueryDataTable $dataTable */
38+
$dataTable = app('datatables')->of(
39+
DB::table('users')
40+
->select('users.*')
41+
->addSelect([
42+
'last_post_id' => DB::table('posts')
43+
->whereColumn('posts.user_id', 'users.id')
44+
->orderBy('created_at')
45+
->select('id'),
46+
])
47+
->orderBy(DB::table('posts')->whereColumn('posts.user_id', 'users.id')->orderBy('created_at')->select('created_at')
48+
)
49+
);
50+
51+
$this->assertQueryHasNoSelect(false, $dataTable->prepareCountQuery());
52+
$this->assertEquals(20, $dataTable->count());
53+
}
54+
55+
public function test_complex_query_can_ignore_select_in_count()
56+
{
57+
/** @var \Yajra\DataTables\QueryDataTable $dataTable */
58+
$dataTable = app('datatables')->of(
59+
DB::table('users')
60+
->select('users.*')
61+
->addSelect([
62+
'last_post_id' => DB::table('posts')
63+
->whereColumn('posts.user_id', 'users.id')
64+
->orderBy('created_at')
65+
->select('id'),
66+
])
67+
->orderBy(DB::table('posts')->whereColumn('posts.user_id', 'users.id')->orderBy('created_at')->select('created_at')
68+
)
69+
)->ignoreSelectsInCountQuery();
70+
71+
$this->assertQueryHasNoSelect(true, $dataTable->prepareCountQuery());
72+
$this->assertEquals(20, $dataTable->count());
73+
}
74+
75+
public function test_simple_queries_with_complexe_select_are_wrapped_without_selects()
76+
{
77+
/** @var \Yajra\DataTables\QueryDataTable $dataTable */
78+
$dataTable = app('datatables')->of(
79+
DB::table('users')
80+
->select('users.*')
81+
->addSelect([
82+
'last_post_id' => DB::table('posts')
83+
->whereColumn('posts.user_id', 'users.id')
84+
->orderBy('created_at')
85+
->select('id'),
86+
])
87+
);
88+
89+
$this->assertQueryWrapped(true, $dataTable->prepareCountQuery());
90+
$this->assertQueryHasNoSelect(true, $dataTable->prepareCountQuery());
91+
$this->assertEquals(20, $dataTable->count());
92+
}
93+
3494
public function test_simple_queries_are_not_wrapped_and_countable()
3595
{
3696
/** @var \Yajra\DataTables\QueryDataTable $dataTable */
@@ -42,9 +102,20 @@ public function test_simple_queries_are_not_wrapped_and_countable()
42102
$this->assertEquals(20, $dataTable->count());
43103
}
44104

105+
public function test_complexe_queries_can_be_wrapped_and_countable()
106+
{
107+
/** @var \Yajra\DataTables\QueryDataTable $dataTable */
108+
$dataTable = app('datatables')->of(
109+
User::with('posts')->select('users.*')
110+
);
111+
112+
$this->assertQueryWrapped(false, $dataTable->prepareCountQuery());
113+
$this->assertEquals(20, $dataTable->count());
114+
}
115+
45116
/**
46-
* @param $expected bool
47-
* @param $query \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
117+
* @param $expected bool
118+
* @param $query \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
48119
* @return void
49120
*/
50121
protected function assertQueryWrapped($expected, $query)
@@ -53,4 +124,16 @@ protected function assertQueryWrapped($expected, $query)
53124

54125
$this->assertSame($expected, Str::endsWith($sql, 'count_row_table'), "'{$sql}' is not wrapped");
55126
}
127+
128+
/**
129+
* @param $expected bool
130+
* @param $query \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder
131+
* @return void
132+
*/
133+
public function assertQueryHasNoSelect($expected, $query)
134+
{
135+
$sql = $query->toSql();
136+
137+
$this->assertSame($expected, Str::startsWith($sql, 'select * from (select 1 from'), "'{$sql}' is not wrapped");
138+
}
56139
}

0 commit comments

Comments
 (0)