diff --git a/README.md b/README.md index 2a91471..3f8ea88 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,10 @@ class UserController extends \yii\rest\Controller 'class' => 'tuyakhov\jsonapi\actions\ViewRelatedAction', 'modelClass' => ExampleModel::className() ], + 'view-relationship' => [ + 'class' => 'tuyakhov\jsonapi\actions\ViewRelationsipAction', + 'modelClass' => ExampleModel::className() + ], 'update-relationship' => [ 'class' => 'tuyakhov\jsonapi\actions\UpdateRelationshipAction', 'modelClass' => ExampleModel::className() @@ -313,14 +317,8 @@ return [ 'urlManager' => [ 'rules' => [ [ - 'class' => 'yii\rest\UrlRule', - 'controller' => 'user', - 'extraPatterns' => [ - 'GET {id}/' => 'view-related', - 'PATCH {id}/relationships/' => 'update-relationship', - 'DELETE {id}/relationships/' => 'delete-relationship', - '{id}/' => 'options' - ], + 'class' => 'tukyahov\jsonapi\UrlRule', + 'controller' => ['user'], 'except' => ['index'], ], diff --git a/src/Relationship.php b/src/Relationship.php new file mode 100644 index 0000000..25744ed --- /dev/null +++ b/src/Relationship.php @@ -0,0 +1,25 @@ +addRelated([$related]); + } + + $this->relations = array_merge($this->relations, $related); + return $this; + } +} \ No newline at end of file diff --git a/src/Serializer.php b/src/Serializer.php index 481f79c..eb3bd65 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -96,6 +96,8 @@ public function serialize($data) return $this->serializeResource($data); } elseif ($data instanceof DataProviderInterface) { return $this->serializeDataProvider($data); + } elseif ($data instanceof Relationship) { + return $this->serializeRelationshipResource($data); } else { return $data; } @@ -109,7 +111,7 @@ public function serialize($data) protected function serializeModel(ResourceInterface $model, array $included = []) { $fields = $this->getRequestedFields(); - $type = $this->pluralize ? Inflector::pluralize($model->getType()) : $model->getType(); + $type = $this->getType($model); $fields = isset($fields[$type]) ? $fields[$type] : []; $topLevel = array_map(function($item) { @@ -333,6 +335,36 @@ protected function serializeModelErrors($model) return $result; } + protected function serializeRelationshipResource($relationship) + { + return [ + 'data' => $this->serializeRelationships($relationship) + ]; + } + + public function serializeRelationships($relationship) + { + if (!$relationship->multiple) { + if (!count($relationship->relations)) { + return null; + } + return $this->serializeRelationship($relationship->relations[0]); + } + + return array_map(function($relation) { + return $this->serializeRelationship($relation); + }, $relationship->relations); + } + + public function serializeRelationship($relationship) + { + $primaryKey = $relationship->getPrimaryKey(true); + return [ + "id" => implode("-", $primaryKey), + "type" => $this->getType($relationship), + ]; + } + /** * @return array */ @@ -369,4 +401,9 @@ protected function prepareMemberNames(array $memberNames = []) { return array_map($this->prepareMemberName, $memberNames); } + + protected function getType($model) + { + return $this->pluralize ? Inflector::pluralize($model->getType()) : $model->getType(); + } } diff --git a/src/UrlRule.php b/src/UrlRule.php index a5206fd..bee6d8a 100644 --- a/src/UrlRule.php +++ b/src/UrlRule.php @@ -21,10 +21,12 @@ public function init() '{relationship}' => '' ])); $this->patterns = array_merge($this->patterns, [ + 'POST' => 'create', 'DELETE {id}/relationships/{relationship}' => 'delete-relationship', 'POST,PATCH {id}/relationships/{relationship}' => 'update-relationship', - 'GET {id}/{relationship}' => 'view-related', - '{id}/{relationship}' => 'options' + 'HEAD,GET {id}/{relationship}' => 'view-related', + '{id}/{relationship}' => 'options', + 'HEAD,GET {id}/relationships/{relationship}' => 'view-relationship', ]); parent::init(); } diff --git a/src/actions/ViewRelationshipAction.php b/src/actions/ViewRelationshipAction.php new file mode 100644 index 0000000..2308a1a --- /dev/null +++ b/src/actions/ViewRelationshipAction.php @@ -0,0 +1,52 @@ +findModel($id); + + if (!$related = $model->getRelation($name, false)) { + throw new NotFoundHttpException('Relationship does not exist'); + } + + if ($this->checkAccess) { + call_user_func($this->checkAccess, $this->id, $model, $name); + } + + $relationship = new Relationship([ + 'multiple' => $related->multiple + ]); + + if ($related->multiple) { + return $relationship->addRelated($related->all()); + } + + return $relationship->addRelated($related->one()); + } +} \ No newline at end of file diff --git a/tests/SerializerTest.php b/tests/SerializerTest.php index eba491d..b0cd1a7 100644 --- a/tests/SerializerTest.php +++ b/tests/SerializerTest.php @@ -6,6 +6,7 @@ use tuyakhov\jsonapi\tests\data\ResourceModel; use tuyakhov\jsonapi\Serializer; +use tuyakhov\jsonapi\Relationship; use yii\base\InvalidValueException; use yii\data\ArrayDataProvider; @@ -490,4 +491,65 @@ public function testTypeInflection() ] ], $serializer->serialize($model)); } + + public function testSerializeRelationshipMultipleEmpty() + { + $serializer = new Serializer(); + $relationship = new Relationship([ + 'multiple' => true + ]); + $response = $serializer->serialize($relationship); + $this->assertSame([ + 'data' => [] + ], $response); + } + + public function testSerializeRelationshipMultipleSuccess() + { + $serializer = new Serializer(); + $models = [ + new ResourceModel(), + ]; + $relationship = new Relationship([ + 'multiple' => true, + 'relations' => $models + ]); + $response = $serializer->serialize($relationship); + $this->assertSame([ + 'data' => [ + [ + 'id' => '123', + 'type' => 'resource-models' + ] + ] + ], $response); + } + + public function testSerializeRelationshipSingleEmpty() + { + $serializer = new Serializer(); + $relationship = new Relationship([]); + $response = $serializer->serialize($relationship); + $this->assertSame([ + 'data' => null + ], $response); + } + + public function testSerializeRelationshipSingleSuccess() + { + $serializer = new Serializer(); + $models = [ + new ResourceModel(), + ]; + $relationship = new Relationship([ + 'relations' => $models + ]); + $response = $serializer->serialize($relationship); + $this->assertSame([ + 'data' => [ + 'id' => '123', + 'type' => 'resource-models' + ] + ], $response); + } } diff --git a/tests/actions/ViewRelationshipActionTest.php b/tests/actions/ViewRelationshipActionTest.php new file mode 100644 index 0000000..acb44b1 --- /dev/null +++ b/tests/actions/ViewRelationshipActionTest.php @@ -0,0 +1,56 @@ + + */ + +namespace tuyakhov\jsonapi\tests\actions; + +use tuyakhov\jsonapi\actions\ViewRelationshipAction; +use tuyakhov\jsonapi\tests\data\ActiveQuery; +use tuyakhov\jsonapi\tests\data\ResourceModel; +use tuyakhov\jsonapi\tests\TestCase; +use yii\data\ActiveDataProvider; +use yii\web\Controller; +use yii\web\ForbiddenHttpException; +use tuyakhov\jsonapi\Relationship; + +class ViewRelationshipActionTest extends TestCase +{ + public function testMultipleSuccess() + { + $model = new ResourceModel(); + $action = new ViewRelationshipAction('test', new Controller('test', \Yii::$app), [ + 'modelClass' => ResourceModel::className() + ]); + ResourceModel::$related = [ + 'extraField1' => new ActiveQuery(ResourceModel::className(), ['multiple' => true]), + ]; + $action->findModel = function ($id, $action) use($model) { + return $model; + }; + $model->extraField1 = [new ResourceModel()]; + $this->assertInstanceOf(Relationship::className(), $relationship = $action->run(1, 'extraField1')); + $this->assertTrue($relationship->multiple); + $this->assertCount(2, $relationship->relations); + $this->assertInstanceOf(ResourceModel::className(), $relationship->relations[0]); + } + + public function testSingleSuccess() + { + $model = new ResourceModel(); + $action = new ViewRelationshipAction('test', new Controller('test', \Yii::$app), [ + 'modelClass' => ResourceModel::className() + ]); + ResourceModel::$related = [ + 'extraField1' => new ActiveQuery(ResourceModel::className()), + ]; + $action->findModel = function ($id, $action) use($model) { + return $model; + }; + $model->extraField1 = new ResourceModel(); + $this->assertInstanceOf(Relationship::className(), $relationship = $action->run(1, 'extraField1')); + $this->assertCount(1, $relationship->relations); + $this->assertInstanceOf(ResourceModel::className(), $relationship->relations[0]); + } + +} \ No newline at end of file