"Apex Code"의 두 판 사이의 차이
잔글 (→sObject 정보 획득) |
잔글 |
||
1,642번째 줄: | 1,642번째 줄: | ||
} | } | ||
− | ==참고 문헌== | + | == 참고 문헌 == |
+ | |||
*[http://www.salesforce.com/ 세일즈포스닷컴] | *[http://www.salesforce.com/ 세일즈포스닷컴] | ||
*[http://www.salesforce.com/appexchange/ AppExchange] | *[http://www.salesforce.com/appexchange/ AppExchange] | ||
1,651번째 줄: | 1,652번째 줄: | ||
*[http://www.peteyg.com/SFDC/DevForce/dev_lifecycle/index_Left.htm Development Lifecycle Guide] | *[http://www.peteyg.com/SFDC/DevForce/dev_lifecycle/index_Left.htm Development Lifecycle Guide] | ||
− | + | == 지원 업체 == | |
− | [[Category:Salesforce]] | + | {{지원업체}} |
− | [[Category:Cloud]] | + | [[Category:Salesforce|Category:Salesforce]]<br/>[[Category:Cloud|Category:Cloud]] |
2018년 1월 31일 (수) 13:23 판
Force.com중 Apex Code를 정리 합니다.
목차
- 1 Apex Code 문법
- 2 Control - Class
- 3 Control - Testing Apex
- 4 Control - Trigger
- 5 Control - Scheduler
- 6 Control - Batch Apex
- 7 Control - Web Service
- 8 Control - HTTP
- 9 Control - RemoteAction
- 10 Control - Email Service
- 11 Custom Setting
- 12 Sandbox
- 13 Deploy
- 14 Force.com 내장 개체
- 15 Dynamic Apex
- 16 Managed Sharing
- 17 JSONObject
- 18 Apex Code 개발
- 19 참고 문헌
- 20 지원 업체
Apex Code 문법
변수 타입
String |
|
배열 |
|
Integer, Long |
|
Decimal, Double |
|
Boolean |
|
Date, Datetime |
|
ID |
|
Object | |
sObject |
|
Blob | |
this | |
List |
|
Set |
|
Map |
|
Enums |
|
- Primitive Data Types
Boolean : true, false Date Datetime Double ID : 18 Character Apex record identifier Integer String 배열 : [] 로 표시
- SObject Type, SObject Fields
- Lists
List<String> zzStr = new List<String>(); zzStr.set(0, "tmpStr"); zzStr.get(0); zzStr.clear();
- Sets
Set<String> zzStr = new Set<String>(); zzStr.add("tmpStr"); zzStr.remove(1);
- Maps
Map<String, String> zzStr = new Map<String, String>(); zzStr.put("zzName", "zzValue"); zzStr.get("zzName");
- Not Support DML Statements
- User, Profile, Territory, RecordType, Transaction,
- WebLink, BusinessHours, BusinessProcess, CategoryNode, Process
- UserInfo
- Currency
- Integer, Double, Boolean, String, Datetime, Date, Math
- List, Set, Map, SObject, Exception
Data 지정 방법
- POJO (Plan Old Java Object)
- obj.name
- obj['name']
- obj.getName(), obj.setName(value)
- obj.get('name'), obj.set('name', value)
- obj.name[0]
제어문
if (~) { } else { }
Loops
- continue, break
do { statement; break; continue; } while (Boolean_condition); while (Boolean_condition) { statement; } for (initialization; Boolean_exit_condition; increment) { statement; } for (variable : array_or_set) { statement; } for (variable : [inline_soql_query]) { statement; }
Exception
try { throw ~; } catch (Exception ex) { //--- System.ArrayException, DmlException, MathException, NullPointerException //--- QueryException, StringException, TypeException } finally { }
Control - Class
Class 정의
- "설정 -> App 설정 -> 개발 -> Apex 클래스" 메뉴
public | private | global [virtual | abstract | with sharing | without sharing] class | interface ClassName [implements InterfaceNameList] [extends ClassName] { [public | private | protected | global] [static] [final] String var = value; [public | private | protected | global] [override] [static] String func() { } }
- with sharing
- <- user’s permissions, field-level security, sharing rules
- 소유자 (사용자)
- 역할
- 공유 설정
- 프로필
- Field Level Security
- Transient String name; //--- view state에 저장되지 않음
Class 상속
- virtual -> extend or override, abstract -> override
- this, super
public virtual class Parent { public virtual String getStrParent() { return ‘Parent String’; } } public class Child extends Parent { public Child() { super(); } public override String getStrParent() { return super.getStrParent() + ‘ : Child String’; } }
public virtual class Parent { public String strParent = null; public Parent() { } public void Func01() { } public String getStrParent() { return strParent; } } public class Child extends Parent { public Child() { super(); //--- Parent의 생성자를 호출 합니다. //--- TODO : 여기에 코딩 } //--- Parent의 Method를 재설정 합니다. public override void Func01() { getStrParent(); } }
- Interface 상속
public virtual interface Parent { } public interface Child extends Parent { }
- 상속 확인
- A instanceof B
Apex Properties
- Apex Properties 정의
[public | private | protected | global] [static, virtual, abstract, overrid] String var { [public | private | protected] get; [~] set;}
- Apex Properties
- set 함수에서 value는 System이 생성하여 전달하는 인수값 입니다.
public class BasicClass { public String varName { get { return varName; } set { name = value; } } }
Parameterized Interface
public virtual interface Pair<T, U> { public T getFirest(); public void setFirst(T val); } public StringPair implements Pair<String, String> { }
Annotations
- @deprecated :
- @future : 비동기적으로 실행되는 Method, 200 호출 / user, 24시간
- @isTest : 테스트 클래스 표시
- @ReadOnly : in Web services, Schedulable interface
- @RemoteAction : JavaScript에서 함수 호출
내장 변수
- {!$ObjectType.Account} === Schema.SObjectType.Account.getKeyPrefix()
- {!$ObjectType.Account.fields.Name.label}
- Object, sObject
- NS__Object__c, NS__Field__c / NS.Class.Method()
- Iterable, Iterators -> Database.batchable
Control - Testing Apex
Apex Code로 프로그램을 작성하면 이를 배포하기 위해서는 전체 코딩된 라인중 75% 이상이 테스트 되어야 합니다. (Code Coverage Result가 75% 이상) Force.com에서 제안하는 테스트 방식을 살펴보면 해당 코드가 한번 이상 수행이 되면 테스트가 된 것으로 처리를 하고 있습니다. 따라서 Code Coverage Result를 높이기만을 원한다면 다양한 테스트 코드를 작성할 필요는 없고 각각의 라인이 한번 이상 실행이 되도록 테스트 코드를 작성하면 됩니다.
- Code Coverage Result를 높이는 방법
- 분기문, 제어문 등에서 각각의 코드 블럭이 실행될 수 있도록 데이터를 구성 합니다. (추천)
- 테스트가 완료된 코드 블럭에 의미없는 코드를 추가하여 라인수를 늘입니다. (비추천)
Test Class 사례
- ClassName 클래스를 테스트하기 위한 Test 클래스 샘플
- ClassName의 모든 라인이 수행될 수 있도록 Test 클래스를 작성하여야 함
- 배포 등을 위해서는 전체 라인중 75%가 테스트(Code Coverage Result)가 되어야 함
@IsTest private class ClassNameTest { private static testMethod void testMain() { ClassName test = null; //--- Test를 위한 사용자 설정 User user = [select id from User where alias='auser']; System.RunAs(u1) { //--- Test를 위한 데이터 설정 manage = new Manage(); //--- Test 코드 작성 test = new ClassName(); test.setManage(manage); System.assert(actual==expected, 'Character.isAscii(\ + charactr + '\') returned ' + actual); System.assertEquals(singletotalMiles, totalMiles); Test.startTest(); //--- Limits를 초기화하고 테스트 시작 ~ Test.stopTest(); } } }
- Apex Batch 테스트 프로그램
@IsTest private class BatEvaluationTest { private static testmethod void testMain() { BatEvaluation test = null; List<Evaluation__c> scope = null; Test.StartTest(); //--- Apex Batch에 전달할 scope 데이터 생성 scope = new List<Evaluation__c>(); scope.add(new Evaluation__c()); //--- Apex Batch의 각 Method를 별도로 실행 test = new BatEvaluation(); test.start(null); test.execute(null, scope); test.finish(null); Test.stopTest(); } }
System.assert
- System.assert(boolean) : boolean 값이 true이면 OK
- System.assert(boolean, e.getMessage()) : boolean 값이 true이면 OK, boolean 값이 false이면 두번째 인자를 메시지로 표시
- System.assertEquals(dataA, dataB [, msg]) : dataA와 dataB의 값이 동일하면 OK
- System.assertNotEquals(dataA, dataB [, msg]) : dataA와 dataB의 값이 다르면 OK
- System.assert(Boolean[, ExceptionMessage]);
- System.assert(false);
- System.assert('a' == 'A');
- System.assertEquals(expectedValue, actualValue[, ExceptionMessage]);
- System.assertEquals('Hello to you!', sayHelloWorld('to you!'));
- System.assertNotEquals(expectedValue, actualValue[, ExceptionMessage]);
Test 실행 방법
- "설정 -> App 설정 -> 개발 -> Apex 클래스 -> 모든 테스트 실행"에서 테스트
- "설정 -> App 설정 -> 개발 -> Apex 테스트 실행"에서 테스트
- Eclipse에서 Class에서 오른쪽 마우스를 눌러 "Force.com -> Run Tests" 메뉴를 실행 합니다.
Control - Trigger
Trigger 개요
- Trigger 종류
- insert : before/after insert
- update : before/after update
- delete : before/after delete
- upsert : before/after insert/update
- merge : before/after delete, before update
- undelete : after undelete (Account, Asset, Campaign, Case, Contract, Custom objects, Event, Lead, Opportunity, Product, Solution, Task)
- Trigger 적용 예외
- Cascading delete, Mass ~, …
- Opportunity : amoutn, ForecastCategory, isWon, …
- 트리거 메뉴
- "설정 -> App 설정 -> 사용자 정의 -> '개체' -> 트리거" 메뉴
- "설정 -> App 설정 -> 작성 -> 개체 -> 트리거 '새로 만들기'"
- Trigger 변수
- Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap, Trigger.size
- Trigger
- isBefore, isAfter, isInsert, isUpdate, isDelete, isUndelete
public Integer size = Trigger.size; public PaymentType__c[] oldData = Trigger.old; public PaymentType__c[] newData = Trigger.new; Trigger.old[idx], Trigger.new[idx]
- Trigger 사례
trigger StandardTrigger on Account (before insert, before update, before delete, after insert, after update, after delete) { //--- @future, 비동기 WebService //--- sObject.addError('~'); //--- Trigger.size, oldMap, newMap, old (ReadOnly), new if (Trigger.isInsert && Trigger.isBefore) { for (Account item : Trigger.old) { } for (Account item : Trigger.new) { //--- 수정 가능 } } if (Trigger.isInsert && Trigger.isAfter) { for (Account item : Trigger.old) { } for (Account item : Trigger.new) { } } }
Triggers and Bulk Triggers
- Bulk Triggers Data import, Bulk Apex API calls, Mass actions Recursive Apex Code methods and triggers that invoke bulk DML statements trigger <triggerName> on <ObjectName> bulk (<trigger_event>) { //--- trigger_event : before insert, before update, before delete, after ~ //--- isInsert, isUpdate, isDelete, isUndelete, isBefore, isAfter //--- Trigger.new, Trigger.newMap, Trigger.old, Trigger.oldMap, Trigger.size //--- Trigger.oldMap.get(q.opportunity__c).addError('Cannot delete opportunity with a quote'); //--- Trigger.new[i].Primary__c.addError('Primary quote cannot be marked non-primary'); try { Dictionary__c obj = new Dictionary__c(Name='Dictionary deploy test'); insert obj; } catch(DmlException e) { System.assert(e.getMessage().contains('first error: FIELD_CUSTOM_VALIDATION_EXCEPTION,'), e.getMessage()); } } //--- 최대 32,000 characters trigger helloWorldAccountTrigger on Account (before insert) { //--- before insert, before update, after insert, after update, after delete //--- Trigger.isBefore Account[] accs = Trigger.new; MyHelloWorld.addHelloWorld(accs); Contact c = new Contact(lastName = 'Weissman'); c.accountId = a.Id; insert c; List<Account> aa = [select id, name from account where name = 'Acme']; c = [select account.name from contact where id = :c.id]; c.account.name = 'salesforce.com'; c.lastName = 'Roth'; update c; update c.account; upsert <SObject> <opt_external_id> delete <SObject> delete <SObject[]> System.assertEquals('xxx', a.accountNumber); } Savepoint sp = Database.setSavepoint(); Database.rollback(sp); throw <exceptionObject>; try { <code_block> } catch (<exceptionType>) { <code_block> }
Trigger Sample 1
/** * 프로그램 명 : DictionaryTrigger.trigger Trigger * 프로그램 설명 : Dictionary 개체용 Trigger 샘플 * 작성자 : 산사랑 * 작성일 : 2008.06.19 ~ 2008.06.19 * * Copyright (c) 2000-2008 pnuskgh, All rights reserved. */ trigger DictionaryTrigger on Dictionary__c (before insert, before update, before delete) { Double tmpNum = 0.0; if (Trigger.isBefore) { if (Trigger.isInsert) { for (Dictionary__c obj:Trigger.new) { obj.num__c = 1; } } if (Trigger.isUpdate) { for (Dictionary__c obj:Trigger.new) { if (obj.num__c == 11) { obj.num__c = obj.num__c + 100; } else { obj.num__c = obj.num__c + 10; } if (150 < obj.num__c) { obj.addError('Error : You can\'t update this record.'); obj.num__c.addError('Error : You can\'t update this record.'); } } } if (Trigger.isDelete) { for (Dictionary__c obj:Trigger.old) { tmpNum = obj.num__c; } } } }
Trigger Test Class Sample 1
/** * 프로그램 명 : DictionaryDeployClass Class * 프로그램 설명 : DictionaryTrigger Trigger를 테스트하는 클래스 * 작성자 : 산사랑 * 작성일 : 2008.06.19 ~ 2008.06.19 * * Copyright (c) 2000-2008 pnuskgh, All rights reserved. */ public class DictionaryDeployClass { public static testmethod void DictionaryDeployTest() { Double tmpNum = 0.0; System.debug('Start insert trigger test.'); Dictionary__c obj = new Dictionary__c(Name='Dictionary deploy test'); insert obj; obj = [select Id, Name, num__c from Dictionary__c where Id = :obj.Id]; System.assertEquals(1, obj.num__c); System.debug('Start update trigger test.'); tmpNum = obj.num__c + 10; update obj; obj = [select Id, Name, num__c from Dictionary__c where Id = :obj.Id]; System.assertEquals(tmpNum, obj.num__c); try { System.debug('Start delete trigger test.'); delete obj; obj = [select Id, Name, num__c from Dictionary__c where Id = :obj.Id]; } catch(QueryException e) { System.debug(e.getMessage()); System.assert(e.getMessage().contains('List has no rows for assignment to SObject'), e.getMessage()); } } }
Trigger Sample 2
/** * 프로그램 명 : OpportunityTrigger.trigger Trigger * 프로그램 설명 : Forecast 데이터만 영업기회에서 분리하여 관리 * 작성자 : 산사랑 * 작성일 : 2008.06.20 ~ 2008.06.20 * * Copyright (c) 2000-2008 pnuskgh, All rights reserved. */ trigger OpportunityTrigger on Opportunity (before insert, after insert, before update, before delete) { if (Trigger.isAfter) { if (Trigger.isInsert) { for (Opportunity opp:Trigger.new) { Forecast__c obj = new Forecast__c(OwnerId = opp.OwnerId, Name = opp.Name, Amount__c = opp.Amount__c, GP__c = opp.GP__c, Probability__c = opp.Probability, opportunity__c = opp.Id); insert obj; } } } if (Trigger.isBefore) { if (Trigger.isUpdate) { for (Opportunity opp:Trigger.new) { Forecast__c obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c from Forecast__c where opportunity__c = :opp.Id]; obj.OwnerId = opp.OwnerId; obj.Name = opp.Name; obj.Amount__c = opp.Amount__c; obj.GP__c = opp.GP__c; obj.Probability__c = opp.Probability; update obj; } } if (Trigger.isDelete) { for (Opportunity opp:Trigger.old) { Forecast__c obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c from Forecast__c where opportunity__c = :opp.Id]; delete obj; } } } }
Trigger Test Class Sample 2
/** * 프로그램 명 : OpportunityDeployClass Class * 프로그램 설명 : OpportunityTrigger Trigger를 테스트하는 클래스 * 작성자 : 산사랑 * 작성일 : 2008.06.20 ~ 2008.06.20 * * Copyright (c) 2000-2008 pnuskgh, All rights reserved. */ public class OpportunityDeployClass { public static testmethod void OpportunityDeployTest() { Boolean flagError = false; Forecast__c obj = Null; System.debug('Start insert trigger test.'); Opportunity opp = new Opportunity(Name = 'Deploy test', Amount__c = 100, GP__c = 20, StageName = '수주확신(90%)', CloseDate = System.today()); insert opp; opp = [select Id, OwnerId, Name, Amount__c, GP__c, Probability from Opportunity where Id = :opp.Id]; obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c from Forecast__c where opportunity__c = :opp.Id]; System.assertEquals(opp.OwnerId, obj.OwnerId); System.assertEquals(opp.Name, obj.Name); System.assertEquals(opp.Amount__c, obj.Amount__c); System.assertEquals(opp.GP__c, obj.GP__c); System.assertEquals(opp.Probability, obj.Probability__c); System.assertEquals(opp.Id, obj.opportunity__c); System.debug('Start update trigger test.'); opp.GP__c = 30; update opp; obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c from Forecast__c where opportunity__c = :opp.Id]; System.assertEquals(opp.OwnerId, obj.OwnerId); System.assertEquals(opp.Name, obj.Name); System.assertEquals(opp.Amount__c, obj.Amount__c); System.assertEquals(opp.GP__c, obj.GP__c); System.assertEquals(30, obj.GP__c); System.assertEquals(opp.Probability, obj.Probability__c); System.assertEquals(opp.Id, obj.opportunity__c); try { System.debug('Start delete trigger test.'); delete opp; obj = [select Id, OwnerId, Name, Amount__c, GP__c, Probability__c, opportunity__c from Forecast__c where opportunity__c = :opp.Id]; } catch(QueryException e) { if (e.getMessage().contains('List has no rows for assignment to SObject')) { flagError = true; } else { throw e; } } System.assert(flagError); } }
Control - Scheduler
- "설정 -> App 설정 -> 개발 -> Apex 클래스 -> Apex 예약" 메뉴
- "설정 -> 관리 설정 -> 모니터링 -> 예약된 작업" 메뉴에서 모니터링
- Scheduler 생성
global class StandardScheduler implements Schedulable { global void execute (SchedulableContext sc) { Batchable batch = null; //--- sc.getTriggerId() CronTrigger 개체 batch = new Batchable(); Database.executebatch(batch); } }
- Scheduler 사용법
- 초(0-59) 분(0-59) 시(0-23) 일(1-31) � 월(1-12) 주(1-7, 1.일요일) 년(1970-2099)
String sch = ‘20 30 8 10 2 ? * ‘; String id = system.schedule(‘name’, sch, StadnardScheduler);
Control - Batch Apex
Database.Batchable Interface
Apex Code에서 Batch 프로그램을 작성하려면 Database.Batchable Interface를 구현 하여야 합니다.
- global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {}
- execute Method에서 처리할 레코드를 수집하여 반환 합니다.
- global void execute(Database.BatchableContext BC, list<P>) {}
- 실제로 Batch 처리가 이루어지는 method 입니다.
- Default로 200개의 records씩 전달되어 실행이 되며 각 execute은 별개의 Transaction으로 처리가 됩니다.
- global void finish(Database.BatchableContext BC) {}
- Batch 처리가 모두 완료된 후에 호출되는 method 입니다.
Database.AllowsCallouts Interface
Apex Batch에서 HTTP request를 호출하는 프로그램을 작성하려면 Database.AllowsCallouts Interface를 implement 하여야 합니다.
Database.Stateful Interface
Apex Batch 처리를 위한 Class에서 선언한 변수의 값을 계속 유지하려면 Database.Stateful Interface를 implement 하여야 합니다.
Database.BatchableContext
- getJobId() : 해당 Apex Batch Job의 ID를 반환 합니다.
Database
- executeBatch Method : Apex Batch를 실행 합니다.
ID batchId = null; BatSession batch = null; batch = new BatSession(); batchId = Database.executeBatch(batch); //batchId = Database.executeBatch(batch, 200); //--- 200. 한번에 처리하는 레코드 갯수
Batch Apex 사례
- Batch Apex 선언
- Database.AllowsCallouts Interface : HTTP 호출시 implement
- Database.Stateful : Class에서 선언한 변수 값을 유지하려면 implement
global class BatAccount implements Database.Batchable<Account>, Database.Stateful { global Database.QueryLocator start(Database.BatchableContext ctx) { return Database.getQueryLocator([SELECT Id, Name FROM Account]); } global void execute(Database.BatchableContext ctx, List<Account> scope) { for (Account item : scope) { } } global void finish(Database.BatchableContext ctx) { ID id = ctx.getJobID(); //--- AsyncApexJob 개체 } }
- Batch Apex 사례
global class BatSession implements Database.Batchable<sObject>, Database.Stateful { global Database.QueryLocator start(Database.BatchableContext ctx) { Datetime tmpDate = null; tmpDate = Datetime.now(); tmpDate.addDays(-3); return Database.getQueryLocator([SELECT Id FROM Session__c WHERE CreatedDate < :tmpDate]); } global void execute(Database.BatchableContext ctx, List<sObject> scope) { delete scope; } global void finish(Database.BatchableContext ctx) { AsyncApexJob job = null; AsyncApexJob job = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE Id = :ctx.getJobId()]; } }
Batch Job 모니터링
- AsyncApexJob : Batch 진행 사항을 저장
- "설정 -> 관리 설정 -> 모니터링 -> Apex 작업" 메뉴에서 모니터링
- Apex 작업
Batch 제약 사항
- start method에서 QueryLocator를 사용할 경우 최대 5000만 레코드만 처리가 가능 합니다.
- start method에서 Iterable을 사용할 경우 최대 레코드 제약은 그대로 적용이 됩니다.
- Apex governor limits are reset for each execution of execute
- 동시에 최대 5개의 Batch Job만 처리가 가능 합니다.
- A user can have up to 5 query cursors open at a time
- Apex query cursors, Batch cursors, Visualforce cursors는 각각 최대 5개까지 동시에 사용이 가능 합니다.
- Aggregate Query는 Batch Apex에서 start 함수의 결과로써 사용할 수 없습니다.
Control - Web Service
- Web Services 생성 in Apex Code
global class StandardWebServices { webService static String getTitle(String title, Account argAccount) { return '[' + title + ']'; } }
- Web Services 호출 in Visualforce Page
<script type="text/javascript"> var __sfdcSessionId = "{!GETSESSIONID()}"; </script> <script type="text/javascript" src="/soap/ajax/22.0/connection.js"></script> <script type="text/javascript" src="/soap/ajax/22.0/apex.js"></script> var account = sforce.sObject("Account"); //--- id, type, name var title = sforce.apex.execute(“StandardWebServices”, “getTitle”, {title:”~”, argAccount:account});
- sforce.debug.trace = true;
Control - HTTP
Http http = null; HttpRequest req = null; HttpResponse res = null; String strHeader = null; String contentType = null, response = null; try { req = new HttpRequest(); req.setEndpoint('http://www.apexdevnet.com/'); req.setMethod('GET'); req.setHeader(‘Content-Type’, ‘application/soap+xml’); req.setTimeout(60000); req.setBody(‘name1=value1&name2=value2’); req.setCompressed(false); //--- Basic Authencation strHeader = EncodingUtil.base64Endoce(Blob.valueOf(userid + ‘:’ + pwd)) req.setHeader(‘Authorization’, ‘BASIC ‘ + strHeader); http = new Http(); res = http.send(req); if (res.getStatusCode() == 404) { } else { contentType = resp.getHeader('Content-Type'); response = resp.getHeader('Content-Length'); this.theXMLDom = new xmldom(res.getBody()); dom.document doc = res.getBodyDocument(); dom.XmlNode [] node = doc.getRootElement().getChildElements(); //--- Atachement로 저장 Attachment attach = new Attachment(); attach.Name = “~’; attach.ContentType = “~”; attach.body = Blob.valueOf(res.getBody()); attach.ParenetId = ~; insert attach; } } catch (System.CalloutException e) { this.theXMLDom = null; }
Control - RemoteAction
RemoteAction은 Visualforce Page에서 JavaScript를 사용하여 Controller의 Method를 호출하는 방법 입니다.
- Apex Code
global class MyJSController { public String accountName { get; set; } public static Account account { get; set; } @RemoteAction global static Account getAccount(String accountName) { account = [ SELECT id, name, phone FROM Account WHERE name = :accountName ]; return account; } }
- Visualforce Page
<apex:page controller="MyJSController“> <script type="text/javascript"> var accountNameJS = null; accountNameJS = "오픈소스 비즈니스 컨설팅"; MyJSController.getAccount( accountNameJS, //--- 전달되는 인자 function(result, event) { //--- result : 반환 값 if (event.status) { //--- 정상 처리 //--- 반환된 값은 result.name //--- 반환된 배열 result[2].name } else if (event.type === 'exception') { //--- Exception 오류 처리 } else { //--- 오류 처리 } }, {escape:true} ); </script> </apex:page>
- 참고 문헌
Control - Email Service
- Single Mail 발송
Messaging.SingleEmailMessage email = null; Messaging.EmailFileAttachment file = null; Messaging.SendEmailResult[] result = null; email = new Messaging.SingleEmailMessage(); email.setSubject(String); email.setToAddress(String[]); email.setPlainTextBody(String); file = new Messaging.EmailFileAttatchment(); file.setFileName(‘~’); file.setBody(Blob); email.setFileAttachments(new Messaging.EmailFileAttachment[] {file}); result = Messaging.sendEmail(new Messaging.SingleEmailMessag[] {email});
Custom Setting
- Setting__c.getValues(Name).Value__c
- Setting__c : Custom Setting 개체명
- Name : 레코드의 Name
- Value__c : 레코드의 Value__c라는 필드의 필드값
Sandbox
- Sandbox 명이 de일 경우, Sandbox의 로그인 아이디는 userid.de 입니다.
Deploy
- Eclipse에서 deploy
- 클래스를 선택한 후 오른쪽 마우스를 누릅니다.
- "Force.com -> Deploy to Server..." 메뉴를 선택하여 deploy 합니다.
- Setup에서 deploy
- 운영의 "설정 -> App 설정 -> 배치 -> 연결 배포"에서 "인바운드 변경 허용"을 선택 합니다.
- Sandbox의 "설정 -> App 설정 -> 배치 -> 아웃바운드 변경 세트"에서 변경세트를 작성하고 업로드 합니다.
- 운영의 "설정 -> App 설정 -> 배치 -> 인바운드 변경 세트"에서 업로드된 변경 세트를 배포 합니다.
- Deploy의 제약 사항
- Class와 Trigger를 75% 이상 테스트 되어야 합니다.
- 하나의 class가 deploy 되더라도 모든 class에서 검증이 들어 갑니다. (Source Coverage의 평균이 75% 이상이 되어야 합니다.)
- Test Class는 실제 운영 서비스에 있는 테스트 클래스가 실행 되므로 먼저 테스트 클래스부터 deploy 해야 합니다.
Force.com 내장 개체
System
- Debug
- System.debug(Message);
- System.debug(logLevel, Message);
- ERROR, WARN, INFO, DEBUG, FINE, FINER, FINEST
- Date and Time
- System.today() : 오늘 날자를 반환한다.
- System.now() : 오늘 날자와 시간을 반환한다.
암호화
- 암호화 key 생성
- Blob key = Crypto.generateAesKey(256)
- 암호화
- String 암호문 = EncodingUtil.base64Encode(Crypto.encryptWithManagedIV('AES256', key, Blob.valueOf('평문')));
- 복호화
- String 평문 = Crypto.decryptWithManagedIV('AES256', key, EncodingUtil.base64Decode('암호문')).toString();
- 해시키 생성
- '[MD5]' + 해시키 = Crypto.generateDigest('MD5', '해시키로 변환할 문자열');
- String과 Blob간 변환
- Blob에 문자열이 저장되어 있을 경우
- String strBlob = dataBlob.toString();
- Blob dataBlob = Blob.valufOf(strBlob);
- Blob에 바이너리가 저장되어 있을 경우
- String strBlob = EncodingUtil.base64Encode(dataBlob);
- Blob dataBlob = EncodingUtil.base64Decode(strBlob);
인코딩
- 인코딩 : EncodingUtil.base64Encode(~)
- 디코딩 : EncodingUtil.base64Decode(~)
- URL 인코딩 : EncodingUtil.urlEncode(~, “UTF-8”)
- URL 디코딩 : EncodingUtil.urlDecode(~, “UTF-8”)
Logging
- System Logging Levels (System.LoggingLevel enum)
- ERROR
- WARN
- INFO
- DEBUG
- FINE
- FINER
- FINEST
- 로그 남기기
- System.debug('MsgTxt'); //--- Default Loggin Level인 Logginglevel.DEBUG 가 적용됨)
- System.debug(Logginglevel.INFO, 'MsgTxt');
- Apex Code별 로그 레벨 설정
- Apex 클래스 : "설정 -> App 설정 -> 개발 -> Apex 클래스" 메뉴에서 원하는 클래스를 선택한 후 "로그 필터" 탭에서 설정
- Apex 트리거 : "설정 -> App 설정 -> 개발 -> Apex 트리거" 메뉴에서 원하는 트리거를 선택한 후 "로그 필터" 탭에서 설정
- 로그 확인
- "시스템 로그" 메뉴에서 확인
- "설정 -> 관리 설정 -> 모니터링 -> 디버그 로그"에서 확인
Dynamic Apex
sObject 정보 획득
sObject |
|
Schema.SObjectType |
|
Schema.DescribeSObjectResult |
|
Schema.SObjectField |
|
Schema.DescribeFieldResult |
|
- picklist entry
- 제약 사항
- 최대 100개의 fields 멤버를 사용할 수 있습니다.
Dynamic SOQL
- 동적으로 데이터 읽기
sObject item = Database.query(string_limit_1); List<sObject> data = Database.query(string);
- 동적으로 데이터 처리하는 샘플
List<sObject> data = null; Map<String, Schema.SObjectField> columns = null; data = Database.query(strQuery); columns = data.getSObjectType().getDescribe().fields.getMap(); for (sObject item : data) { for (String name : columns.keySet()) { string value = null; value = String.valueOf(item.get(name)); } }
- 참고 문헌
Dynamic Visualforce
- 변수 : ~.~, ~[‘~’]
- Map<String, Schema.SobjectField> = Schema.SobjectType.Account.fields.getMap();
- Schema.SobjectField
- getDescribe().isAccessible(), isCustom()
- Component.NameSpace.~
- Component.Apex.OutputText
- {!$ObjectType.Account.FieldSets.~}
- Field : Label, Type, Required, FieldPath, DBRequired
- sObject.put(~, ~), get(~)
- StandardController
- StandardSetController
- reset(), addFields(List<String>)
- Include
- <apex:include pageName=“~” />
- <apex:includeScript value=“{$Resource.~}” />
- <apex:stylesheet value=“{$Resource.~}” />�
- <apex:iframe src=“~” height=“~” width=“~” scrolling=“true” frameborder=“false” />
Managed Sharing
- 공유 권한 : 소유자 -> 역할 -> 공유 설정 / 필드 접근성
- User Managed Sharing
- Access Level : None. Private, Read. Read Only, Edit. Read/Write, All. Full Access
- 개체명__Share
- Modify All Data" 권한이 있어야 사용 가능
public class JobSharing { public Boolean manualShare(ID recordId, ID userOrGroup) { Job__Share share = new Job__Share(); share.ParentId = recordId; share.UserOrGroup = userOrGroup; //--- Job.Recruiter__c share.AccessLevel = ‘Read’; share.RowCause = Schema.Job__Share.RowCause.Manual; //--- 공유 이유가 Recruite일 경우 (사용자 정의 공유 이유) //--- share.RowCause = Schema.Job__Share.RowCause.Recruite__c; Database.insert(share); } }
JSONObject
apex-library의 JSONObject를 사용하여 JSON 데이터를 처리할 수 있습니다.
- JSONObject Class
- 생성자 : JSONObject(String source)
- Key 확인
Boolean has(String key) SET keys()
- 데이터를 저장하는 함수
JSONObject putOpt(String key, JSONObject.value value)
- 데이터 읽어 오는 함수
JSONObject.value getValue(String key) Object get(String key) Object opt(String key) //--- Default는 null을 반환 String getString(String key) //--- Integer.valueOf(string), Date.valueOf(string) 등을 사용하여 다른 type으로 변환 가능 Boolean getBoolean(String key) Boolean optBoolean(String key) //--- Default는 false Boolean optBoolean(String key, Boolean defaultValue) String valueToString()
- 데이터 읽어 오는 함수의 응용
JSONObject snbJson = new JSONObject(strJson); for (JSONObject.value jsonItem : snbJson.getValue('data').values) { JSONObject item = null; item = new JSONObject(jsonItem.valueToString()); }
- JSONObject.value Class의 속성값
JSONObject obj String str Integer num Double dnum Boolean bool List<value> values
- 참고 문헌
Apex Code 개발
Request
- Apex Code에서
ApexPages.currentPage().getParameters().get('id');
- VisualForce Page에서
{!$CurrentPage.parameters.cid}
Cookie
Cookie는 일반적으로 도메인을 기준으로 설정이 됩니다. 세일즈포스닷컴은 CRM 서비스를 제공하는 URL과 Visualforce의 URL이 다르므로 서로 다른 도메인의 URL을 가지게 됩니다.
- 세일즈포스닷컴의 URL 종류
- Saleforce.com의 URL 예) https://na7.salesforce.com/home/home.jsp
- Visualforce의 URL 예) https://c.na7.visual.force.com/apex/DeptTree
- Apex Code에서 Cookie 사용 방법
- Apex Code에서 저장한 Cookie는 이름의 앞에 "apex__"가 자동으로 붙어서 처리가 됩니다.
public class UtilCookie { public static String getCookie(String name) { Cookie cookie = null; cookie = ApexPages.currentPage().getCookies().get(name); if (cookie == null) { return null; } else { return cookie.getValue(); } } //--- 저장되는 Cookie명은 "apex__" + name 입니다. public static void setCookie(String name, String value) { Cookie cookie = null; cookie = new Cookie(name, value, null, -1, false); ApexPages.currentPage().setCookies(new Cookie[]{ cookie }); } }
- JavaScript에서 Cookie 사용 방법
<script type="text/javascript"> var APEX_PREFIX = "apex__"; //--- Cookie를 저장한다. function setCookieTime(name, value, mSecond) { var today = new Date(); var expire = new Date(today.getTime() + mSecond); window.document.cookie = name + "=" + escape(value) + ((expire) ? "; expires=" + expire.toGMTString() : ""); } function setCookie(name, value, days) { setCookieTime(name, value, days * 24 * 60 * 60 * 1000); } //--- Cookie를 가져온다. function getCookie(uName) { var strCookie = " " + window.document.cookie; var ptrFr = strCookie.indexOf(" " + uName + '='); if (ptrFr != -1) { ptrFr = ptrFr + uName.length + 2; ptrTo = strCookie.indexOf(';', ptrFr); if (ptrTo == -1) { ptrTo = strCookie.length; } return unescape(strCookie.substring(ptrFr, ptrTo)); } else { return ""; } } </script>
Session
사용자 세션을 활용하여 세션 정보를 관리하는 모듈을 작성 합니다.
- Session의 종류
- 사용자 세션 (Apex Code 세션)
UserInfo.getSessionID();
- API용 세션 (AJAX Toolkit 세션)
{!$Api.Session_ID} var __sfdcSessionId = "{!GETSESSIONID()}"; //--- sforce.connection.sessionId 에 저장되어 사용됨
- Salesforce 서비스의 세션
- 도메인이 달라 다른 세션이 생성 됩니다.
- https://na7.salesforce.com/home/home.jsp
- Session__c 개체
- DaoSession
public class DaoSession { public String sessionId {get; set;} public DaoSession(String argSessionId) { sessionId = argSessionId; } public String findData(String argName) { Session__c session = null; session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate FROM Session__c WHERE SessionId__c = :sessionId AND Name__c = :argName LIMIT 1]; return session.Value__c; } public void insertData(String argName, String argValue) { Session__c session = null; session = new Session__c(); session.SessionId__c = sessionId; session.Name__c= argName; session.Value__c = argValue; insert session; } public void updateData(String argName, String argValue) { Session__c session = null; session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate FROM Session__c WHERE SessionId__c = :sessionId AND Name__c = :argName LIMIT 1]; session.Value__c = argValue; update session; } public Void upsertData(String argName, String argValue) { Session__c session = null; try { session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate FROM Session__c WHERE SessionId__c = :sessionId AND Name__c = :argName LIMIT 1]; } catch (Exception ex) { } if (session == null) { session = new Session__c(); session.SessionId__c = sessionId; session.Name__c= argName; session.Value__c = argValue; insert session; } else { session.Value__c = argValue; update session; } } public Void deleteData(String argName) { Session__c session = null; session = [SELECT Id, Name, SessionId__c, Name__c, Value__c, LastModifiedDate FROM Session__c WHERE SessionId__c = :sessionId AND Name__c = :argName LIMIT 1]; delete session; } }
- MgrSession
public class MgrSession { public DaoSession dao = null; public MgrSession() { dao = new DaoSession(UserInfo.getSessionId()); } public String getSession(String argName) { return dao.findData(argName); } public Void setSession(String argName, String argValue) { dao.upsertData(argName, argValue); } }
- SchSession
- "App 설정 -> 개발 -> Apex 클래스" 메뉴에서 "Apex 예약"에서 등록
- "관리 설정 -> 모니터링 -> 예약된 작업"에서 등록된 작업을 조회
global class SchSession implements Schedulable { global void execute(SchedulableContext sc) { BatSession batch = null; batch = new BatSession(); Database.executeBatch(batch); } }
- BatSession
global class BatSession implements Database.Batchable<sObject> { global Database.QueryLocator start(Database.BatchableContext ctx) { Datetime tmpDate = null; tmpDate = Datetime.now(); tmpDate.addDays(-3); return Database.getQueryLocator([SELECT Id FROM Session__c WHERE CreatedDate < :tmpDate]); } global void execute(Database.BatchableContext ctx, List<sObject> scope) { delete scope; } global void finish(Database.BatchableContext ctx) { } }
Config
- ConfigSetting__c Custom Settings
- MgrConfig
public class MgrConfig { public MgrConfig() { } public String getConfig(String argName) { ConfigSetting__c config = null; config = ConfigSetting__c.getValues(argName); return config.Value__c; } }
- Config__c 개체
- DaoConfig
public class DaoConfig { public static String CATEGORY = 'global'; public DaoConfig() { } public String findData(String argCategory, String argName) { Config__c config = null; config = [SELECT Id, Name, Category__c, Name__c, Value__c FROM Config__c WHERE Category__c = :argCategory AND Name__c = :argName LIMIT 1]; return config.Value__c; } public String findData(String argName) { return findData(CATEGORY, argName); } public void insertData(String argCategory, String argName, String argValue) { Config__c config = null; config = new Config__c(); config.Category__c = argCategory; config.Name__c = argName; config.Value__c = argValue; insert config; } public void insertData(String argName, String argValue) { insertData(CATEGORY, argName, argValue); } public void updateData(String argCategory, String argName, String argValue) { Config__c config = null; config = [SELECT Id, Name, Category__c, Name__c, Value__c FROM Config__c WHERE Category__c = :argCategory AND Name__c = :argName LIMIT 1]; config.Value__c = argValue; update config; } public void updateData(String argName, String argValue) { updateData(CATEGORY, argName, argValue); } public Void upsertData(String argCategory, String argName, String argValue) { Config__c config = null; try { config = [SELECT Id, Name, Category__c, Name__c, Value__c FROM Config__c WHERE Category__c = :argCategory AND Name__c = :argName LIMIT 1]; } catch (Exception ex) { } if (config == null) { config = new Config__c(); config.Category__c= argCategory; config.Name__c= argName; config.Value__c = argValue; insert config; } else { config.Value__c = argValue; update config; } } public Void upsertData(String argName, String argValue) { upsertData(CATEGORY, argName, argValue); } public Void deleteData(String argCategory, String argName) { Config__c config = null; config = [SELECT Id, Name, Category__c, Name__c, Value__c FROM Config__c WHERE Category__c = :argCategory AND Name__c = :argName LIMIT 1]; delete config; } public Void deleteData(String argName) { deleteData(CATEGORY, argName); } }
- MgrConfig
public class MgrConfig { public DaoConfig dao = null; public MgrConfig() { dao = new DaoConfig(); } public String getConfig(String argCategory, String argName) { return dao.findData(argCategory, argName); } public String getConfig(String argName) { return dao.findData(argName); } public Void setConfig(String argCategory, String argName, String argValue) { dao.upsertData(argCategory, argName, argValue); } public Void setConfig(String argName, String argValue) { dao.upsertData(argName, argValue); } }
참고 문헌
지원 업체
SuiteCRM을 사용한 영업관리, 고객관리는 아래 담당자에게 연락하여 주시면, 빠르고 친절하게 전문적인 답변을 드리겠습니다.
영업 문의 | sales@obcon.biz | 010-4667-1106 | 영업 대표 |
기술 문의 | tech@obcon.biz | 구축/컨설팅 담당 | |
고객 지원 | support@obcon.biz | 고객 지원 담당 |