Skip to content

Commit 25badb8

Browse files
committed
Merge remote-tracking branch 'upstream/master' into prevent_edit_columns_when_columns_not_shown
2 parents 159a4ee + cb61786 commit 25badb8

File tree

5 files changed

+135
-9
lines changed

5 files changed

+135
-9
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
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+
1116
### [v10.4.3] - 2023-06-07
1217

1318
- Fix: Prevent the filteredCount() query if no filter is applied to the initial query #3007
@@ -130,7 +135,8 @@
130135
- Drop support for `ApiResourceDataTable`
131136
- PHP8 syntax / method signature changed
132137

133-
[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
134140
[v10.3.1]: https://github.com/yajra/laravel-datatables/compare/v10.3.1...v10.3.0
135141
[v10.3.1]: https://github.com/yajra/laravel-datatables/compare/v10.3.1...v10.3.0
136142
[v10.3.0]: https://github.com/yajra/laravel-datatables/compare/v10.3.0...v10.2.3

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: 33 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');
@@ -818,4 +836,16 @@ public function getFilteredQuery(): QueryBuilder
818836

819837
return $this->getQuery();
820838
}
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+
}
821851
}

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/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)