Monday, March 19, 2012

Salesforce "Save and New" method for Controller Extension with custom sObject

A client recently wanted to add a "Save and New" command button to a Visualforce page that was using a controller extension overriding the new button. The users should be able to save the current sObject that was being created and then transition straight to adding another instance.

Creating a custom save method in Apex wasn't difficult as I could just keep a reference to the StandardController and then call save on that. However, creating a PageReference that would create the required StandardController was a bit more difficult.

My first attempt was more or less as follows. Note that this doesn't work in practice.

public with sharing class CustomExtensionController {
    
    Foo__c foo;
    ApexPages.StandardController sController;
    
    public CustomExtensionController (ApexPages.StandardController controller) {
        sController = controller;
        foo = (Opportunity)controller.getRecord();
    }
    
    public PageReference saveAndNew() {
        
        // Custom Save code here...
        
        PageReference pr = sController.save();

        return (new ApexPages.StandardController(new Foo__c())).edit();
    }
}

The results in the Exception:

Visualforce Error java.lang.UnsupportedOperationException: You cannot call edit() on a null object

It seems odd that there isn't a new() method on StandardController for such a scenario. Instead the PageReference can be built up manually using the schema.

public with sharing class CustomExtensionController {
    
    private Foo__c foo;
    private ApexPages.StandardController sController;
    private String queryString;
    
    public CustomExtensionController (ApexPages.StandardController controller) {
        sController = controller;
        foo = (Opportunity)controller.getRecord();

        // Keep track of any inbound query string parameters so they can be passed along when creating a new record
        List pageUrl = ApexPages.currentPage().getUrl().split('\\?');
        queryString = pageUrl[1];
    }
    
    public PageReference saveAndNew() {
        
        
        try {
            // Save the current sObject
            sController.save();

            // Get the Meta Data for Foo__c
            Schema.DescribeSObjectResult describeResult = sController.getRecord().getSObjectType().getDescribe();
            // Create PageReference for creating a new sObject and add any inbound query string parameters.
            PageReference pr = new PageReference('/' + describeResult.getKeyPrefix() + '/e?' + queryString);
            // Don't redirect with the viewstate of the current record.
            pr.setRedirect(true);
            return pr;
        } catch(Exception e) {
            // Don't redirect if something goes wrong. May be a validation or trigger issue on save.
            ApexPages.addMessages(e);
            return null;
        }
    }
}

See also:

6 comments:

  1. Hi!....Thanks for this!....I was trying to find a way to retain the save functionally in the controller :)....Thanks to your blog...I did it :)

    ReplyDelete
  2. Hi
    I have one requirement,
    Actually using below code i save object record in database dynamically.
    Schema.SObjectType targetType = Schema.getGlobalDescribe().get(strFieldName);
    SObject myObj = targetType.newSObject();
    insert myObj;

    Note:using above code related object record only created.ButFields information is not saved.Here Fields add visualforce page Dynamically

    but I gave some fields information here but those fields information is not saved in this record
    How to save fields information also related object record Dynamically?

    help me...............

    ReplyDelete
    Replies
    1. Try using the put method on the sObjects to set the field values. See http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_sobject.htm#apex_System_SObject_put

      Delete
  3. Hello,
    Thank you for your article, I found it very useful. I am new to salesforce and apex programming I would appreciate a lot your help on the below problem.

    Do you think we can use "insert myObj;" instead of "StdController.save();" (line 21) ?
    In my case, it looks to me that if one or more validation rules fail, the exception is not thrown by StdController.save() method.
    The problem is that as a result, we are redirected to the new record Edit page without saving previous record.
    Instead, if I call however "insert" command, the exception is raised (lines 30-34 are executed) and error messages are displayed on the UI. Or there is some other way that we should handle validation rule failures in case we call standard controller save() method ?

    Thank you in advance for your help.

    ReplyDelete
    Replies
    1. I haven't tried it, but the StandardController.save() method returns a PageReference (https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/apex_ApexPages_StandardController_save.htm). If that is null it is likely that the StandardController already handled the DMLException and added it as a page message. Try checking for null being returned by save() and then returning null from the saveAndNew() methond in turn.

      Delete
    2. Thank you very much for your reply. Doing a null check + returning null value worked for me perfectly, the validation errors in this case are indeed added in the page message.

      Delete