Skip to content

Commit

Permalink
Merge pull request #9 from siggi-k/callback-scenario
Browse files Browse the repository at this point in the history
callable scenarios for actions: create, update, delete
  • Loading branch information
cebe authored Dec 18, 2024
2 parents 134701e + 8ee5446 commit 83eac62
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 18 deletions.
50 changes: 36 additions & 14 deletions src/actions/CreateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use yii\base\Model;
use yii\db\ActiveRecordInterface;
use yii\helpers\Url;
use yii\base\InvalidConfigException;
use yii\web\ServerErrorHttpException;
use function array_keys;
use function call_user_func;
Expand All @@ -29,27 +30,40 @@ class CreateAction extends JsonApiAction
{
use HasResourceTransformer;
use HasParentAttributes;

/**
* @var array
* * Configuration for attaching relationships
* Configuration for attaching relationships
* Should contains key - relation name and array with
* idType - php type of resource ids for validation
* validator = callback for custom id validation
* Keep it empty for disable this ability
* @see https://jsonapi.org/format/#crud-creating
* @example
* 'allowedRelations' => [
* 'author' => ['idType' => 'integer'],
* 'photos' => ['idType' => 'integer', 'validator' => function($model, array $ids) {
* $relatedModels = Relation::find()->where(['id' => $ids])->andWhere([additional conditions])->all();
* if(count($relatedModels) < $ids) {
* throw new HttpException(422, 'Invalid photos ids');
* }],
* ]
**/
* 'allowedRelations' => [
* 'author' => ['idType' => 'integer'],
* 'photos' => ['idType' => 'integer', 'validator' => function($model, array $ids) {
* $relatedModels = Relation::find()->where(['id' => $ids])->andWhere([additional conditions])->all();
* if (count($relatedModels) < $ids) {
* throw new HttpException(422, 'Invalid photos ids');
* }
* },
* ]
*/

public $allowedRelations = [];

/**
* @var string the scenario to be assigned to the new model before it is validated and saved.
* @var string|callable
* string - the scenario to be assigned to the model before it is validated and saved.
* callable - a PHP callable that will be executed during the action.
* It must return a string representing the scenario to be assigned to the model before it is validated and saved.
* The signature of the callable should be as follows,
* ```php
* function ($action, $model) {
* // $model is the requested model instance.
* }
* ```
*/
public $scenario = Model::SCENARIO_DEFAULT;

Expand Down Expand Up @@ -97,9 +111,17 @@ public function run()
}

/* @var $model \yii\db\ActiveRecord */
$model = new $this->modelClass([
'scenario' => $this->scenario,
]);
$model = new $this->modelClass();

if (is_string($this->scenario)) {
$scenario = $this->scenario;
} elseif (is_callable($this->scenario)) {
$scenario = call_user_func($this->scenario, $this->id, $model);
} else {
throw new InvalidConfigException('The "scenario" property must be defined either as a string or as a callable.');
}
$model->setScenario($scenario);

RelationshipManager::validateRelationships($model, $this->getResourceRelationships(), $this->allowedRelations);
$model->load($this->getResourceAttributes(), '');
if ($this->isParentRestrictionRequired()) {
Expand Down
24 changes: 22 additions & 2 deletions src/actions/DeleteAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Closure;
use Yii;
use yii\base\Model;
use yii\base\InvalidConfigException;
use yii\web\ForbiddenHttpException;
use yii\web\ServerErrorHttpException;

Expand All @@ -23,7 +24,16 @@ class DeleteAction extends JsonApiAction
use HasParentAttributes;

/**
* @var string the scenario to be assigned to the new model before it is validated and saved.
* @var string|callable
* string - the scenario to be assigned to the model before it is validated and saved.
* callable - a PHP callable that will be executed during the action.
* It must return a string representing the scenario to be assigned to the model before it is validated and saved.
* The signature of the callable should be as follows,
* ```php
* function ($action, $model) {
* // $model is the requested model instance.
* }
* ```
*/
public $scenario = Model::SCENARIO_DEFAULT;

Expand Down Expand Up @@ -56,10 +66,20 @@ public function run($id):void
throw new ForbiddenHttpException('Update with relationships not supported yet');
}
$model = $this->isParentRestrictionRequired() ? $this->findModelForParent($id) : $this->findModel($id);
$model->setScenario($this->scenario);

if (is_string($this->scenario)) {
$scenario = $this->scenario;
} elseif (is_callable($this->scenario)) {
$scenario = call_user_func($this->scenario, $this->id, $model);
} else {
throw new InvalidConfigException('The "scenario" property must be defined either as a string or as a callable.');
}
$model->setScenario($scenario);

if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id, $model);
}

if ($model->delete() === false) {
throw new ServerErrorHttpException('Failed to delete the object for unknown reason.');
}
Expand Down
25 changes: 23 additions & 2 deletions src/actions/UpdateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Yii;
use yii\base\Model;
use yii\db\ActiveRecord;
use yii\base\InvalidConfigException;
use yii\web\ServerErrorHttpException;

/**
Expand Down Expand Up @@ -51,8 +52,18 @@ class UpdateAction extends JsonApiAction
* ]
**/
public $allowedRelations = [];

/**
* @var string the scenario to be assigned to the model before it is validated and updated.
* @var string|callable
* string - the scenario to be assigned to the model before it is validated and updated.
* callable - a PHP callable that will be executed during the action.
* It must return a string representing the scenario to be assigned to the model before it is validated and updated.
* The signature of the callable should be as follows,
* ```php
* function ($action, $model) {
* // $model is the requested model instance.
* }
* ```
*/
public $scenario = Model::SCENARIO_DEFAULT;
/**
Expand Down Expand Up @@ -88,10 +99,20 @@ public function run($id):Item
{
/* @var $model ActiveRecord */
$model = $this->isParentRestrictionRequired() ? $this->findModelForParent($id) : $this->findModel($id);
$model->scenario = $this->scenario;

if (is_string($this->scenario)) {
$scenario = $this->scenario;
} elseif (is_callable($this->scenario)) {
$scenario = call_user_func($this->scenario, $this->id, $model);
} else {
throw new InvalidConfigException('The "scenario" property must be defined either as a string or as a callable.');
}
$model->setScenario($scenario);

if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id, $model);
}

$originalModel = clone $model;
RelationshipManager::validateRelationships($model, $this->getResourceRelationships(), $this->allowedRelations);
if (empty($this->getResourceAttributes()) && $this->hasResourceRelationships()) {
Expand Down

0 comments on commit 83eac62

Please sign in to comment.