Symfony:添加记录后更改响应

Im opening a form via ajax (using jquery) and i want to save it without reloading whole page. Where i have to change code, to get text response (eg. "ok!") instead of redirection to edit page?

this is my js:

$("#newcatlink").click(function() {
    $.ajax({
    'url': "category/new",
    'success': function(data, textStatus, jqXHR) {
            $("#mdialog").html(data);
            $("#_form_jqhide").hide();
            $("#mdialog").dialog({
                modal: true,
                width: 500,
                resizable: false,
                buttons: {
                    Save: function() {
                        $.ajax({
                            'type': 'POST', 
                            'url': 'category/create', 
                            'data': $("#categoryform").serialize(),
                            'success': function(data, textStatus, jqXHR) {
                                alert(data);
                            }
                        });
                    },
                    Cancel: function() {
                        $(this).dialog("close");
                    }
                }
            });
        }
    });
});

I changed processForm action in actions.class.php like this:

...
if ($form->isValid())
{
  $Category = $form->save();

        if($request->isXmlHttpRequest()) {
            return $this->renderText("ok!");
        } else {
            $this->redirect('category/edit?id='.$Category->getId());
        }
}

but it didnt change anything. How to do this?

edit 8/1/12:

I've made changes to my code according to blowski and sakfa answers, but i get another issue: how to catch form errors? I added to processForm else to if and it works as expected but i cant get error message/code/whatever back, only a fact that error exists.

Here is code: http://pastebin.com/ppbPM88G and this is server answer after sending form with one required field empty:

{"haserrors":true,"errors":[]}

Based on our conversation, I can see what's happening. If I've understood your comments, it's not redirecting the whole page, it's just re-rendering the original form inside the dialog box?

After the user submits the form, if it's valid, then it redirects. However, because you're bypassing the redirect, it reaches the $this->setTemplate('new') bit of code instead, so shows the new form again, populated with the data from the just-completed form.

To get around it, use something like this in the create action:

if($request->isXmlHttpRequest()) {
  $this->renderText(json_encode(array('valid' => $form->isValid())));
} else {
  $this->setTemplate('new');
}

You'll also want to change your Ajax request to specify that it's expecting a JSON response. Then, if the form was valid, close the dialog box (or whatever you want to do after the save).

Based on conversations here:

you have put return $this->renderText("ok!") in processForm method not in action (like executeCreate). Move the return line and condition check to executeXXX method.

edit 2012-01-08

i have removed my old snippets of code, because they're not for any use now, since you edited your original code effectively incorporating my changes there.

as to your new question:

basically symfony1 api sucks when dealing with forms, thats why you have so many problems.

First of all you need to know that this api is not designed to deal with AJAX form submission when it comes to validation - that's because sfForm can validate itself, can store it's errors internally and can render them as HTML - but there is no routine (as far as I know) to return errors in some serializable format (like an array of fieldname=>errorMessage pairs which json_encodes nicely).

to begin with your code, what I would do in your place was:

First: create separate action just for ajax submission handling, assuming you do POST request

public function exucuteAjaxFormSubmission(sfWebRequest $request)
{
    //this action handles ONLY ajax so we can safely set appropriate headers
    $this->getResponse()->setContentType('text/json');

    $form = new form();
    $form->bind($request->getPostParameters());

    if ($form->isValid()) {
        //do your logic for positive form submission, i.e. create or update record in db
        return json_encode('ok'); //notify client that everything went ok
    } else {
        return $this->doSomeMagicToGetFormErrorsAsAnJsonEncodedArray();
    }
}

Now to the doSomeMagicToGetFormErrorsAsAnJsonEncodedArray part: you want to get form errors in json format. For that we will add a simple method to your form class (as this is your 'interface' to validation logic).

Some theory: internally sfForm stores validation errors in a structure called sfValidatorErrorSchema. sfValidatorErrorSchema stores errors in two lists - namedErrors and globalErrors. Named errors are ones thrown for 'field validators' (i.e. required string validator on name field. Global errors are those thrown for whole form (i.e. global postValidators). Each error is a sfValidatorError object thrown by offending validator.

warning: in fact this structure is recursive, but you will see it only if you use embedded forms in this case following function must be changed to include situation where sfValidatorErrorSchema can contain another sfValidatorErrorSchema, not sfValidatorError.. I hope you don't use embedded forms, because this in symfony sucks even more than plain form API.

But enough talking, if you just want code then copy paste these methods into your form class:

class myForm extends sfForm { 
    (...)

    /** a helper method to convert error schema to an array */
    public function getSerializableErrors()
    {
        $result = array();
        foreach ($this->getErrorSchema()->getNamedErrors() as $fieldName => $error) {
            $result[$fieldName] = $error->getMessage();
        }
        foreach ($this->getErrorSchema()->getGlobalErrors() as $error) {
            $result['_globals'][] = $error->getMessage();
        }

        return $result;
    }

    /** screw array, just give me ready to send json! */
    public function getJsonEncodedErrors()
    {
        return json_encode($this->getSerializableErrors());
    }
}

Now just change doSomeMagicToGetFormErrorsAsAnJsonEncodedArray to getJsonEncodedErros and you're done with basic ajax form posting with validation - you should have enough knowledge now to properly implement your logic. I have tested this on current symfony 1.4 version, so if anything goes wrong it may be a problem of old sf version (you didnt tell me which version you use)