diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index c68ff75..406a498 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
- php_version: ['8.1', '8.2', '8.3']
+ php_version: ['8.1', '8.2', '8.3', '8.4']
laravel_version: ['^9.0', '^10.0', '^11.0']
exclude:
- php_version: '8.1'
diff --git a/CHANGELOG-1.4.md b/CHANGELOG-1.4.md
index bcc63a2..4ffc537 100644
--- a/CHANGELOG-1.4.md
+++ b/CHANGELOG-1.4.md
@@ -1,10 +1,20 @@
Release note
============
-# v1.4.4
+# v1.4.6
+### Change
+- Support PHP 8.4
+### Fixed
+- Fix `applyWhen` behavior
+
+# v1.4.5
### Change
- `applyWhen` condition can be a closure
- CI: test all supported laravel version
+# v1.4.4
+### Fixes
+- typo on ToResponse : change Content-type to Content-Type
+
# v1.4.3
### Added
- Add support for Laravel 11
diff --git a/phpunit.php8.4.xml.dist b/phpunit.php8.4.xml.dist
new file mode 100644
index 0000000..8ef7a48
--- /dev/null
+++ b/phpunit.php8.4.xml.dist
@@ -0,0 +1,26 @@
+
+
+
+
+ tests/Unit
+
+
+ tests/Feature
+
+
+
+
+
+
+
+
+ src
+
+
+
diff --git a/src/Asserts/AssertJsonApiResource.php b/src/Asserts/AssertJsonApiResource.php
new file mode 100644
index 0000000..6bc1182
--- /dev/null
+++ b/src/Asserts/AssertJsonApiResource.php
@@ -0,0 +1,55 @@
+ $class
+ */
+ public function __construct(protected string $class)
+ {
+ }
+
+ public function assert(): void
+ {
+ $this->itCanGenerateSchema();
+ $this->allAttributesAreLazySet();
+ }
+
+ private function itCanGenerateSchema(): void
+ {
+ try {
+ $this->class::schema();
+ } catch (\Throwable $throwable) {
+ throw new FailGenerateSchema($this->class, $throwable);
+ }
+ }
+
+ private function allAttributesAreLazySet(): void
+ {
+ $reflection = new ReflectionClass($this->class);
+ $instance = $reflection->newInstanceWithoutConstructor();
+ $instance->resource = new FakeModel;
+
+ $method = $reflection->getMethod('toAttributes');
+ $method->setAccessible(true);
+ /** @var iterable $attributes */
+ $attributes = $method->invoke($instance, new Request());
+ $attributes = Values::mergeValues($attributes);
+
+ foreach ($attributes as $key => $attribute) {
+ if (!($attribute instanceof \Closure || $attribute instanceof MissingValue || $attribute instanceof Value)) {
+ throw new EagerSetAttribute($this->class, $key);
+ }
+ }
+ }
+}
diff --git a/src/Asserts/EagerSetAttribute.php b/src/Asserts/EagerSetAttribute.php
new file mode 100644
index 0000000..b3b854d
--- /dev/null
+++ b/src/Asserts/EagerSetAttribute.php
@@ -0,0 +1,13 @@
+whenIncluded ??= true;
@@ -83,7 +83,7 @@ public function whenIncluded(bool $whenIncluded = null): static
*
* @return static
*/
- public function whenLoaded(string $relation = null): self
+ public function whenLoaded(null|string $relation = null): self
{
return $this->when(fn(
Request $request,
@@ -100,7 +100,7 @@ public function whenLoaded(string $relation = null): self
*
* @return static
*/
- public function whenPivotLoaded(string $table, string $accessor = null): self
+ public function whenPivotLoaded(string $table, null|string $accessor = null): self
{
return $this->when(fn(
Request $request,
diff --git a/src/Descriptors/Values/ValueRaw.php b/src/Descriptors/Values/ValueRaw.php
deleted file mode 100644
index db0aa72..0000000
--- a/src/Descriptors/Values/ValueRaw.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- */
-class ValueRaw extends Value
-{
- public function __construct(
- null|string|Closure $attribute,
- protected mixed $raw
- ) {
- parent::__construct($attribute);
- }
-
- public function value(mixed $of): mixed
- {
- return $this->raw;
- }
-}
diff --git a/src/Resources/Concerns/ConditionallyLoadsAttributes.php b/src/Resources/Concerns/ConditionallyLoadsAttributes.php
index a6cd0d7..feb1d1a 100644
--- a/src/Resources/Concerns/ConditionallyLoadsAttributes.php
+++ b/src/Resources/Concerns/ConditionallyLoadsAttributes.php
@@ -4,7 +4,7 @@
use Ark4ne\JsonApi\Descriptors\Describer;
use Ark4ne\JsonApi\Descriptors\Relations\RelationRaw;
-use Ark4ne\JsonApi\Descriptors\Values\ValueRaw;
+use Ark4ne\JsonApi\Descriptors\Values\ValueMixed;
use Ark4ne\JsonApi\Resources\Relationship;
use Ark4ne\JsonApi\Support\Fields;
use Ark4ne\JsonApi\Support\Includes;
@@ -57,13 +57,13 @@ protected function whenIncluded(Request $request, string $type, mixed $value)
*/
protected function applyWhen(bool|Closure $condition, iterable $data): MergeValue
{
- return new MergeValue(collect($data)->map(function ($raw, $key) use ($condition) {
+ return new MergeValue(collect($data)->map(function ($raw) use ($condition) {
if ($raw instanceof Describer) {
$value = $raw;
} elseif ($raw instanceof Relationship) {
$value = RelationRaw::fromRelationship($raw);
} else {
- $value = new ValueRaw($key, $raw);
+ $value = new ValueMixed(is_callable($raw) ? $raw : static fn () => $raw);
}
return $value->when(fn () => value($condition));
diff --git a/src/Resources/Concerns/Relationships.php b/src/Resources/Concerns/Relationships.php
index abd97f1..131f9c0 100644
--- a/src/Resources/Concerns/Relationships.php
+++ b/src/Resources/Concerns/Relationships.php
@@ -60,7 +60,7 @@ private function requestedRelationshipsLoadFromSchema(Request $request, Skeleton
if ($load && Includes::include($request, $name)) {
$include = Includes::through($name, fn() => $this->requestedRelationshipsLoadFromSchema($request, $schema->relationships[$name]));
- $apply = static function ($load, string $pre = null) use (&$loads, &$apply) {
+ $apply = static function ($load, null|string $pre = null) use (&$loads, &$apply) {
foreach ((array)$load as $key => $value) {
if (is_string($value)) {
$loads["$pre.$value"] = [];
diff --git a/src/Resources/Concerns/Schema.php b/src/Resources/Concerns/Schema.php
index 4e593e4..8499bd8 100644
--- a/src/Resources/Concerns/Schema.php
+++ b/src/Resources/Concerns/Schema.php
@@ -21,7 +21,7 @@ trait Schema
*/
private static array $schemas = [];
- public static function schema(Request $request = null): Skeleton
+ public static function schema(null|Request $request = null): Skeleton
{
if (isset(self::$schemas[static::class])) {
return self::$schemas[static::class];
diff --git a/src/Resources/Concerns/SchemaCollection.php b/src/Resources/Concerns/SchemaCollection.php
index dedfbb3..39f314d 100644
--- a/src/Resources/Concerns/SchemaCollection.php
+++ b/src/Resources/Concerns/SchemaCollection.php
@@ -8,7 +8,7 @@
trait SchemaCollection
{
- public static function schema(Request $request = null): Skeleton
+ public static function schema(null|Request $request = null): Skeleton
{
return self::new()->collects::schema($request);
}
diff --git a/src/Resources/Relationship.php b/src/Resources/Relationship.php
index 6083e38..c8a64c9 100644
--- a/src/Resources/Relationship.php
+++ b/src/Resources/Relationship.php
@@ -100,7 +100,7 @@ public function asCollection(): self
* @param bool|null $whenIncluded
* @return $this
*/
- public function whenIncluded(bool $whenIncluded = null): static
+ public function whenIncluded(null|bool $whenIncluded = null): static
{
if ($whenIncluded === null) {
$this->whenIncluded ??= true;
diff --git a/src/Support/Arr.php b/src/Support/Arr.php
index 3aae71d..f83b64e 100644
--- a/src/Support/Arr.php
+++ b/src/Support/Arr.php
@@ -107,7 +107,7 @@ private static function flattenDot(array $array, string $prepend, string $saveKe
* @param string|null $saveKey
* @return array
*/
- public static function undot(array $array, string $saveKey = null): array
+ public static function undot(array $array, null|string $saveKey = null): array
{
$results = [];
@@ -129,7 +129,7 @@ public static function undot(array $array, string $saveKey = null): array
* @param string|null $saveKey
* @return mixed
*/
- public static function apply(array &$array, string $path, mixed $value, string $saveKey = null): mixed
+ public static function apply(array &$array, string $path, mixed $value, null|string $saveKey = null): mixed
{
$keys = explode('.', $path);
diff --git a/src/Support/Fields.php b/src/Support/Fields.php
index 0020b35..c17cf59 100644
--- a/src/Support/Fields.php
+++ b/src/Support/Fields.php
@@ -37,7 +37,7 @@ public static function through(string $type, callable $callable): mixed
*
* @return string[]|null
*/
- public static function get(Request $request, string $type = null): ?array
+ public static function get(Request $request, null|string $type = null): ?array
{
$type ??= self::$current;
@@ -57,7 +57,7 @@ public static function get(Request $request, string $type = null): ?array
*
* @return bool
*/
- public static function has(Request $request, string $field, string $type =null): bool {
+ public static function has(Request $request, string $field, null|string $type =null): bool {
$type ??= self::$current;
if ($type === null) {
diff --git a/tests/Feature/SchemaTest.php b/tests/Feature/SchemaTest.php
index 8a3b147..4978272 100644
--- a/tests/Feature/SchemaTest.php
+++ b/tests/Feature/SchemaTest.php
@@ -13,7 +13,14 @@ class SchemaTest extends FeatureTestCase
{
public function testSchema()
{
- $user = new Skeleton(UserResource::class, 'user', ['name', 'email', 'only-with-fields']);
+ $user = new Skeleton(UserResource::class, 'user', [
+ 'name',
+ 'email',
+ 'only-with-fields',
+ 'with-apply-conditional-raw',
+ 'with-apply-conditional-closure',
+ 'with-apply-conditional-value',
+ ]);
$post = new Skeleton(PostResource::class, 'post', ['state', 'title', 'content']);
$comment = new Skeleton(CommentResource::class, 'comment', ['content']);
diff --git a/tests/Feature/User/ResourceTest.php b/tests/Feature/User/ResourceTest.php
index 20c67c3..c1ab1c5 100644
--- a/tests/Feature/User/ResourceTest.php
+++ b/tests/Feature/User/ResourceTest.php
@@ -164,7 +164,16 @@ private function getJsonResult(User $user, ?array $attributes = null, ?array $re
'attributes' => array_filter(array_intersect_key([
'name' => $user->name,
'email' => $user->email,
- ], array_fill_keys($attributes ?? ['name', 'email'], true))),
+ "with-apply-conditional-closure" => "huge-data-set",
+ "with-apply-conditional-raw" => "huge-data-set",
+ "with-apply-conditional-value" => "huge-data-set"
+ ], array_fill_keys($attributes ?? [
+ 'name',
+ 'email',
+ "with-apply-conditional-closure",
+ "with-apply-conditional-raw",
+ "with-apply-conditional-value",
+ ], true))),
'relationships' => [
'main-post' => [],
'posts' => array_filter([
diff --git a/tests/Unit/Resources/Concerns/ConditionallyLoadsAttributesTest.php b/tests/Unit/Resources/Concerns/ConditionallyLoadsAttributesTest.php
index d9b4dcb..4af60e2 100644
--- a/tests/Unit/Resources/Concerns/ConditionallyLoadsAttributesTest.php
+++ b/tests/Unit/Resources/Concerns/ConditionallyLoadsAttributesTest.php
@@ -7,6 +7,7 @@
use Ark4ne\JsonApi\Descriptors\Values\ValueMixed;
use Ark4ne\JsonApi\Descriptors\Values\ValueRaw;
use Ark4ne\JsonApi\Resources\Concerns\ConditionallyLoadsAttributes;
+use Ark4ne\JsonApi\Resources\JsonApiResource;
use Ark4ne\JsonApi\Resources\Relationship;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -75,16 +76,16 @@ public function testApplyWhen()
'missing.2' => 123,
]);
$this->assertEquals(new MergeValue([
- 'missing.1' => (new ValueRaw('missing.1', 'abc'))->when(fn () => false),
- 'missing.2' => (new ValueRaw('missing.2', 123))->when(fn () => false),
+ 'missing.1' => (new ValueMixed(fn() => 'abc'))->when(fn() => false),
+ 'missing.2' => (new ValueMixed(fn() => 123))->when(fn() => false),
]), $actual);
$actual = Reflect::invoke($stub, 'applyWhen', true, [
'present.1' => 'abc',
'present.2' => 123,
]);
$this->assertEquals(new MergeValue([
- 'present.1' => (new ValueRaw('present.1', 'abc'))->when(fn () => true),
- 'present.2' => (new ValueRaw('present.2', 123))->when(fn () => true),
+ 'present.1' => (new ValueMixed(fn() => 'abc'))->when(fn() => true),
+ 'present.2' => (new ValueMixed(fn() => 123))->when(fn() => true),
]), $actual);
$actual = Reflect::invoke($stub, 'applyWhen', true, [
'present.1' => (new ValueMixed(fn() => 'abc')),
@@ -94,11 +95,11 @@ public function testApplyWhen()
'present.5' => (new Relationship(UserResource::class, fn() => null)),
]);
$this->assertEquals(new MergeValue([
- 'present.1' => (new ValueMixed(fn() => 'abc'))->when(fn () => true),
- 'present.2' => (new ValueMixed(fn() => 123))->when(fn () => true),
- 'present.3' => (new RelationOne('present', fn() => 'abc'))->when(fn () => true),
- 'present.4' => (new RelationOne('present', fn() => 123))->when(fn () => true),
- 'present.5' => RelationRaw::fromRelationship(new Relationship(UserResource::class, fn() => null))->when(fn () => true),
+ 'present.1' => (new ValueMixed(fn() => 'abc'))->when(fn() => true),
+ 'present.2' => (new ValueMixed(fn() => 123))->when(fn() => true),
+ 'present.3' => (new RelationOne('present', fn() => 'abc'))->when(fn() => true),
+ 'present.4' => (new RelationOne('present', fn() => 123))->when(fn() => true),
+ 'present.5' => RelationRaw::fromRelationship(new Relationship(UserResource::class, fn() => null))->when(fn() => true),
]), $actual);
$actual = Reflect::invoke($stub, 'applyWhen', false, [
'missing.1' => (new ValueMixed(fn() => 'abc')),
@@ -108,11 +109,11 @@ public function testApplyWhen()
'missing.5' => (new Relationship(UserResource::class, fn() => null)),
]);
$this->assertEquals(new MergeValue([
- 'missing.1' => (new ValueMixed(fn() => 'abc'))->when(fn () => false),
- 'missing.2' => (new ValueMixed(fn() => 123))->when(fn () => false),
- 'missing.3' => (new RelationOne('present', fn() => 'abc'))->when(fn () => false),
- 'missing.4' => (new RelationOne('present', fn() => 123))->when(fn () => false),
- 'missing.5' => RelationRaw::fromRelationship(new Relationship(UserResource::class, fn() => null))->when(fn () => false),
+ 'missing.1' => (new ValueMixed(fn() => 'abc'))->when(fn() => false),
+ 'missing.2' => (new ValueMixed(fn() => 123))->when(fn() => false),
+ 'missing.3' => (new RelationOne('present', fn() => 'abc'))->when(fn() => false),
+ 'missing.4' => (new RelationOne('present', fn() => 123))->when(fn() => false),
+ 'missing.5' => RelationRaw::fromRelationship(new Relationship(UserResource::class, fn() => null))->when(fn() => false),
]), $actual);
}
diff --git a/tests/app/Http/Resources/UserResource.php b/tests/app/Http/Resources/UserResource.php
index 47e0d66..47ae055 100644
--- a/tests/app/Http/Resources/UserResource.php
+++ b/tests/app/Http/Resources/UserResource.php
@@ -25,6 +25,11 @@ protected function toAttributes(Request $request): iterable
'name' => $this->resource->name,
'email' => $this->resource->email,
'only-with-fields' => $this->string(fn() => 'huge-data-set')->whenInFields(),
+ $this->applyWhen(fn () => true, [
+ 'with-apply-conditional-raw' => 'huge-data-set',
+ 'with-apply-conditional-closure' => fn () => 'huge-data-set',
+ 'with-apply-conditional-value' => $this->string(fn () => 'huge-data-set'),
+ ]),
];
}