0

I am trying to create a new case once the rating of a Hotel is < 45%

Here Hotel__c is the parent object and Customer_Reviews__c is child Object with MDR

Even though there is a field called Name i am not able to access it from Hotel__c object. Please help me out. I am new to Salesforce

  //create new case
    public static void CreateCase(List<Customer_Reviews__c> cr_list) {
        List<case> cs = new List<case>();
        List<Hotel__c> HotelName = new List<Hotel__c>();
        for(Customer_Reviews__c cr : cr_list) {
            if(cr.Rating__c < 45){
                Case c = new Case();
                c.Status = 'New';
                c.Priority = 'Medium';
                c.Origin = 'Web';
                HotelName = [SELECT Name,Overall_points__c FROM Hotel__c WHERE id =: cr.Hotel_stayed__c];
                system.debug(HotelName);
                c.Subject = 'Low Customer Reviews for the Hotel : '+HotelName.Name;
                
                c.Description = 'Your Hotel has been given a bad review by Customer '+cr.Name+'. You have been given a rating of .Please follow up with the same.';
                cs.add(c);
            }

        }
        insert cs;
        system.debug('Done!'); 
        
    }
1
  • What is the error message you are receiving. Is it possible that your user does not have read access to this field?
    – Damecek
    Jul 19, 2021 at 11:49

1 Answer 1

0

The main problem

The issue here is that you've declared HotelName as a List<Hotel__c>.

When you need to access fields on an SObject (like the Name field on an Account, for instance), it needs to be done on a single instance of that object.

Lists (as well as Sets and Maps) are collection types. They can hold many instances of the declared type (which can be just about anything, a List<Account>, a List<My_Custom_Object__c>, a List<List<SObject>>, etc...). Collection types don't have fields (or variables), so myAccountList.Name is never valid syntax.

If you want to get at the data inside of a list, you need to either use .get(Integer index) or square brackets (myList[0]), or iterate over the list with a loop (for, while). HotelName[0].Name would work here, but I wouldn't recommend that approach here.

The other (more important) problem

You have a query inside of a loop. Queries and DML statements are the two things that should virtually never appear inside of a loop because of the governor limits we must observe. We only get 100 SOQL queries in a normal transaction. Queries inside of loops will quickly use up your available queries.

The better approach is to gather the data you need first (using a loop), then performing your query (and iterating over the results).

An improved version of your code here would look like this

// The typical naming convention is something like
// - Class names are TitleCase (first letter of each word is capitalized)
// - Method names are camelCase (first letter of each word, except the first, is capitalized)
// - Variable names are camelCase and usually singular
// - Collection variable names (sets, lists, and maps) are camelCase and plural
//   (e.g. myAccounts or myAccountsList)
// It doesn't really matter what you end up doing, as long as you follow some
//   convention and apply it _consistently_
// Being inconsistent will make your code harder for others to read
public static void createCase(List<Customer_Reviews__c> cr_list) {
    List<Case> cases = new List<case>();

    // Instead of storing Hotels, let's just store the Id for now
    // We'll use it to feed a query a little later
    List<Id> hotelIds = new List<Id>();

    // Our only concern right now is gathering information
    for(Customer_Reviews__c cr : cr_list) {
        if(cr.Rating__c < 45){
            hotelIds.add(cr.Hotel_Stayed__c);
        }
    }

    // Now we can perform the query
    // This construction is called a "SOQL for loop"
    // This is different than having a query inside of a loop, and is in fact
    //   something that Salesforce recommends
    for(Hotel__c hotel :[SELECT Id, Name FROM Hotel__c WHERE Id IN :hotelIds]){
        Case c = new Case();
        c.Status = 'New';
        c.Priority = 'Medium';
        c.Origin = 'Web';
        c.Subject = 'Low Customer Reviews for the Hotel : ' + hotel.Name;

        // This approach doesn't allow easy access to the appropriate Customer_Review__c
        //   though... so this line would give you an error.
        c.Description = 'Your Hotel has been given a bad review by Customer ' + cr.Name + '. You have been given a rating of . Please follow up with the same.';
        cases.add(c);
    }

    insert cases;
    system.debug('Done!'); 
}

While that example is better, it doesn't completely solve your problem. I still think there is value in that example though. As you gain experience with Salesforce, you'll come to find that you do a lot of looping and using Lists, Sets, and Maps.

Looping once to gather information, then looping again to process results is a very common task.

The correct way to handle this task is to make use of the fact that Customer_Review__c is related to a Hotel__c (I assume through the Hotel_stayed__c field).

You can get data from the parent object in a query using dot-notation.

Using that approach would look like this:

public static void createCase(List<Customer_Reviews__c> cr_list) {
    List<Case> cases = new List<case>();

    // Instead of gathering Hotel Ids and querying for hotels, just get the
    //   parent data from a query on Customer_Reviews__c
    // Just turn the relationship field from __c to __r, and use dot notation
    //   to specify the field you want to query
    // Salesforce allows us to use a List of SObjects in a filter on the Id field
    // Also, for simple filters (such as your "rating is below X" one), you can
    //   have SOQL handle it by including it in the WHERE clause
    // Just a little less code for you to write.
    for(Customer_Reviews__c review :[SELECT Id, Name, Hotel_Stayed__r.Name FROM Customer_Reviews__c WHERE Id IN :cr_list AND rating__c < 45]){
        Case c = new Case();
        c.Status = 'New';
        c.Priority = 'Medium';
        c.Origin = 'Web';

        // When accessing parent data, it's the same as what you used in the query.
        // __c becomes __r, and use dot-notation to access the parent data
        c.Subject = 'Low Customer Reviews for the Hotel : ' + review.Hotel_Stayed__r.Name;

        c.Description = 'Your Hotel has been given a bad review by Customer ' + review.Name + '. You have been given a rating of ' + review.rating__c + '. Please follow up with them.';
        cases.add(c);
    }

    insert cases;
    system.debug('Done!'); 
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .