Skip to content

Commit 5157137

Browse files
authored
Merge pull request #108 from HendrikN/feature/generic-types
Add support for generics
2 parents 7b0e643 + 78c530c commit 5157137

29 files changed

+314
-65
lines changed

.github/workflows/static-analysis.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Run static analysis
2+
3+
on:
4+
- push
5+
- pull_request
6+
7+
jobs:
8+
types:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Checkout code
13+
uses: actions/checkout@v4
14+
15+
- name: Setup PHP
16+
uses: shivammathur/setup-php@v2
17+
with:
18+
php-version: 8.3
19+
tools: composer:v2
20+
coverage: none
21+
22+
- name: Install dependencies
23+
run: composer update --prefer-stable --prefer-dist --no-interaction --no-progress
24+
25+
- name: Execute type checking
26+
run: vendor/bin/phpstan --configuration="phpstan.types.neon.dist" --no-progress

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,52 @@ N.B. This example uses our [swisnl/php-http-fixture-client](https://github.com/s
477477
This package allows you to easily mock requests with static fixtures.
478478
Definitely worth a try!
479479

480+
## Using generics
481+
482+
This package provides support for generic types in repositories and relationships,
483+
so your IDE can provide type hinting and auto-completion for the items you are working with.
484+
485+
This is achieved by using [generics in PHPDoc annotations](https://phpstan.org/blog/generics-in-php-using-phpdocs).
486+
487+
### Repositories
488+
489+
```php
490+
491+
/** @extends \Swis\JsonApi\Client\Repository<BlogItem> */
492+
class BlogRepository extends \Swis\JsonApi\Client\Repository {...}
493+
494+
```
495+
496+
Now, when you use the `BlogRepository` class, your IDE understands the correct return types for the `all()`, `find()` and `save()` methods.
497+
498+
### Relationships
499+
500+
You can also use generics in your relationships to specify the type of the related item.
501+
Just use the `OneRelationInterface` or `ManyRelationInterface` interfaces in your relation method and specify the type of the related item:
502+
503+
```php
504+
505+
/** @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface<AuthorItem> */
506+
public function author(): OneRelationInterface
507+
{
508+
return $this->hasOne(AuthorItem::class);
509+
}
510+
511+
```
512+
513+
This way, when accessing the `$blog->author()->getData()`, your IDE will understand that it returns an `AuthorItem` instance.
514+
515+
The same can be achieved for ManyRelations (`HasMany`, `MorphToMany`):
516+
517+
```php
518+
519+
/** @return \Swis\JsonApi\Client\Interfaces\ManyRelationInterface<AuthorItem> */
520+
public function comments(): ManyRelationInterface
521+
{
522+
return $this->hasMany(CommentItem::class);
523+
}
524+
525+
```
480526

481527
## Advanced usage
482528

composer.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"guzzlehttp/guzzle": "^7.3",
1919
"laravel/pint": "^1.5",
2020
"php-http/mock-client": "^1.2",
21+
"phpstan/phpstan": "^2.1",
2122
"phpunit/phpunit": "^9.5"
2223
},
2324
"suggest": {
@@ -36,10 +37,6 @@
3637
},
3738
"license": "MIT",
3839
"authors": [
39-
{
40-
"name": "Hendrik Nijmeijer",
41-
"email": "[email protected]"
42-
},
4340
{
4441
"name": "Jasper Zonneveld",
4542
"email": "[email protected]",
@@ -52,9 +49,10 @@
5249
}
5350
],
5451
"scripts": {
55-
"test": "phpunit",
5652
"check-style": "pint --test",
57-
"fix-style": "pint"
53+
"check-types": "phpstan --configuration=\"phpstan.types.neon.dist\"",
54+
"fix-style": "pint",
55+
"test": "phpunit"
5856
},
5957
"extra": {
6058
"branch-alias": {

phpstan.types.neon.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: max
3+
paths:
4+
- tests\_mocks
5+
- types

src/Actions/Create.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
use Swis\JsonApi\Client\Interfaces\ItemInterface;
88

9+
/**
10+
* @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
11+
*/
912
trait Create
1013
{
1114
/**
12-
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
15+
* @return \Swis\JsonApi\Client\Interfaces\ItemDocumentInterface<TItem>
1316
*/
1417
public function create(ItemInterface $item, array $parameters = [], array $headers = [])
1518
{

src/Actions/FetchMany.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
namespace Swis\JsonApi\Client\Actions;
66

7+
/**
8+
* @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
9+
*/
710
trait FetchMany
811
{
912
/**
10-
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
13+
* @return \Swis\JsonApi\Client\Interfaces\CollectionDocumentInterface<TItem>
1114
*/
1215
public function all(array $parameters = [], array $headers = [])
1316
{

src/Actions/FetchOne.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
namespace Swis\JsonApi\Client\Actions;
66

7+
/**
8+
* @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
9+
*/
710
trait FetchOne
811
{
912
/**
10-
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
13+
* @return \Swis\JsonApi\Client\Interfaces\ItemDocumentInterface<TItem>
1114
*/
1215
public function find(string $id, array $parameters = [], array $headers = [])
1316
{

src/Actions/Save.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@
66

77
use Swis\JsonApi\Client\Interfaces\ItemInterface;
88

9+
/**
10+
* @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
11+
*/
912
trait Save
1013
{
14+
/** @use Create<TItem> */
1115
use Create { create as protected saveNew; }
16+
17+
/** @use Update<TItem> */
1218
use Update { update as protected saveExisting; }
1319

1420
/**
15-
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
21+
* @return \Swis\JsonApi\Client\Interfaces\ItemDocumentInterface<TItem>
1622
*/
1723
public function save(ItemInterface $item, array $parameters = [], array $headers = [])
1824
{

src/Actions/TakeOne.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
namespace Swis\JsonApi\Client\Actions;
66

7+
/**
8+
* @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
9+
*/
710
trait TakeOne
811
{
912
/**
10-
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
13+
* @return \Swis\JsonApi\Client\Interfaces\ItemDocumentInterface<TItem>
1114
*/
1215
public function take(array $parameters = [], array $headers = [])
1316
{

src/Actions/Update.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
use Swis\JsonApi\Client\Interfaces\ItemInterface;
88

9+
/**
10+
* @template TItem of \Swis\JsonApi\Client\Interfaces\ItemInterface
11+
*/
912
trait Update
1013
{
1114
/**
12-
* @return \Swis\JsonApi\Client\Interfaces\DocumentInterface
15+
* @return \Swis\JsonApi\Client\Interfaces\ItemDocumentInterface<TItem>
1316
*/
1417
public function update(ItemInterface $item, array $parameters = [], array $headers = [])
1518
{

0 commit comments

Comments
 (0)