Quantcast
Viewing all articles
Browse latest Browse all 8

Overcoming Visualforce Standard List Controller Issue

I ran into an interesting issue while using a standard list controller on a visualforce page today.  This page has been working fine for a while but recently encountered an error due to a new trigger.  The trigger was “bulkified” so I found it odd that the following error was being thrown:

System.LimitException: Too many SOQL queries: 101

Let me set this up for you.  The visualforce page is a simple page (edited here to be even simpler) that is invoked from a list of opportunities.  The user can select any opportunities in the list view, click Edit Selected Opportunities and quickly update basic info on the opportunities (there are more fields on the actual page, but I will just include Name and Stage for brevity).

List View:

Image may be NSFW.
Clik here to view.

VisualforcePage:

Image may be NSFW.
Clik here to view.

Visualforce Code:

<apex:page standardController="Opportunity" recordSetVar="opps" sidebar="false">
    <apex:form id="theForm">
    <apex:pageBlock >
        <apex:pageMessages />

        <apex:pageBlockButtons >
            <apex:commandButton action="{!save}" value="Save All and Close"/>
            <apex:commandButton action="{!cancel}" value="Cancel"/>
        </apex:pageBlockButtons>

        <apex:pageBlockTable width="100%" value="{!selected}" var="o" id="theUSList" rendered="{!$User.US_User__c}">
            <apex:column value="{!o.AccountId}"/>

            <apex:column headerValue="Opportunity Name">
                <apex:inputField value="{!o.Name}"/>
            </apex:column>

            <apex:column HeaderValue="Stage">
                <apex:inputField value="{!o.StageName}"/>
            </apex:column>

        </apex:pageBlockTable>
    </apex:pageBlock>
    </apex:form>
</apex:page>

Problem

One of my users was using the tool after a new trigger was implemented and ran into the above mentioned error. It turned out that he was working with 53 records, which should work fine.

After some investigation it turned out that the standard save functionality was saving each record individually. These individual saves caused the trigger to fire for each record, rather than working on the set in bulk. Not cool salesforce.com, not cool at all. The problem was, there are 2 SOQL statements in the trigger so anything more than 50 records exceeded the limit of 100 SOQL queries. So to counteract this issue I wrote a controller extension which simply grabs the selected opportunities and saves them when the button is clicked.

Note, I had to change two lines in the above VF code

1. The page tag was changed to :

<apex:page standardController="Opportunity" <strong>extensions="selectedOpportunityEditorExtension"</strong> recordSetVar="opps" sidebar="false">

2. The save button was chaged to:

<apex:commandButton action="{!save<strong>Records</strong>}" value="Save All and Close"/>

Apex Class

public with sharing class selectedOpportunityEditorExtension {

    //Our list of opportunities to work on
    private final List<Opportunity> opps;

    //set the list to the selected opportunites. Same list that the page is working on
    public selectedOpportunityEditorExtension(ApexPages.StandardSetController controller) {
        this.opps = controller.getSelected();
    }

    //save the opportunities and return a page refernce to the retURL
    public PageReference SaveRecords(){
    	update opps;
    	PageReference pageRef = new PageReference(ApexPages.currentPage().getParameters().get('retURL'));
    	return pageRef;
    }
}

Test Method

@isTest
private class testSelectedOpportunityEditorExtension {

    static testMethod void myUnitTest() {
        //set up the page reference to the selectedOpportunityEditor and add retURL param
        PageReference pageRef = new PageReference('SelectedOpportunityEditor');
        //add the return URL to the list view
        pageRef.getParameters().put('retURL','/006');
        Test.setCurrentPageReference(pageRef);

        List<Opportunity> oppsList = new List<Opportunity>();
        //Use the current user as owner of new opps
        Id owner = UserInfo.getUserId();
        //Create 101 new opportunties to test with
        for(Integer i=1; i<102; i++){
        	oppsList.add(new Opportunity(Name='Opportunity ' + i,
        								 OwnerId = owner,
        								 Net_Gain__c = 100,
        								 StageName = 'Some Stage',
        								 Probability = 0.2,
        								 CloseDate=Date.newInstance(2100,01,01),
        								 Description='A description',
        								 Sales_Manager__c=owner

        	));
        }
        insert oppsList;

        //set up the set controller then select all opps
        ApexPages.StandardSetController ssc = new ApexPages.StandardSetController(oppsList);
        ssc.setSelected(oppsList);

        //initialize the extension
        selectedOpportunityEditorExtension sOEE = new selectedOpportunityEditorExtension(ssc);

        //Start the test
        Test.startTest();
        //get selected opportunites and change the Net gain for each
        List<Opportunity> loopList = ssc.getSelected();
        for(Opportunity o : loopList){
        	o.Net_Gain__c = 200;
        }

        //call the save records method and make sure it returns the right pagereference
        system.assertEquals('/006', sOEE.SaveRecords().getUrl() );

        //Check that all opportunities were in fact updated
        loopList = ssc.getSelected();
        for(Opportunity o : loopList){
        	System.AssertEquals(200, o.Net_Gain__c);
        }

        Test.stopTest();
    }
}

I hope that this proves useful for somebody. Please note the above code may be altered for this post and is specific to my companies org so it won’t work for you. It is just meant to serve as an example.

A special thanks to Alex Berg for helping me work though this issue.


Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.

Viewing all articles
Browse latest Browse all 8

Trending Articles