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!');
}