Working With Related Objects - El Toro - Find articles about Visualforce, Apex, and Salesforce in general

Print Preview

Working With Related Objects

For some code samples in this site, I have decided to create the basic model of the objects and fields required to build an airline/ticketing system. This is the image the Schema Builder tool created for my objects:

There are few things that are important to highlight with these objects, but probably the most important is the 2 relationships that exist between Flight and City. As you would imagine from an application like this, there are 2 relationships between Flight and City, because  plane needs to depart from a city and hopefully land in a different city (we are not going to provide helicopter tours for this application).

When you have only 1 relationship between objects, in this case like Flight (parent) and Ticket (child), you end up with a relationship like this:

Relationship Names

Which is a relationship build on the child (Ticket) to store the foreign key that relates to the parent (Flight) object. Based on this query, I could build this SOQL query to include the fields on the relationship:

SELECT Id, Flight__r.Name FROM Ticket__c

SELECT Id, (SELECT ID FROM Tickets__r) FROM Flight__c

Since I am using the default names of the relationships, these are the names of the objects with their singular (from child to parent) and plural (from parent to children) versions.

But in the case of Flight and City, I can not use the default names because that would create duplicate names. Since the two relationships can be clearly identified in this application as the origina and destination, I will use those names when defining the relationships so they look like this:

Which leads to these SOQL queries:

SELECT Id, (SELECT ID FROM Origins__r), (SELECT ID FROM Destinations__r) FROM City__c

SELECT Id, Origin__r.ID, Destination__r.ID FROM Flight__c

This knowledge can also be applied to writing code in Visualforce. This sample ilustrates this:

<apex:page standardController="Flight__c">
            <th>Flight Name:</th>
            <td><apex:outputField value="{!Flight__c.Name}"/></td>
            <th>Origin ID:</th>
            <td><apex:outputText value="{!Flight__c.Origin__c}" /></td>
            <th>Origin Name:</th>
            <td><apex:outputText value="{!Flight__c.Origin__r.Name}" /></td>
            <th>Destination Name:</th>
            <td><apex:outputText value="{!Flight__c.Destination__r.Name}" /></td>
                <apex:dataTable value="{!Flight__c.Tickets__r}" var="t">
                    <apex:column value="{!t.Name}"/>


There are couple limits with regards to relationships that is important to note:

Number of Levels: With formula fields you can go 10 levels up, but with Apex/Visualforce you can only go 5 levels up (In our example you could go from Ticket up to Flight up to City and that would be 2 levels - Ticket__c.Flight__r.City__r.Name). With formula fields you can't go down but with Apex/Visualforce you can go 1 level down (Show the flights departing a city - City__c.Origins__r)

Unique relationships: You can only have a maximum of 10 unique relationships per object, regardless of how many fields you use. Although we are retrieving 12 fields in the following query, there are only 4 unique relationships used: 

SELECT Id, Name, Passenger__r.ID, Passenger__r.Name, Flight__r.ID, Flight__r.Name, Flight__r.Departure__c, Flight__r.Arrival__c, Flight__r.Origin__r.ID, Flight__r.Origin__r.Name, Flight__r.Destination__r.ID, Flight__r.Destination__r.Name FROM Ticket__c

Many-to-Many Relationships

In Salesforce all the relationships are One-to-Many, regardless of the type of relationship Lookup or Master-Detail. But you can build special relationships like Self, Hierarchy or Many-to-Many.

In the case of the Many-to-Many relationship, you are going to build 2 One-to-Many relationships between your objects and a third object. In the example that we are working on, we have that a Passenger can take many flights and a flight will have many passengers and we'll use the third object called Ticket.

Also note that in our example we are building these relationships with the help of 2 Master-Detail relationships so we can call our object a "Junction Object". If one (or both) relationships was built using a Lookup, this third object would not be called a "Junction Object". Why is this distinction important?

Well, let's go back to the basics. One of the most important difference between a lookup and the Mster-Detail relationship has to do with security, specifically with record access. When you have a lookup, and you can see the parent record you are not guaranteed to see the child object (or viceversa) but with a Master-Detail the security is controlled by the parent object. Another important differences to consider include cascade delete and foreign key required (althouth you could enforce these with lookups as well), but there is a level of complexity when dealing with a junction object... Which one is the parent? Which one sets the security? Which one does a cascade delete? Those are really good questions, thanks for asking ;-)

I am going to deviate from the Passenger, Flight and Ticket becase I actually need few more objects to explain this... So let's suppose that I have these objects:

Let's suppose we have 2 objects who are master objects (P1, an P2) each of them have two detail objects, like this:

P1 is master for C and C1
P2 is master for C and C2

As you can see, C is a junction object (2 M-D, one for P1 and one for P2). For purpose of these exercise P1 and P2 have OWD private, and I have unchecked "Grant Access Using Hierarchies" to disable the role hierarchy for these objects.

To keep it simple, I have created records that have the same name as the object (P1, P2, C1, C2, C) and these are the record access settings:

Owner: Amy
Read: Melisa

Owner: Andy
Read: Melisa

Notice that Melisa has access to all records, and she is the only one who can see the C record ! In order to see a record with 2 Master-Detail relationships, you must have access to BOTH parents. But the C record will be deleted when any (P1 or P2) record is deleted.

comments powered by Disqus

© El Toro . IT @ 2013
Andrés Pérez