afv-library/skills/generating-lwc-components/assets/apex-controller/LwcController.cls
sandipkumar-yadav 37aa84df42
feat: @W-22444026@ Introducing Core Skills, Datacloud Skills, Industries and Utility Skills. (#268)
* Migrating Core Salesforce Skills

* Updating pr comments

* updat reference

* Updating a skill

* Migrating Datacloud skills

* Migrating Industries cloud skills

* Validating - skills fixing

---------

Co-authored-by: Sandip Kumar Yadav <sandipkumar.yadav+sfemu@salesforce.com>
2026-05-14 19:32:15 +05:30

328 lines
12 KiB
OpenEdge ABL

/**
* APEX CONTROLLER TEMPLATE FOR LWC
*
* This template demonstrates @AuraEnabled methods for LWC with:
* - Cacheable methods for wire service
* - Non-cacheable methods for data mutations
* - Proper error handling
* - Security enforcement
* - Bulk-safe patterns
*
* Replace: LwcController YourControllerName
*/
public with sharing class LwcController {
//
// CACHEABLE METHODS (For @wire service)
//
/**
* Get records for display in LWC
* Use with @wire decorator for automatic caching and refresh
*
* @param parentId - Parent record ID (optional filter)
* @param searchTerm - Search filter (optional)
* @param limitSize - Max records to return
* @return List of Account records
*/
@AuraEnabled(cacheable=true)
public static List<Account> getAccounts(Id parentId, String searchTerm, Integer limitSize) {
try {
Integer recordLimit = limitSize != null ? limitSize : 50;
String searchKey = String.isNotBlank(searchTerm)
? '%' + String.escapeSingleQuotes(searchTerm) + '%'
: '%';
return [
SELECT Id, Name, Industry, AnnualRevenue, Phone, CreatedDate
FROM Account
WHERE Name LIKE :searchKey
WITH SECURITY_ENFORCED
ORDER BY Name
LIMIT :recordLimit
];
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
/**
* Get single record by ID
*/
@AuraEnabled(cacheable=true)
public static Account getAccountById(Id accountId) {
try {
List<Account> accounts = [
SELECT Id, Name, Industry, AnnualRevenue, Phone, BillingAddress,
(SELECT Id, FirstName, LastName, Email FROM Contacts LIMIT 5)
FROM Account
WHERE Id = :accountId
WITH SECURITY_ENFORCED
LIMIT 1
];
if (accounts.isEmpty()) {
throw new AuraHandledException('Account not found');
}
return accounts[0];
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
/**
* Get picklist values for a field
*/
@AuraEnabled(cacheable=true)
public static List<PicklistOption> getIndustryOptions() {
List<PicklistOption> options = new List<PicklistOption>();
Schema.DescribeFieldResult fieldResult = Account.Industry.getDescribe();
List<Schema.PicklistEntry> entries = fieldResult.getPicklistValues();
for (Schema.PicklistEntry entry : entries) {
if (entry.isActive()) {
options.add(new PicklistOption(entry.getLabel(), entry.getValue()));
}
}
return options;
}
//
// NON-CACHEABLE METHODS (For data mutations)
//
/**
* Create a new account
*
* @param accountData - JSON string of account fields
* @return Created Account with Id
*/
@AuraEnabled
public static Account createAccount(String accountData) {
try {
// Parse JSON to Map
Map<String, Object> fieldMap = (Map<String, Object>) JSON.deserializeUntyped(accountData);
// Create Account record
Account newAccount = new Account();
newAccount.Name = (String) fieldMap.get('Name');
newAccount.Industry = (String) fieldMap.get('Industry');
newAccount.Phone = (String) fieldMap.get('Phone');
// Validate required fields
if (String.isBlank(newAccount.Name)) {
throw new AuraHandledException('Account name is required');
}
// Insert with security check
SObjectAccessDecision decision = Security.stripInaccessible(
AccessType.CREATABLE,
new List<Account>{ newAccount }
);
insert decision.getRecords();
return (Account) decision.getRecords()[0];
} catch (DmlException e) {
throw new AuraHandledException(e.getDmlMessage(0));
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
/**
* Update an existing account
*
* @param accountId - Record ID to update
* @param accountData - JSON string of fields to update
* @return Updated Account
*/
@AuraEnabled
public static Account updateAccount(Id accountId, String accountData) {
try {
// Verify record exists and user has access
List<Account> existing = [
SELECT Id FROM Account
WHERE Id = :accountId
WITH SECURITY_ENFORCED
LIMIT 1
];
if (existing.isEmpty()) {
throw new AuraHandledException('Account not found or access denied');
}
// Parse and apply updates
Map<String, Object> fieldMap = (Map<String, Object>) JSON.deserializeUntyped(accountData);
Account accountToUpdate = new Account(Id = accountId);
if (fieldMap.containsKey('Name')) {
accountToUpdate.Name = (String) fieldMap.get('Name');
}
if (fieldMap.containsKey('Industry')) {
accountToUpdate.Industry = (String) fieldMap.get('Industry');
}
if (fieldMap.containsKey('Phone')) {
accountToUpdate.Phone = (String) fieldMap.get('Phone');
}
// Update with security check
SObjectAccessDecision decision = Security.stripInaccessible(
AccessType.UPDATABLE,
new List<Account>{ accountToUpdate }
);
update decision.getRecords();
// Return refreshed record
return getAccountById(accountId);
} catch (DmlException e) {
throw new AuraHandledException(e.getDmlMessage(0));
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
/**
* Delete records
*
* @param recordIds - List of record IDs to delete
*/
@AuraEnabled
public static void deleteAccounts(List<Id> recordIds) {
try {
if (recordIds == null || recordIds.isEmpty()) {
throw new AuraHandledException('No records specified for deletion');
}
// Query records to verify access
List<Account> accountsToDelete = [
SELECT Id FROM Account
WHERE Id IN :recordIds
WITH SECURITY_ENFORCED
];
if (accountsToDelete.size() != recordIds.size()) {
throw new AuraHandledException('Some records not found or access denied');
}
delete accountsToDelete;
} catch (DmlException e) {
throw new AuraHandledException(e.getDmlMessage(0));
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
/**
* Bulk update records (for datatable inline editing)
*
* @param records - List of records with Id and fields to update
*/
@AuraEnabled
public static void updateRecords(List<Account> records) {
try {
if (records == null || records.isEmpty()) {
return;
}
// Security check
SObjectAccessDecision decision = Security.stripInaccessible(
AccessType.UPDATABLE,
records
);
update decision.getRecords();
} catch (DmlException e) {
throw new AuraHandledException(e.getDmlMessage(0));
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
//
// PAGINATED QUERY (For infinite scrolling)
//
/**
* Get paginated records for infinite scrolling
*
* @param limitSize - Records per page
* @param offset - Starting position
* @param sortBy - Field to sort by
* @param sortDirection - ASC or DESC
* @return PagedResult with records and total count
*/
@AuraEnabled(cacheable=true)
public static PagedResult getPagedAccounts(
Integer limitSize,
Integer offset,
String sortBy,
String sortDirection
) {
try {
Integer recordLimit = limitSize != null ? limitSize : 50;
Integer recordOffset = offset != null ? offset : 0;
String sortField = String.isNotBlank(sortBy) ? sortBy : 'Name';
String sortDir = sortDirection == 'desc' ? 'DESC' : 'ASC';
// Whitelist allowed sort fields to prevent SOQL injection
Set<String> allowedSortFields = new Set<String>{
'Name', 'Industry', 'AnnualRevenue', 'CreatedDate', 'Phone'
};
if (!allowedSortFields.contains(sortField)) {
sortField = 'Name';
}
// Build dynamic query
String query = 'SELECT Id, Name, Industry, AnnualRevenue, Phone, CreatedDate ' +
'FROM Account ' +
'WITH SECURITY_ENFORCED ' +
'ORDER BY ' + sortField + ' ' + sortDir + ' NULLS LAST ' +
'LIMIT :recordLimit OFFSET :recordOffset';
List<Account> records = Database.query(query);
// Get total count
Integer totalCount = [SELECT COUNT() FROM Account WITH SECURITY_ENFORCED];
return new PagedResult(records, totalCount);
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
//
// WRAPPER CLASSES
//
/**
* Wrapper for picklist options
*/
public class PicklistOption {
@AuraEnabled public String label;
@AuraEnabled public String value;
public PicklistOption(String label, String value) {
this.label = label;
this.value = value;
}
}
/**
* Wrapper for paginated results
*/
public class PagedResult {
@AuraEnabled public List<Account> records;
@AuraEnabled public Integer totalCount;
public PagedResult(List<Account> records, Integer totalCount) {
this.records = records;
this.totalCount = totalCount;
}
}
}