+ Start a Discussion
Patrick DixonPatrick Dixon 

Struggling with SOQL for loop

I have three custom objects: page_element, content_Item and content. element is a lookup child of content_item and content is a master-detail child of content_item.


I'm trying to render these on a vf page in 3 nested apex:repeats - element (outer) - content_item - content (inner).  I  have this code in a custom controller extension:


    public list<Page_Element__c> getElements() {
        String pgId = ValidatePageName();
        list<Page_Element__c> elements =
            [Select Name, Order_On_Page__c, Box_Size__c, Page_Layout__c, Content_Item__c, Content_Type__c
            From Page_Element__c where Page_Layout__c = :pgId order by Order_On_Page__c asc];
        return elements;
    public list<Content_Item__c> getContentItems() {
        String pgId = ValidatePageName();
        list<Content_Item__c> contentItems;

        // Create a list of account records from a SOQL query  
        list<Page_Element__c> elements =
            [Select Name, Order_On_Page__c, Box_Size__c, Page_Layout__c, Content_Item__c, Content_Type__c
            From Page_Element__c where Page_Layout__c = :pgId order by Order_On_Page__c asc];
        // Loop through the list and generate another SOQL query using the Contact Item referenced
        for (Page_Element__c e : elements) {
            string ciID = e.Content_Item__c;
            contentItems = [select Id, URL_Link__c, Synopsis__c, Release_Date__c, Name, Heading__c,
                (select Name, Content_Item__c, URL_Link__c, Image__c, Description_Text__c
                from Contents__r order by name)
                From Content_Item__c where Id = :ciID order by Release_Date__c desc];
        return contentItems;


and this in a VF page:


    <apex:panelGroup layout="inlined" rendered="{!NOT(ISNULL(elements.size))}">
        {!elements.size} <br/>
        <apex:repeat var="el" value="{!elements}" >

        <apex:panelGroup layout="inlined" rendered="{!NOT(ISNULL(contentItems.size))}">
            <apex:repeat var="ci" value="{!contentItems}" >
                <div class="clearfix contentpage">
                    <h2 class="clearfix contentheading">
                        <a href="{!ci.URL_Link__c}" class="contentpagetitle">{!ci.Heading__c}</a>

                     <div class="article-content">
                        <apex:panelGroup layout="inlined" rendered="{!NOT(ISNULL(ci.contents__r.size))}">
                            <apex:repeat var="c" value="{!ci.contents__r.}" >
                                <a class="logo" href="{!c.URL_Link__c}" target="_blank">
                                    <apex:outputText escape="false" styleClass="logosize"  style="float:left" value="{!c.Image__c}" />
                                 <apex:outputText escape="false" style="float:left" value="test {!c.Description_Text__c}" />


The first problem I have is that the {!elements.size} returns 4 (which is what I'd expect), whilst {!contentitems.size} returns just 1 - even though it's using the exact same query as control for the for loop as 'getElements', and  {!elements.Content_Item__c} returns 4 different valid ids for Content_Item__c.


The second problem I have is that the vf page won't save with the inner loop using {!ci.contents__r}  - it gives me

Save error: Unknown property 'VisualforceArrayList.'


unless I comment out the inner loop.


So what am I doing wrong here, and how can I do it better?


Many thanks.




Two things here.  One is that although you've scoped the contentItems list outside of the for loop, you are selecting into that list within the for loop.  that will manifest as only returning the content items for the last element.  I suggest using an 'in' clause in your query and using the element Ids from the list as your argument.


Second - I'd use a wrapper class to hold the members that you make publicly available and thefore not have to have a whole bunch of accessors/getters in your page controller.  This will promote reuse throughout your app as well.  In your wrapper class, make the child lists members that are publicly accessible.  Then you only need to fill the member array of the wrapper class in your page controller and have an accessor for that array only.  that will solve your second problem.

Patrick DixonPatrick Dixon

Thank you very much for your answer - I am certainly seeing the content item for just the last element although I don't understand your explanation and it seems to defy all my understanding of a for loop!


I'm a VF newbie so it would be a great help if you could give an example of an 'in' clause in a query (is it 'in' instead of 'from'?) and an example of a 'wrapper' class too.  Is a wrapper class just a list of publicly declaired varaibles within the controller?


Any SOQL query (and SQL for that matter) is made up of the following structure:









All you need to do is change your "=" in the condition clause to an "in" and pipe in a bound list/set of IDs.  See here:

Deeper Look on SOQL


And for the wrapper class, see this:


Custom Object Wrappers


Crux of this is.

1. Write a Class that has public member variables for your contentElements, ContentLists

2. Make an array of those object instances members of your page controller and expose those through a getter


Hope this helps.  I'll write you the code, but that'll cost you :)

Patrick DixonPatrick Dixon

Thanks - it helps a lot.  (I haven't got any money I'm afraid).


So, I don't even need an SOQL for loop ... which is just as well as I still don't understand how one would ever work - since it only ever takes the last value in the 'loop'!


(I'm still not sure why VF doesn't accept {!ci.contents__r} - since it seems a perfectly valid relationship AFAICS.)