Nedir?Salesforce

Salesforce Wrapper Class Nedir? Kapsamlı Bir Rehber

Verimlilik ve kod organizasyonu, Salesforce geliştirme dünyasında kritik öneme sahiptir. Karmaşık veri yapılarıyla çalışırken, birden fazla nesnenin özelliklerini bir arada yönetmek ve görselleştirmek gerektiğinde Wrapper Class’lar, Apex geliştiricilerinin başvurduğu en güçlü araçlardan biridir. Bu yazıda, Wrapper Class kavramını anlaşılır bir şekilde açıklayarak hem yeni başlayan hem de deneyimli geliştiricilere kapsamlı bir rehber sunmayı amaçlıyoruz.

Wrapper Class Nedir?

En basit tanımıyla, Wrapper Class birden fazla veri tipini veya nesneyi tek bir nesne içerisinde gruplamak için kullanılan özel bir sınıf yapısıdır. Java gibi diğer nesne yönelimli programlama dillerinde karşımıza çıkan bu kavram, Salesforce’un Apex dilinde çok daha özel bir öneme sahiptir.

Salesforce ekosisteminde, Standard ve Custom Object’ler arasındaki ilişkileri yönetmek, SOQL sorgusu sonuçlarını manipüle etmek veya Visualforce ve Lightning bileşenlerine veri aktarmak için sıklıkla Wrapper Class’lara ihtiyaç duyarız.

Apex’in yerel sınıf yapısı kullanılarak oluşturulan bir Wrapper Class genellikle şu özelliklere sahiptir:

  • Bir veya daha fazla Salesforce nesnesinin özelliklerini içerir
  • Ek hesaplanmış alanlar veya flag’ler barındırabilir
  • Özel metotlar içerebilir
  • İç içe (nested) yapılar oluşturulabilir

Örneğin, bir Account ve ona bağlı Contact kayıtlarını tek bir yapıda yönetmek istediğinizde, bu iki nesneyi bir Wrapper Class içinde birleştirebilirsiniz.

public class AccountContactWrapper {
public Account acc { get; set; }
public List<Contact> contacts { get; set; }
public Integer contactCount { get; set; }
public Boolean hasOpportunities { get; set; }

// Constructor
public AccountContactWrapper(Account a, List<Contact> c) {
this.acc = a;
this.contacts = c;
this.contactCount = c.size();
this.hasOpportunities = false; // Varsayılan değer
}
}

Bu örnekte, standart Account ve Contact nesnelerini tek bir yapıda toplarken, aynı zamanda hesaplanmış contactCount ve flag türünde hasOpportunities gibi ek özellikler ekleyerek veri yapısını zenginleştiriyoruz.

Avantajları Nelerdir?

Bu özel sınıf yapıları Salesforce geliştirme süreçlerinde birçok avantaj sunar:

İlişkili Verileri Birleştirme

Bazı senaryolarda standart objelerin yanı sıra özel objelerle çalışırken, bunları tek bir veri yapısında birleştirmek gerekebilir. Wrapper Class’lar, ilişkili ancak farklı nesne tiplerindeki verileri bir araya getirmenizi sağlar.

public class ProductInventoryWrapper {
public Product2 product { get; set; }
public List<PricebookEntry> priceEntries { get; set; }
public InventoryCustom__c inventory { get; set; }
public Boolean isAvailable { get; set; }
}

UI Bileşenleriyle Entegrasyon

Visualforce veya Lightning bileşenlerinde kullanılacak verileri hazırlarken, bu yapılar özellikle değerlidir. UI’da gösterilecek veri ile backend verisi arasında bir köprü görevi görürler.

public class OpportunityWrapper implements Comparable {
public Opportunity opp { get; set; }
public String statusIcon { get; set; }
public String cssClass { get; set; }
public Double weightedAmount { get; set; }

// Comparable interface için gerekli metot
public Integer compareTo(Object objToCompare) {
OpportunityWrapper wrapper = (OpportunityWrapper)objToCompare;
if (weightedAmount > wrapper.weightedAmount) return 1;
if (weightedAmount < wrapper.weightedAmount) return 1;
return 0;
}
}

Özel Sıralama ve Filtreleme

Standart sıralama ve filtreleme kabiliyetlerinin ötesine geçmek istediğinizde, Comparable interface’i ile birlikte kullanılarak Wrapper Class’lar karmaşık sıralama algoritmalarını uygulayabilmenizi sağlar.

Kod Okunabilirliği ve Bakımı

İyi tasarlanmış özel sınıflar, kodunuzu daha modüler ve yeniden kullanılabilir hale getirir. Veri yapılarını açıkça tanımlayarak, ekip üyelerinin kodu anlamasını ve değiştirmesini kolaylaştırır.

Governor Limit Optimizasyonu

Apex kodu, Governor Limit olarak bilinen çeşitli sınırlamalara tabidir. Bu özel yapılar, verilerinizi daha verimli organize ederek, bu limitleri aşmadan maksimum performans elde etmenize yardımcı olur.

Temel Örnek İncelemesi

Şimdi, bu özel sınıfın nasıl oluşturulduğunu ve kullanıldığını adım adım inceleyelim. Aşağıdaki örnek, Opportunity ve ilgili Product kayıtlarını birleştiren basit bir uygulama sunmaktadır:

public class OpportunityProductWrapper {
     // Wrapper sınıfı özellikleri
public Opportunity opp { get; set; }
public List<OpportunityLineItem> products { get; set; }
public Decimal totalAmount { get; set; }
public Boolean hasProducts { get; set; }

     // Constructor
public OpportunityProductWrapper(Opportunity opp,              List<OpportunityLineItem> products) {
this.opp = opp;
this.products = products;
this.totalAmount = calculateTotalAmount();
this.hasProducts = !products.isEmpty();
}

     // Toplam tutarı hesaplayan yardımcı metot
private Decimal calculateTotalAmount() {
Decimal total = 0;
for(OpportunityLineItem oli : products) {
total += oli.TotalPrice;
}
return total;
}
}

Bu sınıfı kullanarak bir controller metodu şu şekilde oluşturulabilir:

public List<OpportunityProductWrapper> getOpportunitiesWithProducts() {
List<OpportunityProductWrapper> wrapperList = new List<OpportunityProductWrapper>();

     // Fırsatları sorgula
List<Opportunity> opportunities = [SELECT Id, Name, AccountId, Account.Name, StageName, CloseDate
FROM Opportunity
WHERE IsClosed = false
LIMIT 100];

     // Her bir fırsat için
for(Opportunity opp : opportunities) {
// İlgili ürünleri sorgula
List<OpportunityLineItem> lineItems = [SELECT Id, Product2Id, Product2.Name, Quantity, UnitPrice, TotalPrice
FROM OpportunityLineItem
WHERE OpportunityId = :opp.Id];

     // Wrapper nesnesini oluştur ve listeye ekle
OpportunityProductWrapper wrapper = new      OpportunityProductWrapper(opp, lineItems);
wrapperList.add(wrapper);
}

return wrapperList;
}

Not: Yukarıdaki örnekte, SOQL sorgusu for döngüsü içinde kullanıldığından, Governor Limitler açısından dikkatli olunmalıdır. Gerçek uygulamalarda for döngüsü içinde sorgu yapmak yerine, bulk sorgular tercih edilmelidir.

Gerçek Dünya Senaryoları

Bu özel sınıfların gerçek dünya uygulamalarını daha iyi anlamak için, bazı yaygın senaryoları inceleyelim:

Senaryo 1: Checkbox’ları İçeren Veri Tablosu

Visualforce veya Lightning sayfalarında, kullanıcıların seçim yapabileceği checkbox’lara sahip veri tabloları oluşturmak için bu yapılar ideal bir çözümdür:

public class ContactSelectWrapper {
public Contact contact { get; set; }
public Boolean selected { get; set; }
public String contactClass { get; set; }

public ContactSelectWrapper(Contact c) {
contact = c;
selected = false;
contactClass = c.Email != null ?hasEmail:noEmail‘;
}
}

Controller:

public class ContactSelectionController {
public List<ContactSelectWrapper> contactWrappers { get; set; }

    public ContactSelectionController() {
contactWrappers = new List<ContactSelectWrapper>();
for(Contact c : [SELECT Id, Name, Email, Phone FROM Contact LIMIT 50]) {
contactWrappers.add(new ContactSelectWrapper(c));
}
}

public List<Contact> getSelectedContacts() {
List<Contact> selectedContacts = new List<Contact>();
for(ContactSelectWrapper wrap : contactWrappers) {
if(wrap.selected) {
selectedContacts.add(wrap.contact);
}
}
 return selectedContacts;
}
}

Senaryo 2: İç İçe İlişkileri Yönetme

Account, Contact ve Case gibi çok katmanlı ilişkileri yönetmek için iç içe yapılar oluşturabilirsiniz:

public class AccountHierarchyWrapper {
public Account account { get; set; }
public List<ContactCasesWrapper> contactWrappers { get; set; }
public Integer totalCaseCount { get; set; }

public AccountHierarchyWrapper(Account a, List<Contact> contacts, Map<Id, List<Case>> casesByContactId) {
this.account = a;
this.contactWrappers = new List<ContactCasesWrapper>();
this.totalCaseCount = 0;

for(Contact c : contacts) {
List<Case> contactCases = casesByContactId.containsKey(c.Id) ? casesByContactId.get(c.Id) : new List<Case>();
ContactCasesWrapper wrapper = new ContactCasesWrapper(c, contactCases);
this.contactWrappers.add(wrapper);
this.totalCaseCount += wrapper.cases.size();
}
}

// İç içe Wrapper Class
public class ContactCasesWrapper {
public Contact contact { get; set; }
public List<Case> cases { get; set; }
public Boolean hasOpenCases { get; set; }

public ContactCasesWrapper(Contact c, List<Case> caseList) {
this.contact = c;
this.cases = caseList;
this.hasOpenCases = false;

for(Case cs : caseList) {
if(cs.Status != ‘Closed’) {
this.hasOpenCases = true;
break;
}
}
}
}
}

Senaryo 3: Özel Sıralama

Standart sıralama yeteneklerinin ötesine geçmek için Comparable interface’i ile bu özel yapıları kullanabilirsiniz:

public class LeadScoreWrapper implements Comparable {
public Lead lead { get; set; }
public Decimal score { get; set; }
 public String priority { get; set; }

public LeadScoreWrapper(Lead ld, Decimal calculatedScore) {
this.lead = ld;
this.score = calculatedScore;

// Skora göre öncelik belirleme
if(score >= 80) {
this.priority = ‘Yüksek’;
} else if(score >= 50) {
this.priority = ‘Orta’;
} else {
this.priority = ‘Düşük’;
}
}

// Comparable interface metodu
public Integer compareTo(Object objToCompare) {
LeadScoreWrapper compareToWrap = (LeadScoreWrapper) objToCompare;

// Skorla karşılaştır (azalan sıralama için)
if (score > compareToWrap.score) {
return 1;
} else if (score < compareToWrap.score) {
return 1;
else {
// Skorlar eşitse, lead adına göre sırala
return lead.Name.compareTo(compareToWrap.lead.Name);
}
}
}

Bu özel sınıfı kullanarak, Lead’leri özel bir skorlama algoritmasına göre sıralayabilirsiniz:

List<LeadScoreWrapper> leadWrappers = new List<LeadScoreWrapper>();
// Lead’leri doldur ve skorla
// …

// Sırala
leadWrappers.sort();

İleri Düzey Teknikler

Daha etkili kullanım için bazı ileri düzey teknikleri inceleyelim:

Dynamic Wrapper Class

Apex’in Type sınıfı kullanılarak, dinamik olarak bu özel sınıflar oluşturabilirsiniz:

public class DynamicWrapper {
private Map<String, Object> fields = new Map<String, Object>();

     public Object get(String fieldName) {
        return fields.get(fieldName);
}

     public void set(String fieldName, Object value) {
fields.put(fieldName, value);
}

     public Set<String> getFieldNames() {
        return fields.keySet();
}
}

JSON Serileştirme ve Deserileştirme

Bu özel yapılar, JSON serileştirme ve deserileştirme işlemlerinde kullanılan sınıflar oluşturmak için mükemmeldir:

public class APIResponseWrapper {
    public Integer statusCode { get; set; }
    public String message { get; set; }
    public List<ResultWrapper> results { get; set; }

public class ResultWrapper {
    public String id { get; set; }
    public String name { get; set; }
    public Decimal value { get; set; }
}

public static APIResponseWrapper parse(String jsonString) {
     return (APIResponseWrapper) JSON.deserialize(jsonString, APIResponseWrapper.class);
}
}

Batch Apex Uygulamaları

Batch Apex işlemlerinde veri taşıyıcı olarak kullanıldığında bu sınıflar oldukça faydalıdır:

public class AccountProcessBatch implements Database.Batchable<AccountProcessWrapper> {
     private List<AccountProcessWrapper> wrapperList;

     public AccountProcessBatch(List<AccountProcessWrapper> wrappers) {
         this.wrapperList = wrappers;
}

     public List<AccountProcessWrapper> start(Database.BatchableContext bc) {
         return wrapperList;
}

     public void execute(Database.BatchableContext bc,      List<AccountProcessWrapper> scope) {
// İşlem yapılacak
         List<Account> accountsToUpdate = new List<Account>();

         for(AccountProcessWrapper wrapper : scope) {
         // Wrapper verilerini kullanarak Account’u güncelle
wrapper.account.Description = ‘Processed: ‘ + wrapper.processType;
accountsToUpdate.add(wrapper.account);
}

        update accountsToUpdate;
}

     public void finish(Database.BatchableContext bc) {
// Tamamlama işlemleri
}
}

     public class AccountProcessWrapper {
        public Account account { get; set; }
        public String processType { get; set; }
        public Boolean isSuccess { get; set; }

        public AccountProcessWrapper(Account acc, String processType) {
            this.account = acc;
            this.processType = processType;
            this.isSuccess = false;
}
}

Factory Pattern Uygulaması

Büyük projelerde, bu özel sınıfları oluşturmak için Factory Pattern kullanabilirsiniz:

public class WrapperFactory {
     // Account Wrapper oluşturucu
public static AccountWrapper createAccountWrapper(Account acc) {
return new AccountWrapper(acc);
}

     // Opportunity Wrapper oluşturucu
public static OpportunityWrapper createOpportunityWrapper(Opportunity opp) {
return new OpportunityWrapper(opp);
}

    // Diğer Wrapper oluşturucular…
}

Yaygın Hatalar ve Çözümleri

Bu özel sınıfları kullanırken karşılaşabileceğiniz bazı yaygın hatalar ve çözümleri aşağıda verilmiştir:

Governor Limit Aşımı

Sorun: For döngüsü içinde SOQL sorgusu kullanarak bir liste oluştururken Governor Limit’e takılabilirsiniz.

Çözüm: Bulk sorgular ve Map yapılarını kullanarak verimliliği artırın:

// Hatalı yaklaşım (Governor Limit riski):
List<AccountWrapper> wrappers = new List<AccountWrapper>();
for(Account acc : [SELECT Id, Name FROM Account LIMIT 100]) {
List<Contact> contacts = [SELECT Id, Name FROM Contact WHERE AccountId = :acc.Id];
wrappers.add(new AccountWrapper(acc, contacts));
}

// Doğru yaklaşım:
List<AccountWrapper> wrappers = new List<AccountWrapper>();
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 100];
Map<Id, List<Contact>> contactsByAccountId = new Map<Id, List<Contact>>();

// İlk önce tüm Account ID’lerini topla
Set<Id> accountIds = new Set<Id>();
for(Account acc : accounts) {
accountIds.add(acc.Id);
}

// Tek bir sorguda tüm ilgili Contact’ları getir
for(Contact c : [SELECT Id, Name, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
if(!contactsByAccountId.containsKey(c.AccountId)) {
contactsByAccountId.put(c.AccountId, new List<Contact>());
}
contactsByAccountId.get(c.AccountId).add(c);
}

// Şimdi Wrapper’ları oluştur
for(Account acc : accounts) {
List<Contact> contacts = contactsByAccountId.containsKey(acc.Id)
? contactsByAccountId.get(acc.Id)
: new List<Contact>();
wrappers.add(new AccountWrapper(acc, contacts));
}

Heap Size Limiti

Sorun: Büyük veri kümeleriyle çalışırken, tüm verileri bellekte tutmak Heap Size limitini aşmanıza neden olabilir.

Çözüm: Tembel yükleme (lazy loading) tekniklerini kullanın veya veri kümesini daha küçük parçalara bölün:

public class LazyLoadWrapper {
private Id recordId;
private SObject record;

public LazyLoadWrapper(Id recordId) {
this.recordId = recordId;
}

     // Tembel yükleme – yalnızca istendiğinde veriyi yükle
public SObject getRecord() {
if(record == null) {
// Dinamik SOQL ile veriyi yükle
String objectType =             recordId.getSObjectType().getDescribe().getName();
String query = ‘SELECT Id, Name FROM + objectType + ‘ WHERE Id = :recordId LIMIT 1’;
record = Database.query(query)[0];
}
return record;
}
}

Serileştirme Sorunları

Sorun: Bu özel sınıfları JSON olarak serileştirirken transient olmayan iç içe objelerde sorunlar yaşanabilir.

Çözüm: Transient anahtar kelimesini kullanın veya özel bir serileştirme metodu ekleyin:

public class SerializableWrapper {
public String name { get; set; }
public Decimal value { get; set; }

     // Serileştirme sırasında dikkate alınmayacak
public transient SObject sourceRecord;

     // Özel serileştirme metodu
public String toJson() {
Map<String, Object> serializableMap = new Map<String, Object>();
serializableMap.put(‘name’, name);
serializableMap.put(‘value’, value);
// Özel dönüşümler…

return JSON.serialize(serializableMap);
}
}

Geliştirme Süreçlerinde Bir Adım Öne Çıkın

Bu özel sınıf yapıları, karmaşık veri yapılarını yönetmek, kullanıcı arayüzü bileşenleriyle etkileşim kurmak ve kod verimliliğini artırmak için Salesforce geliştirme süreçlerinde güçlü bir araçtır. Bu makalede ele aldığımız temel kavramlar, örnekler ve ileri düzey teknikler, Wrapper Class’ları projelerinizde etkili bir şekilde kullanmanıza yardımcı olacaktır.

İyi tasarlanmış özel sınıflar kodunuzun okunabilirliğini, bakımını ve ölçeklenebilirliğini artırarak, Salesforce uygulamalarınızı daha profesyonel ve verimli hale getirecektir. Governor Limit’leri aşmadan verimli kod yazma konusunda dikkatli olduğunuz sürece, bu yapılar Salesforce geliştirme becerinizi bir üst seviyeye taşıyacaktır.

İlgili Makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu