Salesforce Apex - Populating an object from SOQL query - salesforce

I have a simple SOQL query that returns information relating to a Contact and CampaignMember. I'm attempting to populate a custom object with the results of the SOQL query. However I get the following error when loading the Visualforce page:
Invalid field campaign.name for CampaignMember
List campaignMembers = [select campaign.name, contact.id,contact.firstname, contact.lastname, status, campaignId from CampaignMember where contactId = '003U000000U0eNq' and campaignId in :campaigns];
for (Integer i = 0; i < campaignMembers.size(); i++) {
results.add(new CampaignMemberResult(
(String)campaignMembers[i].get('CampaignId'),
(String)campaignMembers[i].get('campaign.name'),
true
));
}
I've ran the SOQL query seperately in the Developer Console and it queries successfully. Why can I not pull in the campaign.name from the SOQL query within the for loop?

The error you see is caused by the fact you should write it as campaignMembers[i].Campaign.Name. Or if you insist on the getter syntax, campaignMembers[i].getSobject('Campaign').get('Name').
Any special reason you need the wrapper object (or whatever CampaignMemberResult is)?
I have strange feeling you're writing too much code to achieve something simple ;) The syntax with campaignMembers[i].Campaign.Name will also mean you don't have to use casts to String.
Plus - if you need to know "in which campaigns does this Contact occur" you have 2 ways:
flat
select contact.id,contact.firstname, contact.lastname,
campaignid, campaign.name,
status
from CampaignMember
where contactId = '003U000000U0eNq'
subquery
From contact you go down to the related list of campaignmembers, then up to campaigns to get their names
SELECT Id, FirstName, LastName,
(SELECT CampaignId, Campaign.Name FROM CampaignMembers)
FROM Contact WHERE Id = '003U000000U0eNq'
Example how to use "flat" result straight in visualforce (without CampaignMemberResult):
Apex:
public List<CampaignMember> flatMembers {get;set;} // insert dick joke here
flatMembers = [select contact.id,contact.firstname, contact.lastname,
campaignid, campaign.name,
status
from CampaignMember
where contactId = '003U000000U0eNq'];
VF:
<apex:pageBlockTable value="{!flatMembers}" var="cm">
<apex:column value="{!cm.Contact.LastName}" />
<apex:column value="{!cm.Status}" />
<apex:column value="{!cm.Campaign.Name}" />
EDIT
My end goal is a Visualforce page to be displayed on the contact
record showing a list of all campaigns with a checkbox alongside each
indicating if the contact is a member or not.
You do realize it can quickly grow into a pretty long table? Maybe some filter on campaigns (if you feel like reading about sth advanced - check the documentation for "StandardSetController"). Also I'm pretty sure there are some ways to add Contacts/Leads to Campaigns from Campaign reports - maybe something out of the box would save your time and be more maintainable...
But code solution would be pretty straightforward, start with a helper wrapper class:
public class CampaignWrapper{
public Boolean selected {get;set;}
public Campaign c {get; private set;}
public CampaignWrapper(Campaign c){
this.c = c;
selected = !c.CampaignMembers.isEmpty();
}
}
Then a query and build the list of wrappers:
List<CampaignWrapper> wrappers = new List<CampaignWrapper>();
for(Campaign c : [SELECT Id, Name, (SELECT Id FROM CampaignMember WHERE ContactId = '...')
FROM Campaign
LIMIT 1000]){
wrappers.add(new CampaignMember(c));
}
You should be all set ;) If it's just for displaying - you might not even need the wrapper class (some tricks in visualforce expressions maybe or use Map<Campaign, Boolean> even...
1000 records is the limit of collections passed to Visualforce (10K if your page will be readonly). Past that - pagination, most likely with use with abovementioned StandardSetController.

Related

How do I get info from lookup in salesforce query

I have a custom salesforce object Installation__c and it has a custom field Product__c which is a lookup to a custom object Product__c I am trying to get the fields from the child object using these query:
public with sharing class InstallationController {
#AuraEnabled
public static List<Installation__c> getItems() {
// Perform isAccessible() checking first, then
return [SELECT Id, Name, Installation_Display_Name__c, Product__c, Status__c, (SELECT Product__c.Name FROM Product__c) FROM Installation__c];
}
}
I get the error:
Didn't understand relationship 'Product__c' in FROM part of query call. If you are attempting to use a custom relationship, be sure to append the '__r' after the custom relationship name. Please reference your WSDL or the describe call for the appropriate names.
I have tried changing the Query to
FROM Product__rand FROM Product__c__r but neither seems to work, how do I fix my query?
If you're traversing up or down a relationship hierarchy, the __c suffix becomes __r (r for 'relationship') until you finally get to the field that you're looking for (which still ends in __c if it's a custom field). So in your case, it will be
public with sharing class InstallationController {
#AuraEnabled
public static List<Installation__c> getItems() {
// Perform isAccessible() checking first, then
return [SELECT Id, Name, Installation_Display_Name__c, Product__r.Name, Status__c FROM Installation__c];
}
}
So, the change here is, for the relationship you have to use Product__r.Name
Click into the relationship on the object that has the look up. Copy the relationship name adding __r to it
This example would be Test_Drives__r

Writing a Custom Controller extension to get related records and iterate over list/index and use with apex:repeat

I have 3 custom objects with a Master-Detail Relationship and Lookup Relationship.
CustomA__c (related CustomB__c) <-> CustomB__c <-> CustomC_c (related CustomB_cc)
I´ve built a Visualforce page with HTML table to replicate a PDF document and a Custom Controller Extension, so far so good.
But I´m just a beginner with apex coding.
The problem is that I need to replicate the document as it is, when there are no related records for CustomA__c or less then 5, it should still show the full table (the empty rows). Max. rows/related records on the document is 5, no second page needed.
Currently I´m trying to accomplisch that by using apex:variable and apex:repeat as I´ve seen some examples, but perhaps there is also another solution. For the Visualforce page I already wrote the code for the rows with data and another apeax:repeat for the empty rows.
But I´m really strugling with the controller, i know i need to iterate over the list, the code that i already wrote is also put together out of examples as i just don´t understand it yet good enough.
Any help would be appreciated! Thanks, Josip
public with sharing class CustomAController {
public CustomA__c customa{get; set;}
public CustomA__c ca{get; set;}
public CustomAController (ApexPages.StandardController controller) {
ca = (CustomA__c )controller.getRecord();
customa= [SELECT Id, Name FROM CustomA__c WHERE Id = :ApexPages.currentPage().getParameters().get('Id')];
}
public List<CustomB__c > getrelatedCustomB() {
List <CustomB__c > cbList = New List<CustomB__c >(5);
for(CustomA__c acc:[SELECT Id, Name, (SELECT Id, Name, ... , CustomCfield__r.Name FROM CustomBs__r ORDER BY Name LIMIT 5) FROM CustomA__c WHERE Id = :customa.Id]){
for(CustomB__c cb:acc.CustomBs__r)
cbList.add(cb);
}
return cbList;
}
}
You can dramatically simplify your code by writing a direct query on the child object instead of a parent-child SOQL query.
public List<CustomB__c > getrelatedCustomB() {
return [SELECT Id, Name, ... , CustomCfield__r.Name
FROM CustomB__c
WHERE CustomA__c = :customA.Id
ORDER BY Name
LIMIT 5];
}
There's no need to iterate in Apex here.

Unable to filter using ContentDocument id in query in trigger query not retrieving any results

I have a requirement to make to make a file private and be available to only the user whose role name consists the name of the file for a specific custom object. For this I am trying to retrieve from Content Document Link with the custom object name LinkedEntity.Type and ContentDocumentid as filters, when I hard code the ContentDocumentid it is working fine but when I try to dynamically provide the ContentDocumentId then the query is not returning any result. I am adding a snippet of my code. Please Help!!. Thanks
List<Id> listOfConDocuId = new List<Id>();
for(ContentVersion cv: Trigger.new){
if((!cv.Title.contains('product proposal')) || (!cv.Title.contains('final')) || (!cv.Title.contains('packet')))
listOfConDocuId.add(cv.ContentDocumentId);
}
Map<Id, Project__c> mapOfProjectId = new Map<Id, Project__c>([SELECT Id FROM Project__c]);
Set<Id> setOfProjectId = mapOfProjectId.keySet();
List<ContentDocumentLink> LinkedProject = [SELECT ContentDocumentId, LinkedEntityId, ContentDocument.Title FROM ContentDocumentLink where LinkedEntityId in :setOfProjectId and LinkedEntity.Type='Project__c' and ContentDocumentId IN :listOfConDocuId];`
I don't think it's necessary to add a WHERE clause for both ID and TYPE. Id should be enough. As far as restricting files to only users with certain roles, have you tried sharing the Custom Object (Project__c) with only those users and then simply linking the files to that Custom Object record with Inferred Permission?
Read more about the sharing types and visibility of Content Document Link here:
https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_contentdocumentlink.htm

Apex Trigger Context Variable

Here my code for apex trigger.
trigger LeadTrigger on Lead (after insert)
{
if(Trigger.isInsert){
for(Lead newLead: Trigger.new)
{
//newLead.RecrodTypeId //'Give value of record type id.
//newLead.RecordType.Name //'Null'
}
}
}
Why "newLead.RecordType.Name" returns null?
The lists of objects available in triggers only have values for the fields on the object the trigger is running on. No relationships are traversed, only the IDs of the lookup records are included.
Therefore, to pull in any extra information you need to from related objects needs to be queried for.
You'll want to do something like this:
trigger LeadTrigger on Lead (after insert) {
map<id, RecordType> mapRecordTypes = new map<id, RecordType>();
if(Trigger.isInsert) {
for(Lead newLead: Trigger.new) {
mapRecordTypes.put(newLead.RecordTypeId, null);
}
}
for(RecordType rt : [select Id, Name from RecordType
where Id in : mapRecordTypes.ketSet()]) {
mapRecordTypes.put(rt.Id, rt);
}
for(Lead newLead : Trigger.new) {
string recordTypeName = mapRecordTypes.get(sLead.RecordTypeId).Name;
}
}
This is probably because some of your leads that just got inserted don't have record types associated with them. This is normal. You can enforce that record type selection is mandatory through configuration, if that's what you're looking for.
[EDIT]
Now I think I understand the issue (from your comment). The reason is that since you're in a trigger, the associated RecordType referenced object is not available. The RecordTypeId will always be available since it is literally part of the trigger object as an Id. However, child objects (referenced objects) will not be available to simply reference from within a trigger. To do this you need to create a map of the referenced object in question by doing an additional SOQL call WHERE Id IN: theIdList.
From Apex, not in a trigger, you need to specifically call this field out from your SOQL like this:
List<Lead> leads = [SELECT Id, RecordType.Name FROM Lead];
What just happened there is that the child object, the RecordType in this case, was included in the query and therefore available to you. By default a trigger will not have all of your child objects pre-selected and therefore need to be selected afterwards from within the trigger or class called by the trigger:
List<Id> recIds = new List<Id>();
for(Lead l : leads)
{
recIds.add(l.RecordTypeId);
}
List<RecordType> rt = [SELECT Id, Name FROM RecordType WHERE Id IN :recIds];
Map <Id, String> idRecNameMap = new Map<Id, String>();
for(RecordType r : rt)
{
idRecNameMap.put(r.Id, r.Name);
}
// And finally...
for(Lead l : Trigger.new)
{
String tmpRecordTypeName = idRecNameMap.get(l.RecordTypeId);
}
I did not test this code but I think it look ok. Hope this makes sense.
you can't get extra information on the related objects from this trigger. if you want to get more information you need to make query for other objects.
List<RecordType> records = [SELECT Id, Name FROM RecordType WHERE Id = newLead.RecrodTypeId];
string myname = records[0].name;
but remember that you shouldn't make a query in for loop. so if you wanted to do it in the right way go for Adam's solution.
Put some system debug inside the loop and check your system debug logs for more information
system.debug('lead:' + newLead);
inside the for loop and see what is being passed in. You may find that it is null.
We cant really give you a good answer without knowint the rest of your set up.

Populate select list with contacts

I want to use a apex:selectList to populate contacts. The problem is that it gives an error
Collection size 3,403 exceeds maximum size of 1,000
This is because i have 3403 contacts, and Vf has limitation on the collections in the VF page.
I want to limit the inital set of contacts to <1000, and as the user starts typing in the characters i would want to query the contacts. For ex if the user types in Ji i want to query the contacts to retrieve records starting with JI.
Is this possible to do?
<apex:selectlist id="ClientsSearch" value="{!Appointment.Client__c}"
size="1" required="true" rendered="{!NOT (SearchMode)}">
<apex:selectOptions value="{!Clients}" />
</apex:selectlist>
public List<SelectOption> getClients() {
List<SelectOption> options = new List<SelectOption>();
List<Contact> Clients = [Select id, Name From Contact order by Name];
options.add(new SelectOption('0001', '--Select--'));
for(Contact c : Clients ){
options.add(new SelectOption(c.id, c.Name));
}
return options;
}
You can use wild cards in your query to do this — so you'll want to add an <apex:inputText> element in your page to allow them to enter a search term, which writes to a string variable in the controller. Then add a search button to run the query and re-render the list with the new list of contacts.
The important parts of the controller will look something like this:
public string SearchTerm {get; set;}
public list<ContacT> Contacts {get; set;}
public Pagereference SearchContacts()
{
// etc.
Contacts = [select Id, Name from Contact where name like : '%' + SearchTerm + '%' order by name limit 1000];
// populate list here
return null;
}
You could perform the search using an action function and firing it from the onChange event of the input field but having a button to do the search will make the whole thing more response (IMO) from the user's point of view.
Note: I wrote this code on the fly, it may be that you can't just concat '%' with the search term in this manner or maybe that you don't even need to when querying directly. Usually in these cases I've had to utilise dynamic SOQL due to other requirements, where you build up the query in a string:
strQuery = 'select id from contact where name like \'%' +
String.escapeSingleQuotes(SearchTerm) + '%\' order by name';
for(Contact sContact : Database.query(strQuery) ...
There are a few options to get the functionality you'd like. It depends on how complicated of a solution you'd like, but there is always a solution.
You may want to consider using jQuery and SOSL (Search Language) in a controller. Here's an example of jQuery autocomplete with the Ajax API in Salesforce. Click the link below:
http://matthewkeefe-developer-edition.na8.force.com/jQueryAutocompleteWithAjaxAPI
Also, check out Tehnrd's post "Super Cool Advanced Lookup Component" for another option.

Resources