Permalink
Browse files

implementation of links object for relationships

  • Loading branch information...
1 parent 53c7260 commit 6f3eb0937e62fda86847a2fee2e42aea975616e2 @tuyakhov committed Dec 17, 2016
Showing with 149 additions and 13 deletions.
  1. +45 −5 README.md
  2. +15 −0 src/LinksInterface.php
  3. +25 −0 src/ResourceTrait.php
  4. +7 −0 src/Serializer.php
  5. +1 −0 src/actions/ViewRelatedAction.php
  6. +45 −6 tests/SerializerTest.php
  7. +11 −2 tests/data/ResourceModel.php
View
@@ -23,10 +23,6 @@ or add
to the require section of your `composer.json` file.
-
-Usage
-==============================
-Once the extension is installed, simply use it in your code by :
Data Serializing and Content Negotiation:
-------------------------------------------
Controller:
@@ -49,7 +45,6 @@ class Controller extends \yii\rest\Controller
}
```
Defining models:
-
1) Let's define `User` model and declare an `articles` relation
```php
use tuyakhov\jsonapi\ResourceTrait;
@@ -115,6 +110,51 @@ component in the application configuration like the following:
],
],
```
+Links
+---------------------------
+Your resource classes may support HATEOAS by implementing the `LinksInterface`.
+The interface contains `getLinks()` method which should return a list of links.
+Typically, you should return at least the `self` link representing the URL to the resource object itself.
+In order to appear the links in relationships `getLinks()` method should return `self` link.
+Based on this link each relationship will generate `self` and `related` links.
+By default it happens by appending a relationship name at the end of the `self` link of the primary model,
+you can simply change that behavior by overwriting `getRelationshipLinks()` method.
+For example,
+```php
+class User extends ActiveRecord implements ResourceInterface, LinksInterface
+{
+ use ResourceTrait;
+
+ public function getLinks()
+ {
+ return [
+ Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
+ ];
+ }
+}
+```
+As the result:
+```javascript
+{
+ "data": {
+ "type": "users",
+ "id": "1",
+ // ... this user's attributes
+ "relationships": {
+ "articles": {
+ // ... article's data
+ "links": {
+ "self": {"href": "http://yourdomain.com/users/1/relationships/articles"},
+ "related": {"href": "http://yourdomain.com/users/1/articles"}
+ }
+ }
+ }
+ "links": {
+ "self": {"href": "http://yourdomain.com/users/1"}
+ }
+ }
+}
+```
Enabling JSON Input
---------------------------
To let the API accept input data in JSON API format, configure the [[yii\web\Request::$parsers|parsers]] property of the request application component to use the [[tuyakhov\jsonapi\JsonApiParser]] for JSON input
@@ -0,0 +1,15 @@
+<?php
+/**
+ * @link http://www.stombox.com/
+ * @copyright Copyright (c) 2015 Stombox LLC
+ * @license http://www.stombox.com/license/
+ */
+
+namespace tuyakhov\jsonapi;
+
+use \yii\web\Linkable;
+
+interface LinksInterface extends Linkable
+{
+ public function getRelationshipLinks($name);
+}
View
@@ -8,6 +8,8 @@
use yii\base\Arrayable;
use yii\db\ActiveRecordInterface;
use yii\helpers\Inflector;
+use yii\web\Link;
+use yii\web\Linkable;
trait ResourceTrait
{
@@ -86,6 +88,29 @@ public function setResourceRelationship($name, $relationship)
}
/**
+ * @param string $name the case sensitive name of the relationship.
+ * @return array
+ */
+ public function getRelationshipLinks($name)
+ {
+ if (!$this instanceof Linkable) {
+ return [];
+ }
+ $primaryLinks = $this->getLinks();
+ if (!array_key_exists(Link::REL_SELF, $primaryLinks)) {
+ return [];
+ }
+ $resourceLink = is_string($primaryLinks[Link::REL_SELF]) ? rtrim($primaryLinks[Link::REL_SELF], '/') : null;
+ if (!$resourceLink) {
+ return [];
+ }
+ return [
+ Link::REL_SELF => "{$resourceLink}/relationships/{$name}",
+ 'related' => "{$resourceLink}/{$name}",
+ ];
+ }
+
+ /**
* @param array $fields
* @param array $fieldSet
* @return array
View
@@ -110,8 +110,15 @@ protected function serializeModel($model)
} elseif ($items instanceof ResourceIdentifierInterface) {
$relationship = ['id' => $items->getId(), 'type' => $items->getType()];
}
+
if (!empty($relationship)) {
$data['relationships'][$name]['data'] = $relationship;
+ if ($model instanceof LinksInterface) {
+ $links = $model->getRelationshipLinks($name);
+ if (!empty($links)) {
+ $data['relationships'][$name]['links'] = Link::serialize($links);
+ }
+ }
}
}
}
@@ -7,6 +7,7 @@
use tuyakhov\jsonapi\ResourceInterface;
+use yii\base\Arrayable;
use yii\data\ActiveDataProvider;
use yii\db\ActiveQuery;
use yii\rest\Action;
@@ -47,6 +47,9 @@ public function testSerializeModelData()
'attributes' => [
'field1' => 'test',
'field2' => 2,
+ ],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123']
]
]
], $serializer->serialize($model));
@@ -59,6 +62,9 @@ public function testSerializeModelData()
'type' => 'resource-models',
'attributes' => [
'field1' => 'test',
+ ],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123']
]
]
], $serializer->serialize($model));
@@ -71,6 +77,9 @@ public function testSerializeModelData()
'type' => 'resource-models',
'attributes' => [
'field1' => 'test',
+ ],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123']
]
]
], $serializer->serialize($model));
@@ -87,7 +96,16 @@ public function testExpand()
'field2' => 2,
],
'relationships' => [
- 'extraField1' => ['data' => ['id' => '123', 'type' => 'resource-models']]
+ 'extraField1' => [
+ 'data' => ['id' => '123', 'type' => 'resource-models'],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123/relationships/extraField1'],
+ 'related' => ['href' => 'http://example.com/resource/123/extraField1'],
+ ]
+ ]
+ ],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123']
]
];
$model = new ResourceModel();
@@ -109,6 +127,9 @@ public function testExpand()
'field1' => 'test',
'field2' => 2,
],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123']
+ ]
]
]
], $serializer->serialize($model));
@@ -124,6 +145,9 @@ public function testExpand()
'field1' => 'test',
'field2' => 2,
],
+ 'links' => [
+ 'self' => ['href' => 'http://example.com/resource/123']
+ ]
]
]
], $serializer->serialize($model));
@@ -140,14 +164,29 @@ public function dataProviderSerializeDataProvider()
$bob->username = 'Bob';
$bob->extraField1 = new ResourceModel();
$expectedBob = ['id' => '123', 'type' => 'resource-models',
- 'attributes' => ['username' => 'Bob'],
- 'relationships' => ['extraField1' => ['data' => ['id' => '123', 'type' => 'resource-models']]]];
+ 'attributes' => ['username' => 'Bob'],
+ 'links' => ['self' => ['href' => 'http://example.com/resource/123']],
+ 'relationships' => ['extraField1' => [
+ 'data' => ['id' => '123', 'type' => 'resource-models'],
+ 'links' => [
+ 'related' => ['href' => 'http://example.com/resource/123/extraField1'],
+ 'self' => ['href' => 'http://example.com/resource/123/relationships/extraField1']
+ ]
+ ]]];
$tom = new ResourceModel();
$tom->username = 'Tom';
$tom->extraField1 = new ResourceModel();
- $expectedTom = ['id' => '123', 'type' => 'resource-models',
- 'attributes' => ['username' => 'Tom'],
- 'relationships' => ['extraField1' => ['data' => ['id' => '123', 'type' => 'resource-models']]]];
+ $expectedTom = [
+ 'id' => '123', 'type' => 'resource-models',
+ 'attributes' => ['username' => 'Tom'],
+ 'links' => ['self' => ['href' => 'http://example.com/resource/123']],
+ 'relationships' => ['extraField1' => [
+ 'data' => ['id' => '123', 'type' => 'resource-models'],
+ 'links' => [
+ 'related' => ['href' => 'http://example.com/resource/123/extraField1'],
+ 'self' => ['href' => 'http://example.com/resource/123/relationships/extraField1']
+ ]
+ ]]];
return [
[
new ArrayDataProvider([
@@ -5,12 +5,14 @@
namespace tuyakhov\jsonapi\tests\data;
+use tuyakhov\jsonapi\LinksInterface;
use tuyakhov\jsonapi\ResourceInterface;
use tuyakhov\jsonapi\ResourceTrait;
use yii\base\Model;
-use yii\db\ActiveQuery;
+use yii\helpers\Url;
+use yii\web\Link;
-class ResourceModel extends Model implements ResourceInterface
+class ResourceModel extends Model implements ResourceInterface, LinksInterface
{
use ResourceTrait;
@@ -49,4 +51,11 @@ public function setRelation($name, $value)
{
$this->_related[$name] = $value;
}
+
+ public function getLinks()
+ {
+ return [
+ Link::REL_SELF => Url::to('http://example.com/resource/' . $this->getId())
+ ];
+ }
}

0 comments on commit 6f3eb09

Please sign in to comment.