Yii Framework Forum: Rest Api - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Rest Api REST parameter validation Rate Topic: -----

#1 User is offline   Aruna Attanayake 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 116
  • Joined: 08-September 11
  • Location:Colombo - Sri Lanka

Posted 11 July 2014 - 09:49 PM

class UserController extends ApiController
{
	public function actionCreate()
	{
		$model = new User();
		$model->scenario = 'create';
		$model->attributes = $this->params;
		$model->createdAt = date('Y-m-d H:i:s');
		$model->createdById = 1;
		if ($model->validate()) {
			try {
				$model->save();
				$statusCode = RestServer::SUCCESS;
			} catch (Exception $e) {
				$statusCode = RestServer::USER_CREATE_FAILED;
			}
		} else {
			$errors = $model->errors;
			$error = current($errors);
			$statusCode = RestServer::VALIDATION_FAILED;
			$statusMessage = sprintf($this->statusMessages[RestServer::VALIDATION_FAILED], $error[0]);
		}
		
		$this->sendResponse($statusCode, $statusMessage);
	}
}


Above is a sample REST API code written to creating a user. If any validation error I`ll send first error in the $model->errors object. Response is like "{"statusCode":100,"statusMessage":"Validation failed. Reason:First Name cannot be blank."}".
Here statusCode is same for any validation error but statusMessage is changed according to the Yii error message.

Now I want to send separate error codes to each validation error. Ex: first name required - 100, last name required - 101 etc...

Is there any proper way to achieve this rather than checking them individually? Can we bind error code to the rules without interrupting to the web flow.

Thanks,
Aruna
Extensions:
SFTP

PageSize

Dashboard
0

#2 User is offline   Joblo 

  • Master Member
  • PipPipPipPip
  • Yii
  • Group: Members
  • Posts: 685
  • Joined: 12-September 10
  • Location:Austria

Posted 13 July 2014 - 03:36 AM

You can create a integer error code as the sum of two parts:

1. The type of the validator (CRequiredValidator -> required, CStringValidator -> length ...)

So you have to map the validator classes to a number like
CRequiredValidator => 100
CStringValidator => 200
...
You can use the array of CValidator::$builtInValidators to generate the validator code.

2. the attribute (firstname, lastname ...)
The integer value can be the index+1 of the errorattribute in the models array attributeNames().
firstname -> 1
lastname -> 2

You easily can get the index of the errorattribute, because the errors are assigned for each attribute.
But the problem is, that you don't know which type of validator did add the errormessage.

So you have to override the validate() and the addError() method.

Create a 'ApiBaseModel' (User extends ApiBaseModel) or alternative a 'ApiHelper' class with static methods or a 'ApiBehavior'.


class ApiBaseModel extends CActiveRecord 
{
   protected $_apiErrors=array();
   private $_currentValidator;


   public function getApiErrors() 
   {
      return $this->_apiErrors;
   }    


   public function getValidatorApiCode($validator) 
   {
        $classes = array_values(CValidator::$builtInValidators);
        $idx = array_search(get_class($validator), $classes);
        return $idx !== false ? ($idx+1) * 100 : 10000; //= custom validators           
   }      


   public function getAttributeApiCode($attribute) 
   {
        $names = $this->attributeNames();
        return array_search($attribute, $names);           
   }   


        //the only difference: assign the _currentValidator   
        public function validate($attributes=null, $clearErrors=true)
	{
		$this->_currentValidator=null;
                if($clearErrors)
			$this->clearErrors();
		if($this->beforeValidate())
		{
			foreach($this->getValidators() as $validator)
                        {
                                $this->_currentValidator=$validator; //<---- set the current validator
				$validator->validate($this,$attributes);
                        }   
			$this->afterValidate();
			return !$this->hasErrors();
		}
		else
			return false;
	}  


        //assign the apiCode with the error message to the _apiErrors   
        public function addError($attribute,$error)
	{
            $apiCode = $this->getValidatorApiCode($this->_currentValidator) + $this->getValidatorApiCode($this->_currentValidator);
            if(!is_array($this->_apiErrors[$apiCode])
                $this->_apiErrors[$apiCode]=array();
            $this->_apiErrors[$apiCode][]=$error;
             
            parent::addError($attribute,$error);
	}
      
}  




0

#3 User is offline   Aruna Attanayake 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 116
  • Joined: 08-September 11
  • Location:Colombo - Sri Lanka

Posted 13 July 2014 - 09:42 AM

View PostJoblo, on 13 July 2014 - 03:36 AM, said:

You can create a integer error code as the sum of two parts:

1. The type of the validator (CRequiredValidator -> required, CStringValidator -> length ...)

So you have to map the validator classes to a number like
CRequiredValidator => 100
CStringValidator => 200
...
You can use the array of CValidator::$builtInValidators to generate the validator code.

2. the attribute (firstname, lastname ...)
The integer value can be the index+1 of the errorattribute in the models array attributeNames().
firstname -> 1
lastname -> 2

You easily can get the index of the errorattribute, because the errors are assigned for each attribute.
But the problem is, that you don't know which type of validator did add the errormessage.

So you have to override the validate() and the addError() method.

Create a 'ApiBaseModel' (User extends ApiBaseModel) or alternative a 'ApiHelper' class with static methods or a 'ApiBehavior'.


class ApiBaseModel extends CActiveRecord 
{
   protected $_apiErrors=array();
   private $_currentValidator;


   public function getApiErrors() 
   {
      return $this->_apiErrors;
   }    


   public function getValidatorApiCode($validator) 
   {
        $classes = array_values(CValidator::$builtInValidators);
        $idx = array_search(get_class($validator), $classes);
        return $idx !== false ? ($idx+1) * 100 : 10000; //= custom validators           
   }      


   public function getAttributeApiCode($attribute) 
   {
        $names = $this->attributeNames();
        return array_search($attribute, $names);           
   }   


        //the only difference: assign the _currentValidator   
        public function validate($attributes=null, $clearErrors=true)
	{
		$this->_currentValidator=null;
                if($clearErrors)
			$this->clearErrors();
		if($this->beforeValidate())
		{
			foreach($this->getValidators() as $validator)
                        {
                                $this->_currentValidator=$validator; //<---- set the current validator
				$validator->validate($this,$attributes);
                        }   
			$this->afterValidate();
			return !$this->hasErrors();
		}
		else
			return false;
	}  


        //assign the apiCode with the error message to the _apiErrors   
        public function addError($attribute,$error)
	{
            $apiCode = $this->getValidatorApiCode($this->_currentValidator) + $this->getValidatorApiCode($this->_currentValidator);
            if(!is_array($this->_apiErrors[$apiCode])
                $this->_apiErrors[$apiCode]=array();
            $this->_apiErrors[$apiCode][]=$error;
             
            parent::addError($attribute,$error);
	}
      
}  






Hi Jobio,

Thank you for the solution. I just wanted to get an error code for validation instead of a error message the I can directly send that error code to REST request. Anyway I achieved it another way.


	// Model rules
	public function rules()
	{
		// NOTE: you should only define rules for those attributes that
		// will receive user inputs.
		$rules = array(
			array('firstName, lastName', 'required', 'on'=>'create'),
		);
		
		if ($this->isApi) {
			$rules = array(
				array('firstName', 'required', 'message'=>100, 'on'=>'create'),
				array('lastName', 'required', 'message'=>101, 'on'=>'create'),
			);
		}
		
		return $rules;
	}
	
	// Controller action
	public function actionCreate()
	{
		$model = new User();
		$model->scenario = 'create';
		$model->isApi = true; // Saying that action related to API call and rules prepared according to it
		$model->attributes = $this->params;
		if ($model->validate()) {
			try {
				$model->save();
				$statusCode = RestServer::SUCCESS;
			} catch (Exception $e) {
				$statusCode = RestServer::FAILED;
			}
		} else {
			// Getting the first error of the $model->errors, So it will be error code
			$errors = $model->errors;
			$error = current($errors);
			$statusCode = $error[0];
		}
		
		$this->sendResponse($statusCode);// Sending REST response via another function.
	}


Thanks,
Aruna
Extensions:
SFTP

PageSize

Dashboard
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users