Skip to content
This repository was archived by the owner on Feb 14, 2023. It is now read-only.

Commit 7fb2f3f

Browse files
committed
wip
1 parent f3d7ddc commit 7fb2f3f

File tree

9 files changed

+417
-1
lines changed

9 files changed

+417
-1
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor/
2+
composer.lock
3+
.phpunit.result.cache

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
# laravel-json-api
2-
Integrate JSON:API resources on Laravel
2+
Integrate JSON:API resources on Laravel.
3+
4+
**Note: This package is under active development and initial stage, please DON'T use it for production purposes.**

composer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "skore-labs/laravel-json-api",
3+
"description": "Integrate JSON:API resources on Laravel",
4+
"type": "library",
5+
"license": "MIT",
6+
"minimum-stability": "stable",
7+
"require": {
8+
"php": "^7.1",
9+
"illuminate/http": "~5.6.34|~5.7.0|~5.8.0",
10+
"illuminate/support": "~5.6.34|~5.7.0|~5.8.0"
11+
},
12+
"require-dev": {
13+
"phpunit/phpunit": "^7.0|^8.0",
14+
"orchestra/testbench": "~3.6.0|~3.7.0|~3.8.0",
15+
"ext-json": "*"
16+
},
17+
"autoload": {
18+
"psr-4": {
19+
"SkoreLabs\\JsonApi\\": "src"
20+
}
21+
}
22+
}

phpunit.xml.dist

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="vendor/autoload.php"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="true"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="Skore Test Suite">
14+
<directory>tests</directory>
15+
</testsuite>
16+
</testsuites>
17+
<filter>
18+
<whitelist>
19+
<directory suffix=".php">src/</directory>
20+
</whitelist>
21+
</filter>
22+
<php>
23+
<env name="DB_CONNECTION" value="testing"/>
24+
</php>
25+
</phpunit>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace SkoreLabs\JsonApi\Http\Resources;
4+
5+
use Illuminate\Database\Eloquent\{Model, Collection};
6+
use Illuminate\Support\{Str, Collection as SupportCollection};
7+
8+
trait CollectsWithRelationships
9+
{
10+
/**
11+
* Included relations on the response.
12+
*
13+
* @var array
14+
*/
15+
protected $included = [];
16+
17+
/**
18+
* Process resources relationships to included/relationship attributes.
19+
*
20+
* @return void
21+
*/
22+
protected function processRelations()
23+
{
24+
$this->collection->map(function (JsonApiResource $jsonResource) {
25+
$relations = $jsonResource->resource->getRelations();
26+
27+
foreach ($relations as $relation => $relationObj) {
28+
if ($relationObj instanceof Collection) {
29+
foreach ($relationObj->all() as $relationModel) {
30+
$jsonResource->with['relationships'][$relation]['data'][] = $this->processModelRelation(
31+
$relationModel
32+
);
33+
}
34+
}
35+
36+
if ($relationObj instanceof Model) {
37+
$jsonResource->with['relationships'][$relation]['data'] = $this->processModelRelation(
38+
$relationObj
39+
);
40+
}
41+
42+
$jsonResource->resource->unsetRelation($relation);
43+
}
44+
45+
return $jsonResource;
46+
});
47+
48+
$this->removeRepeateds();
49+
}
50+
51+
public function with()
52+
{
53+
return $this->included;
54+
}
55+
56+
/**
57+
* Process a model relation attaching to its model additional attributes.
58+
*
59+
* @param \Illuminate\Database\Eloquent\Model $relation
60+
* @return void
61+
*/
62+
protected function processModelRelation(Model $relation)
63+
{
64+
$relatedModelType = Str::lower(class_basename($relation));
65+
66+
$this->included[] = [
67+
'id' => (string) $relation->getKey(),
68+
'type' => $relatedModelType,
69+
'attributes' => array_filter($relation->toArray(), function ($key) {
70+
return ! Str::endsWith($key, '_id') && $key !== 'id';
71+
}, ARRAY_FILTER_USE_KEY),
72+
];
73+
74+
return [
75+
$relation->getKeyName() => (string) $relation->getKey(),
76+
'type' => $relatedModelType,
77+
];
78+
}
79+
80+
public function removeRepeateds()
81+
{
82+
$filtered = SupportCollection::make($this->included)->unique(function ($item) {
83+
return $item['id'] . $item['type'];
84+
});
85+
86+
$this->included = $filtered->values()->all();
87+
}
88+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace SkoreLabs\JsonApi\Http\Resources;
4+
5+
use Illuminate\Http\Resources\MissingValue;
6+
7+
trait ConditionallyLoadsAttributes
8+
{
9+
/**
10+
* Retrieve an attribute if it has been appended.
11+
*
12+
* @param string $attribute
13+
* @param mixed $value
14+
* @param mixed $default
15+
* @return \Illuminate\Http\Resources\MissingValue|mixed
16+
*/
17+
protected function whenAppended($attribute, $value = null, $default = null)
18+
{
19+
if (func_num_args() < 3) {
20+
$default = new MissingValue();
21+
}
22+
23+
if (! $this->resource->getAppends('flags')) {
24+
return value($default);
25+
}
26+
27+
if (func_num_args() === 1) {
28+
return $this->resource->{$attribute};
29+
}
30+
31+
if ($this->resource->{$attribute} === null) {
32+
return;
33+
}
34+
35+
return value($value);
36+
}
37+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace SkoreLabs\JsonApi\Http\Resources;
4+
5+
use Illuminate\Http\Resources\Json\ResourceCollection;
6+
7+
class JsonApiCollection extends ResourceCollection
8+
{
9+
use CollectsWithRelationships;
10+
11+
/**
12+
* Create a new resource instance.
13+
*
14+
* @param mixed $resource
15+
* @return void
16+
*/
17+
public function __construct($resource)
18+
{
19+
$this->collects = JsonApiResource::class;
20+
21+
parent::__construct($resource);
22+
23+
$this->processRelations();
24+
}
25+
26+
/**
27+
* Transform the resource into an array.
28+
*
29+
* @param \Illuminate\Http\Request $request
30+
* @return array
31+
*/
32+
public function toArray($request)
33+
{
34+
//
35+
36+
return JsonApiResource::collection(
37+
$this->collection
38+
);
39+
}
40+
41+
/**
42+
* Get any additional data that should be returned with the resource array.
43+
*
44+
* @param \Illuminate\Http\Request $request
45+
* @return array
46+
*/
47+
public function with($request)
48+
{
49+
return [
50+
'included' => $this->when(
51+
$this->included, $this->included
52+
),
53+
];
54+
}
55+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace SkoreLabs\JsonApi\Http\Resources;
4+
5+
use Illuminate\Http\Resources\Json\JsonResource;
6+
use Illuminate\Http\Resources\MissingValue;
7+
use Illuminate\Support\Facades\Auth;
8+
use Illuminate\Support\Str;
9+
10+
class JsonApiResource extends JsonResource
11+
{
12+
use RelationshipsWithIncludes, ConditionallyLoadsAttributes;
13+
14+
/**
15+
* The resource instance.
16+
*
17+
* @var \Illuminate\Database\Eloquent\Model
18+
*/
19+
public $resource;
20+
21+
/**
22+
* Specify if show any pivot table data.
23+
*
24+
* @var bool
25+
*/
26+
protected $showPivot = false;
27+
28+
/**
29+
* Determine whether authorize to view this resource.
30+
*
31+
* @var bool
32+
*/
33+
protected $authorize;
34+
35+
/**
36+
* Authorize to view this resource.
37+
*
38+
* @return bool
39+
*/
40+
protected function authorize()
41+
{
42+
$strClass = get_class($this->resource);
43+
44+
return $this->authorize
45+
?: is_string($strClass)
46+
?: Auth::user()->can('viewAny', $strClass)
47+
?: Auth::user()->can('view', $this->resource);
48+
}
49+
50+
/**
51+
* Set authorization bypassing automatic authorization.
52+
*
53+
* @param bool $value
54+
* @return void
55+
*/
56+
public function setAuthorize($value = true)
57+
{
58+
$this->authorize = $value;
59+
}
60+
61+
/**
62+
* Transform the resource into an array.
63+
*
64+
* @param \Illuminate\Http\Request $request
65+
* @return array
66+
*/
67+
public function toArray($request)
68+
{
69+
if ($this->authorize() === false) {
70+
return new MissingValue();
71+
}
72+
73+
if (is_null($this->resource)) {
74+
return [];
75+
}
76+
77+
return (is_array($this->resource))
78+
? $this->resource
79+
: $this->formatResponse();
80+
}
81+
82+
/**
83+
* Format model response to array.
84+
*
85+
* @return array
86+
*/
87+
protected function formatResponse()
88+
{
89+
$hiddenAttrs = [
90+
$this->resource->getKeyName(),
91+
];
92+
93+
if (! $this->showPivot) {
94+
$hiddenAttrs[] = 'pivot';
95+
}
96+
97+
$this->resource->addHidden($hiddenAttrs);
98+
99+
return [
100+
$this->resource->getKeyName() => (string) $this->resource->getKey(),
101+
'type' => Str::lower(class_basename($this->resource)),
102+
'attributes' => $this->resource->toArray(),
103+
'relationships' => $this->when(
104+
$this->relationships, $this->relationships
105+
),
106+
];
107+
}
108+
}

0 commit comments

Comments
 (0)