Skip to content

Commit 0d00a26

Browse files
authored
ECS-914 (#10)
1 parent 6bb4061 commit 0d00a26

File tree

9 files changed

+334
-6
lines changed

9 files changed

+334
-6
lines changed

src/Filters/DateTimeFilter.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Ensi\QueryBuilderHelpers\Filters;
4+
5+
class DateTimeFilter extends Filter
6+
{
7+
public function exact(): static
8+
{
9+
return $this->addFilter(ExtraFilter::dateTimeExact(...$this->makeFilterParams(self::EQUAL)));
10+
}
11+
12+
public function empty(): static
13+
{
14+
return $this->addFilter(ExtraFilter::empty(...$this->makeFilterParams(self::EMPTY)));
15+
}
16+
17+
public function not(): static
18+
{
19+
return $this->addFilter(ExtraFilter::dateTimeNot(...$this->makeFilterParams(self::NOT)));
20+
}
21+
22+
public function gt(): static
23+
{
24+
return $this->addFilter(ExtraFilter::dateTimeGreater(...$this->makeFilterParams(self::GREATER)));
25+
}
26+
27+
public function gte(): static
28+
{
29+
return $this->addFilter(ExtraFilter::dateTimeGreaterOrEqual(...$this->makeFilterParams(self::GREATER_OR_EQUAL)));
30+
}
31+
32+
public function lt(): static
33+
{
34+
return $this->addFilter(ExtraFilter::dateTimeLess(...$this->makeFilterParams(self::LESS)));
35+
}
36+
37+
public function lte(): static
38+
{
39+
return $this->addFilter(ExtraFilter::dateTimeLessOrEqual(...$this->makeFilterParams(self::LESS_OR_EQUAL)));
40+
}
41+
}

src/Filters/ExtraFilter.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersDateExact;
1111
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersDateNot;
1212
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersDateRange;
13+
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersDateTimeExact;
14+
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersDateTimeNot;
15+
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersDateTimeRange;
1316
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersEmpty;
1417
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersHas;
1518
use Ensi\QueryBuilderHelpers\QueryFilters\FiltersLike;
@@ -102,6 +105,36 @@ public static function dateLessOrEqual(string $name, ?string $internalName = nul
102105
return new AllowedFilter($name, new FiltersDateRange(RangeOperator::LESS_OR_EQUAL), $internalName);
103106
}
104107

108+
public static function dateTimeExact(string $name, ?string $internalName = null): AllowedFilter
109+
{
110+
return new AllowedFilter($name, new FiltersDateTimeExact(), $internalName);
111+
}
112+
113+
public static function dateTimeNot(string $name, ?string $internalName = null): AllowedFilter
114+
{
115+
return new AllowedFilter($name, new FiltersDateTimeNot(), $internalName);
116+
}
117+
118+
public static function dateTimeGreater(string $name, ?string $internalName = null): AllowedFilter
119+
{
120+
return new AllowedFilter($name, new FiltersDateTimeRange(RangeOperator::GREATER), $internalName);
121+
}
122+
123+
public static function dateTimeGreaterOrEqual(string $name, ?string $internalName = null): AllowedFilter
124+
{
125+
return new AllowedFilter($name, new FiltersDateTimeRange(RangeOperator::GREATER_OR_EQUAL), $internalName);
126+
}
127+
128+
public static function dateTimeLess(string $name, ?string $internalName = null): AllowedFilter
129+
{
130+
return new AllowedFilter($name, new FiltersDateTimeRange(RangeOperator::LESS), $internalName);
131+
}
132+
133+
public static function dateTimeLessOrEqual(string $name, ?string $internalName = null): AllowedFilter
134+
{
135+
return new AllowedFilter($name, new FiltersDateTimeRange(RangeOperator::LESS_OR_EQUAL), $internalName);
136+
}
137+
105138
public static function arrayExact(string $name, ?string $internalName = null): AllowedFilter
106139
{
107140
return new AllowedFilter($name, new FiltersArrayExact(), $internalName);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Ensi\QueryBuilderHelpers\QueryFilters;
4+
5+
use Carbon\CarbonImmutable;
6+
use Ensi\QueryBuilderHelpers\Utils\Date;
7+
use Illuminate\Database\Eloquent\Builder;
8+
9+
class FiltersDateTimeExact extends FiltersBase
10+
{
11+
protected function applyOne(Builder $query, mixed $value, string $column): void
12+
{
13+
$query->whereBetween($column, $this->range($value));
14+
}
15+
16+
protected function applyMulti(Builder $query, array $values, string $column): void
17+
{
18+
$query->where(function (Builder $query) use ($values, $column) {
19+
foreach ($values as $value) {
20+
$query->orWhereBetween($column, $this->range($value));
21+
}
22+
});
23+
}
24+
25+
protected function castValue(mixed $source): CarbonImmutable
26+
{
27+
return Date::makeImmutable($source);
28+
}
29+
30+
private function range(CarbonImmutable $value): array
31+
{
32+
return [$value->startOfSecond(), $value->endOfSecond()];
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Ensi\QueryBuilderHelpers\QueryFilters;
4+
5+
use Carbon\CarbonImmutable;
6+
use Ensi\QueryBuilderHelpers\Utils\Date;
7+
use Illuminate\Database\Eloquent\Builder;
8+
9+
class FiltersDateTimeNot extends FiltersBase
10+
{
11+
protected function applyOne(Builder $query, mixed $value, string $column): void
12+
{
13+
$this->whereNotWithinDate($query, $value, $column);
14+
}
15+
16+
protected function applyMulti(Builder $query, array $values, string $column): void
17+
{
18+
foreach ($values as $value) {
19+
$this->whereNotWithinDate($query, $value, $column);
20+
}
21+
}
22+
23+
protected function castValue(mixed $source): CarbonImmutable
24+
{
25+
return Date::makeImmutable($source);
26+
}
27+
28+
protected function whereNotWithinDate(Builder $query, CarbonImmutable $value, string $column): void
29+
{
30+
$query->whereNot(function (Builder $query) use ($value, $column) {
31+
$query->where($column, $value)->whereNotNull($column);
32+
});
33+
}
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Ensi\QueryBuilderHelpers\QueryFilters;
4+
5+
use Carbon\CarbonImmutable;
6+
use Ensi\QueryBuilderHelpers\Enums\RangeOperator;
7+
use Ensi\QueryBuilderHelpers\Utils\Date;
8+
use Spatie\QueryBuilder\Exceptions\InvalidFilterValue;
9+
10+
class FiltersDateTimeRange extends FiltersRange
11+
{
12+
protected function castValue(mixed $source): CarbonImmutable
13+
{
14+
/** @var CarbonImmutable $value */
15+
$value = Date::makeImmutable($source) ?? throw InvalidFilterValue::make($source);
16+
17+
// > (дата исключается) - сравнение с концом секунды
18+
// >= (дата включается) - сравнение с началом секунды
19+
// < (дата исключается) - сравнение с началом секунды
20+
// <= (дата включается) - сравнение с концом секунды
21+
return $this->operator === RangeOperator::GREATER || $this->operator === RangeOperator::LESS_OR_EQUAL
22+
? $value->endOfSecond()
23+
: $value->startOfSecond();
24+
}
25+
}

tests/Component/FiltersDateNotTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use function Pest\Laravel\postJson;
77

8-
test('filter exclude date success', function (string|int|array $value, int $count, bool $timestampMs = true) {
8+
test('filter exclude (datetime as date) success', function (string|int|array $value, int $count, bool $timestampMs = true) {
99
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
1010

1111
ParentModel::factory()->createMany([
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Ensi\QueryBuilderHelpers\Filters\ExtraFilter;
4+
use Ensi\QueryBuilderHelpers\Tests\Models\ParentModel;
5+
6+
use function Pest\Laravel\postJson;
7+
8+
test('filter exclude (datetime as datetime) success', function (string|int|array $value, int $count, bool $timestampMs = true) {
9+
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
10+
11+
ParentModel::factory()->createMany([
12+
['datetime_value' => '2022-03-17 15:00:01'],
13+
['datetime_value' => '2022-03-17 15:00:02'],
14+
['datetime_value' => '2022-03-17 15:00:03'],
15+
['datetime_value' => null],
16+
]);
17+
18+
attachQueryBuilder('test', ParentModel::class, [
19+
ExtraFilter::dateTimeNot('datetime__not', 'datetime_value'),
20+
]);
21+
22+
postJson('/test', ['filter' => ['datetime__not' => $value]])
23+
->assertOk()
24+
->assertJsonCount($count, 'data');
25+
})->with([
26+
'datetime' => ['2022-03-17 15:00:01', 3],
27+
'multiple datetimes' => [['2022-03-17 15:00:01', '2022-03-17 15:00:02'], 2],
28+
'timestamp' => [1647529201, 3, false],
29+
'timestampMs' => [1647529201000, 3],
30+
'multiple timestamps' => [[1647529201, 1647529202], 2, false],
31+
'multiple timestampsMs' => [[1647529201000, 1647529202000], 2],
32+
]);

tests/Component/FiltersExactTest.php

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use function Pest\Laravel\postJson;
99

10-
test('filter datetime single value success', function ($value, bool $timestampMs = true) {
10+
test('filter (datetime as date) single value success', function ($value, bool $timestampMs = true) {
1111
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
1212

1313
$expected = ParentModel::factory()->createOne(['datetime_value' => '2022-03-19 12:45:14']);
@@ -34,7 +34,29 @@
3434
'timestampMs' => [1647704715000],
3535
]);
3636

37-
test('filter datetime multi value one result success', function ($value, bool $timestampMs = true) {
37+
test('filter (datetime as datetime) single value success', function ($value, bool $timestampMs = true) {
38+
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
39+
40+
$expected = ParentModel::factory()->createOne(['datetime_value' => '2022-03-17 15:00:02']);
41+
ParentModel::factory()->createMany([
42+
['datetime_value' => '2022-03-17 15:00:01'],
43+
['datetime_value' => '2022-03-17 15:00:03'],
44+
['datetime_value' => null],
45+
]);
46+
47+
attachQueryBuilder('test', ParentModel::class, [ExtraFilter::dateTimeExact('datetime_value')]);
48+
49+
postJson('/test', ['filter' => ['datetime_value' => $value]])
50+
->assertOk()
51+
->assertJsonCount(1, 'data')
52+
->assertJsonPath('data.0.id', $expected->id);
53+
})->with([
54+
'datetime' => ['2022-03-17 15:00:02'],
55+
'timestamp' => [1647529202, false],
56+
'timestampMs' => [1647529202000],
57+
]);
58+
59+
test('filter (datetime as date) multi value one result success', function ($value, bool $timestampMs = true) {
3860
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
3961

4062
$expected = ParentModel::factory()->createOne(['datetime_value' => '2022-03-18 12:45:14']);
@@ -56,7 +78,29 @@
5678
'timestampMs' => [[1647561600000, 1647388800000, 1647734400000]],
5779
]);
5880

59-
test('filter datetime multi value multi result success', function ($value, bool $timestampMs = true) {
81+
test('filter (datetime as datetime) multi value one result success', function ($value, bool $timestampMs = true) {
82+
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
83+
84+
$expected = ParentModel::factory()->createOne(['datetime_value' => '2022-03-17 15:00:02']);
85+
ParentModel::factory()->createMany([
86+
['datetime_value' => '2022-03-17 15:00:01'],
87+
['datetime_value' => '2022-03-17 15:00:03'],
88+
['datetime_value' => null],
89+
]);
90+
91+
attachQueryBuilder('test', ParentModel::class, [ExtraFilter::dateTimeExact('datetime_value')]);
92+
93+
postJson('/test', ['filter' => ['datetime_value' => $value]])
94+
->assertOk()
95+
->assertJsonCount(1, 'data')
96+
->assertJsonPath('data.0.id', $expected->id);
97+
})->with([
98+
'date' => [['2022-03-17 15:00:02', '2022-03-17 15:00:04']],
99+
'timestamp' => [[1647529202, 1647529204], false],
100+
'timestampMs' => [[1647529202000, 1647529204000]],
101+
]);
102+
103+
test('filter (datetime as date) multi value multi result success', function ($value, bool $timestampMs = true) {
60104
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
61105

62106
ParentModel::factory()->createMany([
@@ -79,6 +123,31 @@
79123
'timestampMs' => [[1647475200000, 1647561600000, 1647734400000]],
80124
]);
81125

126+
test('filter (datetime as datetime) multi value multi result success', function ($value, bool $timestampMs = true) {
127+
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
128+
129+
ParentModel::factory()->createMany([
130+
['datetime_value' => '2022-03-17 15:00:01'],
131+
['datetime_value' => '2022-03-17 15:00:02'],
132+
['datetime_value' => '2022-03-17 15:00:03'],
133+
['datetime_value' => '2022-03-17 15:00:04'],
134+
]);
135+
136+
attachQueryBuilder('test', ParentModel::class, [ExtraFilter::dateTimeExact('datetime_value')]);
137+
138+
postJson('/test', ['filter' => ['datetime_value' => $value]])
139+
->assertOk()
140+
->assertJsonCount(2, 'data')
141+
->assertJsonFragment(['datetime_value' => '2022-03-17T15:00:01.000000Z'])
142+
->assertJsonFragment(['datetime_value' => '2022-03-17T15:00:04.000000Z'])
143+
->assertJsonMissing(['datetime_value' => '2022-03-17T15:00:02.000000Z'])
144+
->assertJsonMissing(['datetime_value' => '2022-03-17T15:00:03.000000Z']);
145+
})->with([
146+
'date' => [['2022-03-17 15:00:01', '2022-03-17 15:00:04']],
147+
'timestamp' => [[1647529201, 1647529204], false],
148+
'timestampMs' => [[1647529201000, 1647529204000]],
149+
]);
150+
82151
test('filter date value success', function ($value, bool $timestampMs = true) {
83152
config()->set('query-builder-helpers.timestamp_ms', $timestampMs);
84153

0 commit comments

Comments
 (0)