From 729c52a4f7e5cff1a11328d4961f91c7c350c676 Mon Sep 17 00:00:00 2001 From: Salem Ouerdani Date: Sun, 23 Aug 2020 17:07:52 +0100 Subject: [PATCH] [doc] Update README file --- README.md | 94 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 9c2c600..31d3183 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Adds nested resources routing support along with related actions and relationship handlers to the [Yii RESTful API framework](http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html). ## How It Works + This extension doesn't replace any of the built-in REST components. It is about a collection of helper actions and a custom `UrlRule` class designed to be used along with the default one: ```php @@ -29,10 +30,10 @@ This extension doesn't replace any of the built-in REST components. It is about ] ``` -To explain how it works, lets better go through an example: +To explain how it works, lets better go through an example: + +If within the previous configurations we expect `team` and `player` to share a _one-to-many_ relationship while `player` and `skill` shares a _many-to-many_ relation within a junction table and having an extra column called `level` in that junction table then this extension may help achieving the following HTTP requests: -If within the previous configurations we expect `team` and `player` to share a *one-to-many* relationship while `player` and `skill` shares a *many-to-many* relation within a junction table and having an extra column called `level` in that junction table then this extension may help achieving the following HTTP requests: - ```bash # get the players 2, 3 and 4 from team 1 GET /teams/1/players/2,3,4 @@ -81,13 +82,12 @@ or add to the `require` section of your `composer.json` file. - - ## Configuration -By default, all the properties used by the custom UrlRule class in this extension will be used to generate multiple instances of the built-in [yii\rest\UrlRule](http://www.yiiframework.com/doc-2.0/yii-rest-urlrule.html) so basically both classes are sharing similar configurations. +By default, all the properties used by the custom UrlRule class in this extension will be used to generate multiple instances of the built-in [yii\rest\UrlRule](http://www.yiiframework.com/doc-2.0/yii-rest-urlrule.html) so basically both classes are sharing similar configurations. Those are all the possible configurations that may be set to the UrlManager in the app config file: + ```php 'rules' => [ [ @@ -116,7 +116,7 @@ Those are all the possible configurations that may be set to the UrlManager in t */ 'linkAttribute' => 'player_id', /* optional */ /** - * building related rules using 'controller => ['teams' => 'v1/team']' + * building related rules using 'controller => ['teams' => 'v1/team']' * instead of 'controller => ['team']' */ 'modulePrefix' => 'v1', /* optional */ @@ -127,6 +127,30 @@ Those are all the possible configurations that may be set to the UrlManager in t '{id}' => '', '{IDs}' => '', ], + /** + * The Regular Expressions Syntax used to parse the id of the main resource from url. + * For example, in the following final rule, $linkAttributePattern is default to that `\d+` to parse $brand_id value: + * + * GET,HEAD v1/brands//items/ + * + * While that works fine with digital IDs, in a system using a different format, like uuid for example, + * you may use $linkAttributePattern to define different patterns. Something like this maybe: + * + * [ + * // Nested Rules Brand + * 'class' => 'tunecino\nestedrest\UrlRule', + * 'modelClass' => 'app\modules\v1\models\Brand', + * 'modulePrefix' => 'v1', + * 'resourceName' => 'v1/brands', + * 'relations' => ['items'], + * 'tokens' => [ + * '{id}' => '', + * '{IDs}' => '', + * ], + * 'linkAttributePattern' => '[a-f0-9]{8}\\-[a-f0-9]{4}\\-4[a-f0-9]{3}\\-(8|9|a|b)[a-f0-9]{3}\\-[a-f0-9]{12}', + * ], + */ + 'linkAttributePattern' => '\d+', /* optional */ /** * the default list of patterns. they may all be overridden here * or just edited within $only, $except and $extraPatterns properties @@ -156,15 +180,16 @@ Those are all the possible configurations that may be set to the UrlManager in t ], ] ``` -As you may notice; by default; `$patterns` is pointing to 6 new actions different from the basic CRUD actions attached to the [ActiveController](http://www.yiiframework.com/doc-2.0/yii-rest-activecontroller.html) class. Those are the helper actions included in this extension and you will need to manually declare them whenever needed inside your controllers or inside a `BaseController` from which all others should extend. Also note that by default we are expecting an [OptionsAction](http://www.yiiframework.com/doc-2.0/yii-rest-optionsaction.html) attached to the related controller. That should be the case for any controller extending [ActiveController](http://www.yiiframework.com/doc-2.0/yii-rest-activecontroller.html) or its child controllers. Otherwise, you should also implement `\yii\rest\OptionsAction`. + +As you may notice; by default; `$patterns` is pointing to 6 new actions different from the basic CRUD actions attached to the [ActiveController](http://www.yiiframework.com/doc-2.0/yii-rest-activecontroller.html) class. Those are the helper actions included in this extension and you will need to manually declare them whenever needed inside your controllers or inside a `BaseController` from which all others should extend. Also note that by default we are expecting an [OptionsAction](http://www.yiiframework.com/doc-2.0/yii-rest-optionsaction.html) attached to the related controller. That should be the case for any controller extending [ActiveController](http://www.yiiframework.com/doc-2.0/yii-rest-activecontroller.html) or its child controllers. Otherwise, you should also implement `\yii\rest\OptionsAction`. The following is an example of a full implementation within the [controller::actions()](http://www.yiiframework.com/doc-2.0/yii-rest-activecontroller.html#actions%28%29-detail) function: ```php -public function actions() +public function actions() { - $actions = parent::actions(); - + $actions = parent::actions(); + $actions['nested-index'] = [ 'class' => 'tunecino\nestedrest\IndexAction', /* required */ 'modelClass' => $this->modelClass, /* required */ @@ -186,7 +211,7 @@ public function actions() */ 'scenario' => 'default', /* optional */ /** - * the scenario to be assigned to the model class responsible + * the scenario to be assigned to the model class responsible * of handling the data stored in the juction table. */ 'viaScenario' => 'default', /* optional */ @@ -204,7 +229,7 @@ public function actions() 'modelClass' => $this->modelClass, /* required */ 'checkAccess' => [$this, 'checkAccess'], /* optional */ /** - * the scenario to be assigned to the model class responsible + * the scenario to be assigned to the model class responsible * of handling the data stored in the juction table. */ 'viaScenario' => 'default', /* optional */ @@ -221,19 +246,19 @@ public function actions() 'modelClass' => $this->modelClass, /* required */ 'checkAccess' => [$this, 'checkAccess'], /* optional */ ]; - + return $actions; } ``` ## What you need to know -***1.*** This doesn't support composite keys. In fact one of my main concerns when building this extension was to figure out a clean alternative to not have to build resources for composite keys related models like the ones mapping a junction table. check the example provided in section ***8.*** for more details. +**_1._** This doesn't support composite keys. In fact one of my main concerns when building this extension was to figure out a clean alternative to not have to build resources for composite keys related models like the ones mapping a junction table. check the example provided in section **_8._** for more details. -***2.*** When defining relation names in the config file they should match the method names implemented inside your model *(see [Declaring Relations](http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#declaring-relations) section in the Yii guide for more details)*. -This extension will do the check and will throw an *InvalidConfigException* if they don't match but for performance reasons *(check [this](http://www.yiiframework.com/doc-2.0/guide-runtime-routing.html#performance-consideration))* and because it make no sense to keep doing the same verification with each request when you already did correctly set a list of relations, this extension won't do that DB schema parsing anymore when the application is in *production* mode. in other words verification is made only when`YII_DEBUG` is true. +**_2._** When defining relation names in the config file they should match the method names implemented inside your model _(see [Declaring Relations](http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#declaring-relations) section in the Yii guide for more details)_. +This extension will do the check and will throw an _InvalidConfigException_ if they don't match but for performance reasons _(check [this](http://www.yiiframework.com/doc-2.0/guide-runtime-routing.html#performance-consideration))_ and because it make no sense to keep doing the same verification with each request when you already did correctly set a list of relations, this extension won't do that DB schema parsing anymore when the application is in _production_ mode. in other words verification is made only when`YII_DEBUG` is true. -***3.*** By default, when you specify a relation 'abc' in the `$relation` property, its related name expected to be used in the URL endpoint should be 'abcs' (pluralized) while its controller is expected to be `AbcController`. This can be changed by configuring the `$relation` property to explicitly specify how to map the relation name used in endpoint URLs to its related controller ID. +**_3._** By default, when you specify a relation 'abc' in the `$relation` property, its related name expected to be used in the URL endpoint should be 'abcs' (pluralized) while its controller is expected to be `AbcController`. This can be changed by configuring the `$relation` property to explicitly specify how to map the relation name used in endpoint URLs to its related controller ID. For example, if we had a relation defined inside the `Team` model class within a `getJuniorCoaches()` method we can do the following: ```php @@ -253,33 +278,35 @@ For example, if we had a relation defined inside the `Team` model class within a ] ``` -***4.*** When it comes to linking *many-to-many* relations with extra columns in a junction table it is highly recommended to use [via()](http://www.yiiframework.com/doc-2.0/yii-db-activerelationtrait.html#via%28%29-detail) instead of [viaTable()](http://www.yiiframework.com/doc-2.0/yii-db-activequery.html#viaTable%28%29-detail) so the intermediate class can be used by this extension to validate related attributes instead of using [link()](http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#link%28%29-detail) and saving data without performing the appropriate validations. Refer to the [Relations via a Junction Table](http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#junction-table) section in the Yii guide for more details. +**_4._** When it comes to linking _many-to-many_ relations with extra columns in a junction table it is highly recommended to use [via()](http://www.yiiframework.com/doc-2.0/yii-db-activerelationtrait.html#via%28%29-detail) instead of [viaTable()](http://www.yiiframework.com/doc-2.0/yii-db-activequery.html#viaTable%28%29-detail) so the intermediate class can be used by this extension to validate related attributes instead of using [link()](http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#link%28%29-detail) and saving data without performing the appropriate validations. Refer to the [Relations via a Junction Table](http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#junction-table) section in the Yii guide for more details. + +**_5._** When you do: -***5.*** When you do: ```bash POST /players/9/skills {name: 'dribble', level: 10} ``` -and the 'name' attribute is supposed to be loaded and saved along with the new created model while 'level' should be added in a related junction table. Then you should know this: - - If relation between both models is defined within [via()](http://www.yiiframework.com/doc-2.0/yii-db-activerelationtrait.html#via%28%29-detail) , `Yii::$app->request->bodyParams` will be populated to to both models using the [load()](http://www.yiiframework.com/doc-2.0/yii-base-model.html#load()-detail) method: - - ```php - $model->load($bodyParams); - $viaModel->load($bodyParams); - /* Scenarios can also be assigned to both models. when attaching actions. see configuration section */ - ``` +and the 'name' attribute is supposed to be loaded and saved along with the new created model while 'level' should be added in a related junction table. Then you should know this: + +- If relation between both models is defined within [via()](http://www.yiiframework.com/doc-2.0/yii-db-activerelationtrait.html#via%28%29-detail) , `Yii::$app->request->bodyParams` will be populated to to both models using the [load()]() method: - - If relation is defined within [viaTable()](http://www.yiiframework.com/doc-2.0/yii-db-activequery.html#viaTable%28%29-detail) instead the script will try to do some guessing. + ```php + $model->load($bodyParams); + $viaModel->load($bodyParams); + /* Scenarios can also be assigned to both models. when attaching actions. see configuration section */ + ``` + +- If relation is defined within [viaTable()](http://www.yiiframework.com/doc-2.0/yii-db-activequery.html#viaTable%28%29-detail) instead the script will try to do some guessing. So when unexpected results happens or when attribute names are similar in model class and junction related class, it would be recommended to set the `viaWrapper` property. See the 'nested-create' action in the [configuration](#configuration) section for more details. -***6.*** When unlinking data, if the relation type between both models is *many_to_many* related row in the junction table will be removed. Otherwise the concerned foreign key attribute will be set to NULL in its related column in database. +**_6._** When unlinking data, if the relation type between both models is _many_to_many_ related row in the junction table will be removed. Otherwise the concerned foreign key attribute will be set to NULL in its related column in database. -***7.*** When a successful linking or unlinking request happens, a `204` response should be expected while a `304` response should tell that no change has been made like when asking to link two already linked models. +**_7._** When a successful linking or unlinking request happens, a `204` response should be expected while a `304` response should tell that no change has been made like when asking to link two already linked models. When you try to link 2 models sharing a `many_to_many` relationship and both models are already linked no extra row will be added to related junction table: If the `bodyRequest` is empty you'll get a `304` response otherwise the `bodyRequest` content will be used to update the extra attributes found in the junction table and you'll get a `204` headers response. -***8.*** When performing any HTTP request; lets say as example `GET /players/9/skills/2`; The custom `UrlRule` will redirect it by default to the route `skill/nested-view` *(or other depending on your patterns)* with those 4 extra attributes added to `Yii::$app->request->queryParams`: +**_8._** When performing any HTTP request; lets say as example `GET /players/9/skills/2`; The custom `UrlRule` will redirect it by default to the route `skill/nested-view` _(or other depending on your patterns)_ with those 4 extra attributes added to `Yii::$app->request->queryParams`: ```php relativeClass = 'app/models/player'; // the class name of the relative model @@ -287,6 +314,7 @@ relationName = 'skills'; // the one you did set in rules configuration. linkAttribute = 'player_id'; // the foreign key attribute name. player_id = 9; // the foreign key attribute and its value ``` + Those may be useful when building your own actions or doing extra things like for example if we add the following inside `app/models/skill` : ```php @@ -331,4 +359,4 @@ GET /players/9/skills/2 "level": 11 } } -``` \ No newline at end of file +```