Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Serialize resources using schemas #2

Merged
merged 10 commits into from
Feb 2, 2021
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file. This project adheres to
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).

## Unreleased

### Added
- [#2](https://github.com/laravel-json-api/core/pull/2)
**BREAKING** The `Core\Resources\JsonApiResource` is no longer abstract, and now expects the
schema *and* the model to be injected via its constructor. It will use the schema to convert a
model to a JSON:API resource. This allows the resource classes to be optional, as the resource
resolution logic can now fall-back to the `JsonApiResource` when no specific resource class
exists. Schema fields must implement the `Contracts\Resources\Serializer\Attribute`
and `Contracts\Resources\Serializer\Relation` interfaces on their fields for the serialization
to work.
- **BREAKING** The `Contracts\Encoder\Encoder` contract now has a `withRequest` method to inject the
current HTTP request into the encoding process. The response classes have been updated to pass
the request through to the encoder in their `toResponse()` methods.
- **BREAKING** The `Contracts\Schema\Container` contract now has a `schemaForModel` method to
lookup a schema by providing either a model instance, or the fully-qualified class name of a model.
- **BREAKING** The `Contracts\Schema\ID` contract now has a `key()` method, that can return
the model key for the ID.
- **BREAKING** The `Contracts\Schema\Schema` contract now has a `idKeyName()` method
- New `Contracts\Resources\JsonApiRelation` contract for the relation object returned by the
`JsonApiResource::relationships()` method. This has the methods on it that encoders can rely on when
encoding the relationship to JSON.
- New `Core\Resources\ConditionalIterator` class for iterating over values that could contain
conditional attributes.

### Changed
- **BREAKING** The `attributes`, `relationships`, `meta` and `links` method of the `JsonApiResource`
class now require the request to be passed as a parameter. This is to bring the resource in line
with Laravel's Eloquent resource, though our implementation allows the request to be `null` to
cater for resource encoding outside of HTTP requests (e.g. queued broadcasting). Additionally,
the `relationship` method return type has been changed to the new `Contracts\Resources\JsonApiResource`
contract.
- **BREAKING** The `exists` and `create` methods on the `Contracts\Resources\Container` contract now
correctly type-hint the parameter as an `object`.
- **BREAKING** The `createResource` method on the `Contracts\Resources\Factory` contract now correctly
type-hints the parameter as an `object`.
- **BREAKING** The constructor of the `Core\Resources\Factory` class now expects a schema container
and an optional array of resource bindings (instead of an iterable). If a `null` value is provided
for the bindings, the bindings will be retrieved from the schema container. Additionally, the protected
`build` method signature has been updated to correctly type-hint the second argument as an `object`.
- **BREAKING** The constructor arguments for the `Core\Resources\Relation` class have been changed
so that it now receives the model and base URI - rather than the `JsonApiResource` object. This
change was made so that it can be used more broadly.

### Removed
- **BREAKING** The `attach` and `attachAll` methods of the `Core\Resources\Factory` class have been
removed, because they were not in use.

## [1.0.0-alpha.1] - 2021-01-25

Initial release.
8 changes: 8 additions & 0 deletions src/Contracts/Encoder/Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@

namespace LaravelJsonApi\Contracts\Encoder;

use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;

interface Encoder
{

/**
* @param Request|null $request
* @return $this
*/
public function withRequest($request): self;

/**
* @param $includePaths
* @return $this
Expand Down
20 changes: 10 additions & 10 deletions src/Contracts/Resources/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,32 @@ interface Container
* Resolve the value to a resource object or a cursor of resource objects.
*
* @param mixed $value
* a resource object, record or an iterable of records.
* a resource object, model or an iterable of models.
* @return JsonApiResource|Generator
*/
public function resolve($value);

/**
* Can the provided record be converted to a resource object?
* Can the provided model be converted to a resource object?
*
* @param mixed $record
* @param object $model
* @return bool
*/
public function exists($record): bool;
public function exists(object $model): bool;

/**
* Create a resource object for the supplied record.
* Create a resource object for the supplied models.
*
* @param mixed $record
* @param object $model
* @return JsonApiResource
*/
public function create($record): JsonApiResource;
public function create(object $model): JsonApiResource;

/**
* Create resource objects for the supplied records.
* Create resource objects for the supplied models.
*
* @param iterable $records
* @param iterable $models
* @return Generator
*/
public function cursor(iterable $records): Generator;
public function cursor(iterable $models): Generator;
}
6 changes: 3 additions & 3 deletions src/Contracts/Resources/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ interface Factory
public function handles(): iterable;

/**
* Create a resource object for the supplied record.
* Create a resource object for the supplied model.
*
* @param mixed $record
* @param object $model
* @return JsonApiResource
*/
public function createResource($record): JsonApiResource;
public function createResource(object $model): JsonApiResource;
}
66 changes: 66 additions & 0 deletions src/Contracts/Resources/JsonApiRelation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php
/*
* Copyright 2021 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Resources;

use LaravelJsonApi\Core\Document\Links;

interface JsonApiRelation
{

/**
* Get the relation's JSON:API field name.
*
* @return string
*/
public function fieldName(): string;

/**
* Get the value of the links member.
*
* @return Links
*/
public function links(): Links;

/**
* Get the value of the meta member.
*
* @return array|null
*/
public function meta(): ?array;

/**
* Get the value of the data member.
*
* @return mixed
*/
public function data();

/**
* Should data always be shown?
*
* If this method returns `false`, the relationship's data will only be shown
* if the client has requested it (via include paths). Returning `true` means
* the relationship data will always be shown, regardless of what the client
* has requested.
*
* @return bool
*/
public function showData(): bool;
}
39 changes: 39 additions & 0 deletions src/Contracts/Resources/Serializer/Attribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/*
* Copyright 2021 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Resources\Serializer;

interface Attribute extends Hideable
{

/**
* Get the JSON:API field name for the serialized attribute.
*
* @return string
*/
public function serializedFieldName(): string;

/**
* Get the JSON value from the provided model.
*
* @param object $model
* @return mixed
*/
public function serialize(object $model);
}
42 changes: 42 additions & 0 deletions src/Contracts/Resources/Serializer/Hideable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/*
* Copyright 2021 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Resources\Serializer;

use Illuminate\Http\Request;

interface Hideable
{

/**
* Is the field hidden?
*
* @param Request|null $request
* @return bool
*/
public function isHidden($request): bool;

/**
* Is the field not hidden?
*
* @param Request|null $request
* @return bool
*/
public function isNotHidden($request): bool;
}
42 changes: 42 additions & 0 deletions src/Contracts/Resources/Serializer/Relation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/*
* Copyright 2021 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Resources\Serializer;

use LaravelJsonApi\Contracts\Resources\JsonApiRelation;

interface Relation extends Hideable
{

/**
* Get the JSON:API field name for the serialized relation.
*
* @return string
*/
public function serializedFieldName(): string;

/**
* Get the JSON representation of the relationship.
*
* @param object $model
* @param string $baseUri
* @return JsonApiRelation
*/
public function serialize(object $model, string $baseUri): JsonApiRelation;
}
10 changes: 9 additions & 1 deletion src/Contracts/Schema/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ interface Container
{

/**
* Get a schema by JSON API resource type.
* Get a schema by JSON:API resource type.
*
* @param string $resourceType
* @return Schema
*/
public function schemaFor(string $resourceType): Schema;

/**
* Get a schema for the provided model class.
*
* @param string|object $model
* @return Schema
*/
public function schemaForModel($model): Schema;

/**
* Does a schema exist for the supplied resource type?
*
Expand Down
2 changes: 1 addition & 1 deletion src/Contracts/Schema/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface Field
{

/**
* The JSON API field name.
* The JSON:API field name.
*
* @return string
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Contracts/Schema/ID.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
interface ID extends Field, Sortable
{

/**
* Get the model key for the id.
*
* @return string|null
*/
public function key(): ?string;

/**
* Get the regex pattern for the ID field.
*
Expand Down
10 changes: 10 additions & 0 deletions src/Contracts/Schema/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ public function repository(): Repository;
*/
public function id(): ID;

/**
* Get the key name for the resource "id".
*
* If this method returns `null`, resource classes should fall-back to a
* sensible default. E.g. `UrlRoutable::getRouteKeyName()`.
*
* @return string|null
*/
public function idKeyName(): ?string;

/**
* Get all the field names.
*
Expand Down
Loading