Exception

Hi,

I’ve been hunting this all weekend. No success.

I’m converting my Yii 1 to a Yii 2 web application.

I have my custom exception:




namespace app\dao\user;

use yii\base\Exception;


class EmailDuplicateException extends Exception

{

}



My DAO




	public function createUser()

	{

		

		try

		{

			$sql = "INSERT INTO ";

			$command = Yii::$app->db->createCommand($sql);

			$command->bindValue(":first_name", $this->daoRequest->firstName);

			$command->bindValue(":last_name", $this->daoRequest->lastName);

			$command->bindValue(":email", $this->daoRequest->email);

			$command->bindValue(":password_hash", $this->daoRequest->passwordHash);


			$rows = $command->execute();


		}

		catch ( IntegrityException $e )

		{

			$this->exceptionLogging( $e );


			if ( strpos( $e->errorInfo[2], 'email_unq' ) !== false )

                        {

    			        throw new EmailDuplicateException( $e );

                        }

		}


	}



My Bo catches the exception. Sets a few values and returns to the controller.




 public function createUser()

    {


		try

		{


			$returnValue = $this->dao->createUser();


			return true;

		}

		catch ( EmailDuplicateException $e )

		{

		

			$this->boResponse->errors[] = Yii::t( "app", "text.error.duplicate.email" );

			$this->boResponse->focus = "firstName";

			return false;

		}

   }



My controller




public function actionNewsave()

    {


        $request = Yii::$app->request;

 

        $bo = new UserBo();

        $bo->boRequest = new UserBoRequest();

        $bo->boResponse = new UserBoResponse();

        $bo->dao = new UserDao();


        // retrieve posted data

        $request = Yii::$app->request;

        $bo->boRequest->firstName = $request->post('firstName');

        $bo->boRequest->lastName = $request->post('lastName');

        $bo->boRequest->email = $request->post('email');

        $bo->boRequest->password = $request->post('password');

        $bo->boRequest->passwordConfirmation = $request->post('passwordConfirmation');


        // create the user

        $returnValue = $bo->createUser();


        // return data

        $responseData = array();


        if ( !$returnValue )

        {

            $responseData["focus"] = $bo->boResponse->focus;

            $responseData["errors"] = $bo->boResponse->errors;

            $responseData["status"] = "fail";

        }

        else

        {

            $responseData["status"] = "success";

        }


        exit( json_encode(

			$responseData

	 ));


    }



Finally, my code in the web page is this.

The issue is that when there is an EmailDuplicateException the ERROR function is called on the AJAX call.

I do not want this, I want to handle the messaging myself in the success function.

$( ‘#saveBtn’ ).click (function() {

        var formData = 





        $.ajax({


            url: "/lescapsules/web/index.php?r=site/newsave",


            type: "POST",


            dataType: "json",


            data: {


                firstName: $( "#firstName" ).val(),


                lastName: $( "#lastName" ).val(),


                email: $( "#email" ).val(),


                password: $( "#password" ).val(),


                passwordConfirmation: $( "#passwordConfirmation" ).val(),


                _csrf: "<?=Yii::$app->request->getCsrfToken()?>"


            },


            success: function( responseData ) {


                processCall( responseData );


            },


            error: function(err){


                alert( err );


                console.log("server error");


             }


        });





       return false;


    });

I have put these statements in the index.php file. This has no effect.




defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', false);

defined('YII_ENABLE_EXCEPTION_HANDLER') or define('YII_ENABLE_EXCEPTION_HANDLER', false);



How can I get this to work the way I want it to?

Thank you, any feedback is greatly appreciated.

Glenn

Instead of exiting have you tried returning?




use yii\web\Response;


\Yii::$app->response->format = Response::FORMAT_JSON;

return $responseData;



I also noticed a few lines that will give you issues with you jQuery later on as you get further along so I’m going to try and help you avoid some of them.

You should use ‘on’ instead of ‘click’.


jQuery(document).on('click','someId_or_class', function(){})

I only say this because if you plan to ajax / pjax load content or the form the binding wont bubble up properly and thus won’t work more than once.

You are also only binding to the click to just a button. This will be problematic when a user submits the form in any other way besides clicking the button for example hitting the enter key. It should be bound to the actual form.

Not really an issue but you are grabbing the inputs manually and you don’t need to as you can use FormData.

If you use active form you won’t need to put the _csrf token as it puts it in a hidden input and it gets passed automatically when using FormData.

Here is what I’d recommend doing. Also notice it isn’t an on submit it’s onBeforeSumbit because of how Yii works you will most likely need to do this for ajax validation purposes.




jQuery(document).on('beforeSubmit', "#formId", function (e) {

    e.preventDefault();    

    var form = jQuery(this);

    jQuery.ajax({

      url: form.attr('action'),

      type: form.attr('method'),

      data: new FormData(form),

      contentType: false,

      cache: false,

      processData: false,

      dataType: 'json',

      success: function(data) {       

      }

    });

});



Here is an example of a multipart form




jQuery(document).on('beforeSubmit', "#formId", function (e) {

    e.preventDefault();    

    var form = jQuery(this);

    jQuery.ajax({

      url: form.attr('action'),

      type: form.attr('method'),

      data: new FormData(form[0]),

      mimeType: 'multipart/form-data',

      contentType: false,

      cache: false,

      processData: false,

      dataType: 'json',

      success: function(data) {       

      }

    });

});



I know it was unsolicited but hopefully it will help you or someone else.

Really appreciate the feedback.

Let’s get the easy part done first.

I had not pasted the code that cancelled the form submission.




        $( '#accountForm' ).submit( function() {

            return false;

        });



However, your suggestion is very good and I will implement it.


The ajax post call. I have tried your suggestion and it makes no difference.

Using the debugger, here is an example of a post with a response from a validation in the business object.

[ see example 1 ]

The second example is the result of a post with an exception.

exception ‘PDOException’ with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry

[see example 2]

The responseData is some other object. Look at the responseText, curiously the first value is the SQL State 23000.

Why / what is being overridden?

The integrity exception is being caught, I’m creating a new exception that is specific to my application called EmailDuplicateException, the EmailDuplicateException is also being caught and a message is to be returned to the user.

All I want to do is display it !!!

I’m really stuck here. :rolleyes:

You are sending a request to valid url. The action associated with the url successfully connects without error therefore returning a 2XX response that the ajax considered a successful action and its contents are returned to the success ajax callback function.

In the past i have assigned my own status code and checked for it in the success callback of the ajax call. I knew if it was that code there was a custom error and i could do what I wanted to do with it.




public function actionDelete($id) {

    $model = $this->findModel($id);

    $title = Html::encode($model->name);

    $response= \Yii::$app->response; 

    $response->format = Response::FORMAT_JSON;

    try {

      if ($model->delete()) {        

          return['message' => "Successfully deleted the department $title from the system!"];   	

      }

    } catch (\yii\db\IntegrityException $e) {

 	$response->statusCode = '424';

 	$response->statusText = "Deletion Denied: The department $title is currently associated with one or more jobs.";

    }

}



I just noticed what your function is doing and there is no need to reinvent the wheel here. You can use the unique validator to do the same exact thing.


public function rules() {

  return [

  ['email', 'unique']

 ];

}

Just some context to explain where I’m coming from.

I’ve created an architecture so that I can do Test Driven Development for this project.

I have my code separated in order to be easily mocked into Business Objects & Data Access Objects.

So the Controller doesn’t have any validation, it just handles requests & responses.

Also, I’d like my application to be as fast as possible. Looking at the testing results in the Yii Application Development Cookbook 3rd edition, the Active Record is generally slower to execute than an straight SQL. Of course this all depends on the complexity and peculiarities of the query.

What I find odd here is why the response is different between an normal execution and an execution with an exception?

Who is deciding to change the format of the response?

( see the attachments, example 1 & 2 )

Can this be a bug in the framework?

I have been diving deeper into the jQuery ajax which is beneficial since I’m learning more.

I have also started to look into the Yii controllers and filters but I find this not to be the best use of my time.

The reason to use a framework is to save time.

However, time is pressing and I need to move forward.

I appreciate your patience and feedback. Is there something fundamental that I’m missing here?

As you mentioned in your first response, leaving a complete trail of the problem and solution in the forum is great for the community.

This is also an additional incentive to get to the bottom of this.