Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

Commit

Permalink
support custom parameter naming for model ID
Browse files Browse the repository at this point in the history
  • Loading branch information
cebe committed Jun 26, 2019
1 parent 665b773 commit 14955c3
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 27 deletions.
6 changes: 6 additions & 0 deletions src/generator/ApiGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ protected function generateUrls()
$action = [];
$params = false;
$actionParams = [];
$idParam = null;
foreach ($parts as $p => $part) {
if (preg_match('/\{(.*)\}/', $part, $m)) {
$params = true;
Expand All @@ -358,6 +359,9 @@ protected function generateUrls()
} else {
$actionParams[$m[1]] = null;
}
if ($idParam === null && preg_match('/\bid\b/i', Inflector::camel2id($m[1]))) {
$idParam = $m[1];
}
// TODO add regex to param based on openAPI type
} elseif ($params) {
$action[] = $part;
Expand Down Expand Up @@ -395,6 +399,7 @@ protected function generateUrls()
'pattern' => $pattern,
'route' => "$controller/$a$action",
'actionParams' => $actionParams,
'idParam' => $idParam,
'openApiOperation' => $operation,
'modelClass' => $modelClass !== null ? $this->modelNamespace . '\\' . $modelClass : null,
'responseWrapper' => $responseWrapper,
Expand Down Expand Up @@ -566,6 +571,7 @@ protected function generateControllers()
$c[$parts[0]][] = [
'id' => $parts[1],
'params' => array_keys($url['actionParams']),
'idParam' => $url['idParam'] ?? null,
'modelClass' => $url['modelClass'],
'responseWrapper' => $url['responseWrapper'],
];
Expand Down
83 changes: 73 additions & 10 deletions src/generator/default/controller.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
<?php echo '<?php';

$modelActions = [
'index' => yii\rest\IndexAction::class,
'view' => yii\rest\ViewAction::class,
'create' => yii\rest\CreateAction::class,
'update' => yii\rest\UpdateAction::class,
'delete' => yii\rest\UpdateAction::class,
'index' => [
'class' => yii\rest\IndexAction::class,
],
'view' => [
'class' => yii\rest\ViewAction::class,
'implementation' => <<<'PHP'
$model = $this->findModel($id);
$this->checkAccess(ACTION_ID, $model);
return $model;
PHP
],
'create' => [
'class' => yii\rest\CreateAction::class,
],
'update' => [
'class' => yii\rest\UpdateAction::class,
'implementation' => <<<'PHP'
$model = $this->findModel($id);
$this->checkAccess(ACTION_ID, $model);
$model->load(Yii::$app->getRequest()->getBodyParams(), '');
if ($model->save() === false && !$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to update the object for unknown reason.');
}
return $model;
PHP
],
'delete' => [
'class' => yii\rest\DeleteAction::class,
'implementation' => <<<'PHP'
$model = $this->findModel($id);
$this->checkAccess(ACTION_ID, $model);
if ($model->delete() === false) {
throw new ServerErrorHttpException('Failed to delete the object for unknown reason.');
}
\Yii::$app->response->setStatusCode(204);
PHP
],
];
$findModel = [];

?>

Expand All @@ -21,9 +58,9 @@ public function actions()
<?php

foreach ($actions as $action):
if (isset($modelActions[$action['id']], $action['modelClass'])): ?>
if (isset($modelActions[$action['id']], $action['modelClass']) && ($action['idParam'] === null || $action['idParam'] === 'id')): ?>
<?= var_export($action['id'], true) ?> => [
'class' => \<?= $modelActions[$action['id']] ?>::class,
'class' => \<?= $modelActions[$action['id']]['class'] ?>::class,
'modelClass' => <?= '\\' . $action['modelClass'] . '::class' ?>,
'checkAccess' => [$this, 'checkAccess'],
],
Expand Down Expand Up @@ -84,23 +121,49 @@ public function checkAccess($action, $model = null, $params = [])
{
// TODO implement checkAccess
}

<?php
foreach ($actions as $action):
if (isset($modelActions[$action['id']], $action['modelClass'])) {
continue;
if ($action['idParam'] === null || $action['idParam'] === 'id') {
continue;
}
if (isset($modelActions[$action['id']]['implementation'])) {
$implementation = $modelActions[$action['id']]['implementation'];
$findModel[$action['modelClass']] = 'find' . \yii\helpers\StringHelper::basename($action['modelClass']) . 'Model';
$implementation = str_replace('findModel', $findModel[$action['modelClass']], $implementation);
$implementation = str_replace('$id', '$'.$action['idParam'], $implementation);
$implementation = str_replace('ACTION_ID', var_export($action['id'], true), $implementation);
}
}

$actionName = 'action' . \yii\helpers\Inflector::id2camel($action['id']);
$actionParams = implode(', ', array_map(function ($p) {
return "\$$p";
}, $action['params']));
?>

public function <?= $actionName ?>(<?= $actionParams ?>)
{
// TODO implement <?= $actionName ?>
<?= $implementation ?? ' // TODO implement ' . $actionName ?>

}
<?php endforeach; ?>
<?php foreach($findModel as $modelName => $methodName): ?>

/**
* Returns the <?= \yii\helpers\StringHelper::basename($modelName) ?> model based on the primary key given.
* If the data model is not found, a 404 HTTP exception will be raised.
* @param string $id the ID of the model to be loaded.
* @return \<?= $modelName ?> the model found
* @throws NotFoundHttpException if the model cannot be found.
*/
public function <?= $methodName ?>($id)
{
$model = \<?= $modelName ?>::findOne($id);
if ($model !== null) {
return $model;
}
throw new NotFoundHttpException("Object not found: $id");
}
<?php endforeach; ?>
}
39 changes: 37 additions & 2 deletions tests/specs/petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
/pets/{id}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
- name: id
in: path
required: true
description: The id of the pet to retrieve
Expand All @@ -79,6 +79,41 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
summary: update a specific pet
operationId: updatePetById
tags:
- pets
parameters:
- name: id
in: path
required: true
description: The id of the pet to update
schema:
type: string
responses:
'200':
description: The updated pet
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
delete:
summary: delete a specific pet
operationId: deletePetById
tags:
- pets
parameters:
- name: id
in: path
required: true
description: The id of the pet to delete
schema:
type: string
responses:
'204':
description: successfully deleted pet

components:
schemas:
Pet:
Expand Down
4 changes: 3 additions & 1 deletion tests/specs/petstore/config/urls.rest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
return array (
'GET pets' => 'pet/index',
'POST pets' => 'pet/create',
'GET pets/<petId>' => 'pet/view',
'GET pets/<id>' => 'pet/view',
'DELETE pets/<id>' => 'pet/delete',
'PATCH pets/<id>' => 'pet/update',
);
11 changes: 10 additions & 1 deletion tests/specs/petstore/controllers/PetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ public function actions()
'modelClass' => \app\models\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'delete' => [
'class' => \yii\rest\DeleteAction::class,
'modelClass' => \app\models\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'update' => [
'class' => \yii\rest\UpdateAction::class,
'modelClass' => \app\models\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'options' => [
'class' => \yii\rest\OptionsAction::class,
],
Expand All @@ -44,5 +54,4 @@ public function checkAccess($action, $model = null, $params = [])
{
// TODO implement checkAccess
}

}
36 changes: 35 additions & 1 deletion tests/specs/petstore_arrayref.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ paths:
schema:
type: string
content:
application/json:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
Expand Down Expand Up @@ -75,6 +75,40 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
patch:
summary: update a specific pet
operationId: updatePetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to update
schema:
type: string
responses:
'200':
description: The updated pet
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
delete:
summary: delete a specific pet
operationId: deletePetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to delete
schema:
type: string
responses:
'204':
description: successfully deleted pet
components:
schemas:
Pet:
Expand Down
2 changes: 2 additions & 0 deletions tests/specs/petstore_arrayref/config/urls.rest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
'GET pets' => 'pet/index',
'POST pets' => 'pet/create',
'GET pets/<petId>' => 'pet/view',
'DELETE pets/<petId>' => 'pet/delete',
'PATCH pets/<petId>' => 'pet/update',
);
52 changes: 47 additions & 5 deletions tests/specs/petstore_arrayref/controllers/PetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ public function actions()
'modelClass' => \app\models\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'view' => [
'class' => \yii\rest\ViewAction::class,
'modelClass' => \app\models\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'options' => [
'class' => \yii\rest\OptionsAction::class,
],
Expand All @@ -45,4 +40,51 @@ public function checkAccess($action, $model = null, $params = [])
// TODO implement checkAccess
}

public function actionView($petId)
{
$model = $this->findPetModel($petId);
$this->checkAccess('view', $model);
return $model;
}

public function actionDelete($petId)
{
$model = $this->findPetModel($petId);
$this->checkAccess('delete', $model);

if ($model->delete() === false) {
throw new ServerErrorHttpException('Failed to delete the object for unknown reason.');
}

\Yii::$app->response->setStatusCode(204);
}

public function actionUpdate($petId)
{
$model = $this->findPetModel($petId);
$this->checkAccess('update', $model);

$model->load(Yii::$app->getRequest()->getBodyParams(), '');
if ($model->save() === false && !$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to update the object for unknown reason.');
}

return $model;
}

/**
* Returns the Pet model based on the primary key given.
* If the data model is not found, a 404 HTTP exception will be raised.
* @param string $id the ID of the model to be loaded.
* @return \app\models\Pet the model found
* @throws NotFoundHttpException if the model cannot be found.
*/
public function findPetModel($id)
{
$model = \app\models\Pet::findOne($id);
if ($model !== null) {
return $model;
}
throw new NotFoundHttpException("Object not found: $id");
}
}
4 changes: 3 additions & 1 deletion tests/specs/petstore_namespace/config/rest-urls.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
return array (
'GET pets' => 'pet/index',
'POST pets' => 'pet/create',
'GET pets/<petId>' => 'pet/view',
'GET pets/<id>' => 'pet/view',
'DELETE pets/<id>' => 'pet/delete',
'PATCH pets/<id>' => 'pet/update',
);
11 changes: 10 additions & 1 deletion tests/specs/petstore_namespace/mycontrollers/PetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ public function actions()
'modelClass' => \app\mymodels\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'delete' => [
'class' => \yii\rest\DeleteAction::class,
'modelClass' => \app\mymodels\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'update' => [
'class' => \yii\rest\UpdateAction::class,
'modelClass' => \app\mymodels\Pet::class,
'checkAccess' => [$this, 'checkAccess'],
],
'options' => [
'class' => \yii\rest\OptionsAction::class,
],
Expand All @@ -44,5 +54,4 @@ public function checkAccess($action, $model = null, $params = [])
{
// TODO implement checkAccess
}

}
Loading

0 comments on commit 14955c3

Please sign in to comment.