Skip to content

Commit e0d2ac7

Browse files
committed
Merge branch 'release/2.2.0'
2 parents f64ff8c + 45792ad commit e0d2ac7

File tree

8 files changed

+384
-8
lines changed

8 files changed

+384
-8
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ jobs:
1414
strategy:
1515
fail-fast: true
1616
matrix:
17-
php: [7.4, '8.0', 8.1]
17+
php: [7.4, '8.0', 8.1, 8.2]
1818
laravel: [8, 9]
1919
exclude:
2020
- php: 7.4
2121
laravel: 9
2222

2323
steps:
2424
- name: Checkout Code
25-
uses: actions/checkout@v2
25+
uses: actions/checkout@v3
2626

2727
- name: Setup PHP
2828
uses: shivammathur/setup-php@v2
@@ -37,7 +37,7 @@ jobs:
3737
run: composer require "illuminate/database:^${{ matrix.laravel }}" --no-update
3838

3939
- name: Install dependencies
40-
uses: nick-invision/retry@v1
40+
uses: nick-fields/retry@v2
4141
with:
4242
timeout_minutes: 5
4343
max_attempts: 5

CHANGELOG.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
All notable changes to this project will be documented in this file. This project adheres to
44
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).
55

6+
## [2.2.0] - 2022-12-22
7+
8+
### Added
9+
10+
- [#27](https://github.com/laravel-json-api/eloquent/pull/27) Added the `WhereNull` and `WhereNotNull` filters.
11+
612
## [2.1.1] - 2022-04-04
713

814
### Fixed
@@ -16,10 +22,10 @@ All notable changes to this project will be documented in this file. This projec
1622

1723
- The `Number` field can now be configured to accept numeric strings by calling the `acceptStrings()` method on the
1824
field.
19-
25+
2026
### Fixed
2127

22-
- The `JsonApiBuilder` class was previously converting a `null` decoded id to an empty string when querting for a
28+
- The `JsonApiBuilder` class was previously converting a `null` decoded id to an empty string when querting for a
2329
resource id. This has been fixed to pass `null` to the query builder instead of the empty string, as this was most
2430
likely the cause of failures in Postgres.
2531

@@ -36,8 +42,8 @@ All notable changes to this project will be documented in this file. This projec
3642
- [#20](https://github.com/laravel-json-api/eloquent/pull/20) **BREAKING** To support PHP 8.1 we needed to rename the
3743
`ReadOnly` contract and trait. This is because PHP 8.1 introduced `readonly` as a reserved word. The following changes
3844
were made:
39-
- `LaravelJsonApi\Eloquent\Contracts\ReadOnly` is now `IsReadOnly`.
40-
- `LaravelJsonApi\Eloquent\Fields\Concerns\ReadOnly` is now `IsReadOnly`.
45+
- `LaravelJsonApi\Eloquent\Contracts\ReadOnly` is now `IsReadOnly`.
46+
- `LaravelJsonApi\Eloquent\Fields\Concerns\ReadOnly` is now `IsReadOnly`.
4147

4248
## [1.0.1] - 2021-12-08
4349

src/Filters/WhereNotNull.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Eloquent\Filters;
21+
22+
class WhereNotNull extends WhereNull
23+
{
24+
/**
25+
* @param bool $value
26+
* @return bool
27+
*/
28+
protected function isWhereNull(bool $value): bool
29+
{
30+
return $value === false;
31+
}
32+
}

src/Filters/WhereNull.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Eloquent\Filters;
21+
22+
use LaravelJsonApi\Core\Support\Str;
23+
use LaravelJsonApi\Eloquent\Contracts\Filter;
24+
25+
class WhereNull implements Filter
26+
{
27+
use Concerns\HasColumn;
28+
use Concerns\IsSingular;
29+
30+
/**
31+
* @var string
32+
*/
33+
private string $name;
34+
35+
/**
36+
* Create a new filter.
37+
*
38+
* @param string $name
39+
* @param string|null $column
40+
*
41+
* @return static
42+
*/
43+
public static function make(string $name, string $column = null): self
44+
{
45+
return new static($name, $column);
46+
}
47+
48+
/**
49+
* WhereNull constructor.
50+
*
51+
* @param string|null $column
52+
*/
53+
public function __construct(string $name, string $column = null)
54+
{
55+
$this->name = $name;
56+
$this->column = $column ?: $this->guessColumn();
57+
}
58+
59+
/**
60+
* @return string
61+
*/
62+
public function key(): string
63+
{
64+
return $this->name;
65+
}
66+
67+
/**
68+
* @inheritDoc
69+
*/
70+
public function apply($query, $value)
71+
{
72+
$value = $this->deserialize($value);
73+
$column = $query->getModel()->qualifyColumn($this->column());
74+
75+
if ($this->isWhereNull($value)) {
76+
return $query->whereNull($column);
77+
}
78+
79+
return $query->whereNotNull($column);
80+
}
81+
82+
/**
83+
* Should a "where null" query be used?
84+
*
85+
* @param bool $value
86+
* @return bool
87+
*/
88+
protected function isWhereNull(bool $value): bool
89+
{
90+
return $value === true;
91+
}
92+
93+
/**
94+
* Deserialize the value.
95+
*
96+
* @param mixed $value
97+
* @return bool
98+
*/
99+
private function deserialize($value): bool
100+
{
101+
return filter_var($value, FILTER_VALIDATE_BOOL);
102+
}
103+
104+
/**
105+
* @return string
106+
*/
107+
private function guessColumn(): string
108+
{
109+
return Str::underscore($this->name);
110+
}
111+
}

tests/app/Schemas/PostSchema.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
use LaravelJsonApi\Eloquent\Filters\WhereHas;
3737
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
3838
use LaravelJsonApi\Eloquent\Filters\WhereIn;
39+
use LaravelJsonApi\Eloquent\Filters\WhereNotNull;
40+
use LaravelJsonApi\Eloquent\Filters\WhereNull;
3941
use LaravelJsonApi\Eloquent\Filters\WithTrashed;
4042
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
4143
use LaravelJsonApi\Eloquent\Schema;
@@ -44,7 +46,6 @@
4446

4547
class PostSchema extends Schema
4648
{
47-
4849
use SoftDeletes;
4950

5051
/**
@@ -76,6 +77,7 @@ public function fields(): iterable
7677
BelongsToMany::make('images'),
7778
BelongsToMany::make('videos'),
7879
])->canCount(),
80+
DateTime::make('publishedAt'),
7981
Str::make('slug')->sortable(),
8082
BelongsToMany::make('tags')
8183
->fields(new ApprovedPivot())
@@ -92,7 +94,9 @@ public function filters(): iterable
9294
{
9395
return [
9496
WhereIdIn::make($this),
97+
WhereNull::make('draft', 'published_at'),
9598
WhereDoesntHave::make($this, 'tags', 'notTags'),
99+
WhereNotNull::make('published', 'published_at'),
96100
WhereHas::make($this, 'tags'),
97101
OnlyTrashed::make('trashed'),
98102
Where::make('slug')->singular(),
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Eloquent\Tests\Acceptance\Filters;
21+
22+
use App\Models\Post;
23+
use Illuminate\Database\Eloquent\Model;
24+
use Illuminate\Support\Collection;
25+
use LaravelJsonApi\Eloquent\Tests\Acceptance\TestCase as BaseTestCase;
26+
27+
class TestCase extends BaseTestCase
28+
{
29+
/**
30+
* Assert the two iterables contain the same filtered models.
31+
*
32+
* As we are just asserting filtering, we do not care what order the models are in -
33+
* just that the two iterables contain the same models by id.
34+
*
35+
* @param iterable $expected
36+
* @param iterable $actual
37+
* @return void
38+
*/
39+
protected function assertFilteredModels(iterable $expected, iterable $actual): void
40+
{
41+
$expected = Collection::make($expected)
42+
->map(static fn(Model $model) => $model->getKey())
43+
->sort()
44+
->values()
45+
->all();
46+
47+
$actual = Collection::make($actual)
48+
->map(static fn(Model $model) => $model->getKey())
49+
->sort()
50+
->values()
51+
->all();
52+
53+
$this->assertSame($expected, $actual);
54+
}
55+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace LaravelJsonApi\Eloquent\Tests\Acceptance\Filters;
21+
22+
use App\Models\Post;
23+
use App\Schemas\PostSchema;
24+
use Carbon\Carbon;
25+
26+
class WhereNotNullTest extends TestCase
27+
{
28+
/**
29+
* @var PostSchema
30+
*/
31+
private PostSchema $schema;
32+
33+
/**
34+
* @return void
35+
*/
36+
protected function setUp(): void
37+
{
38+
parent::setUp();
39+
40+
$this->schema = $this->schemas()->schemaFor('posts');
41+
}
42+
43+
/**
44+
* @return void
45+
*/
46+
public function testTrue(): void
47+
{
48+
$posts = Post::factory()->sequence(
49+
['published_at' => null],
50+
['published_at' => Carbon::now()],
51+
)->count(5)->create();
52+
53+
$expected = $posts->whereNotNull('published_at');
54+
55+
$actual = $this->schema
56+
->repository()
57+
->queryAll()
58+
->filter(['published' => 'true'])
59+
->get();
60+
61+
$this->assertFilteredModels($expected, $actual);
62+
}
63+
64+
/**
65+
* @return void
66+
*/
67+
public function testFalse(): void
68+
{
69+
$posts = Post::factory()->sequence(
70+
['published_at' => null],
71+
['published_at' => Carbon::now()],
72+
)->count(5)->create();
73+
74+
$expected = $posts->whereNull('published_at');
75+
76+
$actual = $this->schema
77+
->repository()
78+
->queryAll()
79+
->filter(['published' => 'false'])
80+
->get();
81+
82+
$this->assertFilteredModels($expected, $actual);
83+
}
84+
}

0 commit comments

Comments
 (0)