diff --git a/src/actions/CreateAction.php b/src/actions/CreateAction.php index 3ac00c9..6f965f9 100644 --- a/src/actions/CreateAction.php +++ b/src/actions/CreateAction.php @@ -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; @@ -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; @@ -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()) { diff --git a/src/actions/DeleteAction.php b/src/actions/DeleteAction.php index d9341a3..f408491 100644 --- a/src/actions/DeleteAction.php +++ b/src/actions/DeleteAction.php @@ -10,6 +10,7 @@ use Closure; use Yii; use yii\base\Model; +use yii\base\InvalidConfigException; use yii\web\ForbiddenHttpException; use yii\web\ServerErrorHttpException; @@ -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; @@ -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.'); } diff --git a/src/actions/UpdateAction.php b/src/actions/UpdateAction.php index 528daf1..f0517cd 100644 --- a/src/actions/UpdateAction.php +++ b/src/actions/UpdateAction.php @@ -15,6 +15,7 @@ use Yii; use yii\base\Model; use yii\db\ActiveRecord; +use yii\base\InvalidConfigException; use yii\web\ServerErrorHttpException; /** @@ -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; /** @@ -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()) {