// comentar
/**/ comentar varias lineas
|| OR
&& AND
Integer, Decimal, String, Boolean, Id TIPOS DE DATOS
System.debug(); Para monitorear el cambio de una variable o lista
Lists, Sets, Maps Colecciones de Salesforce
Las listas son ordenadas y los sets no
Maps son colecciones de records con keys value-key
String[] groceries = new String[4]; DECLARACION DE LISTA CON VALORES FIJOS O ARRAY
List<String> nombrelista = new List<String>(); DECLARACION DE LISTA DINAMICA
nombrelista.add(indexopcioonal,tipdedatoquequierameter); AÑADIR VALORES A LA LISTA
SINTAXIS IF
if(condition is true) {
//do this
}else if(condicion2){
//do this if condicion2
} else {
//do this si otra cosa que no sea c1 o c2
SINTAXIS DEL SWITCH
switch on expression {
when value1 { //single value
//code block 1
when value2, value3 { //multiple values
//code block 2
SINTAXIS DEL WHILE
While(mientrascondicion) {
//correr esto
SINTAXIS DO WHILE
Do {
//run this block of code
} while(seguirmientrascondicioncumpla)
DECLARAR CLASE:
public class nombredelaclase {
DECLARA METODO
public static void nombredelmetodo(parametros){
Un sObject es un datatype que corresponde a un objeto de Salesforce.
DECLARA sOBJECT
Account nombredelacuenta = new Account();
para usar el metodo de asignar el nombre a la cuenta:
nombredelacuenta.Name = 'The Tea Factory';
LENGUAJE DML(DATA MANIPULATION LENGAUGE)
insert
update
upsert
delete
undelete
merge
Cada metodo de estos acepta un solo record o una lista de ellos.
Por ejemplo:
insert nombredelobjeto; lo agrega a la base de datos
EJEMPLO DE CLASE QUE CREA ACCOUNT Y LA AGREGA
public class NewAccounts {
public static void sObjectsInsert(){
Account store = new Account();
store.Name = 'The Tea Factory';
store.AccountNumber = '356281';
store.Phone = '555-0158';
insert store;
}
}
Y en la Anonymous ósea en la consola:
NewAccounts.sObjectsInsert(); llamamos el metodo
EJEMPLO DE CLASE QUE CREA ACCOUNTS Y LAS AGREGA
public class NewAccounts {
public static void sObjectsInsert(Integer value){
Integer counter = 1;
//create a list to add our accounts
List<Account> teaFactoryAccounts = new List<Account>();
while(counter <= value){
//display the current counter value
System.debug('Counter Value before Incrementing ' +
counter);
//create a new account
Account store = new Account();
store.Name = 'The Tea Factory ' + counter;
store.AccountNumber = '35629' + counter;
teaFactoryAccounts.add(store);
System.debug(teaFactoryAccounts);
//increment the counter
counter = counter + 1;
System.debug('Counter Value after incrementing ' +
counter);
}
System.debug('Size of Account List: ' +
teaFactoryAccounts.size() );
System.debug('Elements in Account List: ' +
teaFactoryAccounts);
//insert all of the accounts in the list
insert teaFactoryAccounts;
}
}
Y en el Debug | Open Execute Anonymous Window:
NewAccounts.sObjectsInsert(3); 3 o el número que queramos
MAP Acepta valores duplicados igual que list, lo que no acepta son mismas
keys. Y puede tener diferentes datatypes.
Set no acepta valores duplicados y no tiene orden. Pero al igual que lista son del mismo
datatype.
Para agregar valores a un MAP:
Nombredelmapa.put(key,valor);
Para acceder a un valor de un MAP:
Nombredelmapa.get(key);
EJEMPLO DE MAP
public class Tea{
public static void orderTea(){
Map <String, String> teaTypes = new Map <String, String>();
teaTypes.put('Black', 'Earthy');
teaTypes.put('White', 'Sweet');
teaTypes.put('Herbal', 'Sweet');
system.debug(teaTypes);
}
}
FOR LOOP
1. for(Integer i = 0; i < 5; i++){
2. System.debug('The number is ' + i );
3. }
1) For loops are used when you know how many times the loop should run. If you want
the loop to stop based on a condition other than the number of times it runs you
should use the while loop.
2) For loops are more concise because they keep the three parts—the variable, the
condition, and the increment—together in one statement.
FOR LOOP IN COLLECTIONS
for ( tipodedato nombrevariablecontadora: nombredelistaoset){
}
EJEMPLO DE LOOP EN LIST
List <String> tea = new List<String>{'Black Tea', 'Green Tea', 'Chai
Tea'};
for(String t : tea){
System.debug('We have ' + t);
SOQL (Salesforce Object Query Language)
SELECT y FROM son las clauses requeridas
SIEMPRE SE USAN LAS API NAMES PARA REFERIR A LOS RECORDS(NO SE USAN LOS LABEL).
SELECT loscamposquesean FROM losobjetosquesean WHERE condición
Después del WHERE se puede usar los operadores AND, OR e IN si hay más de una
condición
IN
El IN se usa para simplificar lo que de otra manera serian muchos OR. Devuelve valores de
una lista o set.
SELECT Name, Email FROM Contact
WHERE LastName IN ('James', 'Barr', 'Nedaerk', 'Forbes')
LIMIT
The LIMIT keyword sets the maximum number of records to return.
FROM Contact LIMIT 5
ORDER BY
El ORDER BY también va después de del where y ordena los resultados de 3 formas.
ORDER BY campo ASC(ascendente) DESC(descendente) NULLS LAST/FIRST(valores nulos)
QUERIES EN APEX
Para asignarle los valores de una consulta a una lista encerramos la consulta en [ ] e igualamos
la lista = [la query] y punto y coma.
List<Contact> nombredelalista = [SELECT FirstName, LastName FROM Contact];
Para especificar un field de un objeto se usa
objeto.field
String targetDepartment = 'Wingo';
Contact[] techContacts = [SELECT FirstName,LastName
FROM Contact WHERE
Department=:targetDepartment];
QUERIE DE OBJETO PADRE E HIJO
DE HIJO A PADRE
SELECT Name, Account.Name FROM Contact
En el select ponemos el
Objetopadre.elcampoquequeremos FROM Objeto hijo
DE PADRE A HIJO
Para haces un padre hijo tenemos que hacer una subquery para el hijo dentro de la main
query.
SE USA EL NOMBRE DE HIJO DEL OBJETO HIJO EN ESTA CASO CONTACTS Y NO SU NOMBRE API
CONTACT
SELECT Name, (SELECT Name FROM Contacts) FROM Account WHERE Id IN
(SELECT AccountId FROM Contact WHERE LastName = 'Forbes')
Primero va a la subquery del where, después al where en si y después a
la subquery en los fields y después repite por cada uno.
The API name for a custom object is the custom object name with __c [underscore
underscore c] added at the end. Los standard no.
QUERY A UNA LOOKUP RELATIONSHIP CHILD TO PARENT
En este caso Property es hijo y Broker es padre.
1. SELECT Address__c, Picture__c, Broker__r.Name FROM Property__c
Se usa Brooker__r.Name
QUERY A UNA LOOKUP RELATIONSHIP PARENT TO CHILD
1. SELECT Name, (SELECT Address__c, Price__c FROM Properties__r) FROM
Broker__c
BIND VARIABLE usa :
Integer maxHomeValue = 200000;
List<Property__c> property = [SELECT Name, Price__c FROM Property__c
WHERE Price__c < :maxHomeValue];
COUNT() Returns the number of SELECT COUNT(Name) FROM Broker__c
rows that are associated
with the field
COUNT_DISTINCT( Returns the number of SELECT COUNT_DISTINCT(City__c) FROM Property__c
) unique rows that match
the query criteria
MIN() Returns the minimum SELECT MIN(Days_On_Market__c) FROM Property__c
value of a field
MAX() Returns the maximum SELECT MAX(Beds__c) FROM Property__c
value of a field
AVG() Returns the average SELECT City__c, AVG(Days_On_Market__c) FROM
value of a numeric field Property__c GROUP BY City__c
SUM() Returns the total value SELECT SUM(Price__c), Broker__r.Name FROM
of a numeric field Property__c GROUP BY Broker__r.Name
AGGREGATE FUNCTIONS
The HAVING clause filters the results returned by an aggregate function.
HAVING EN VEZ DE WHERE SI HAY AGGREGATE FUNCTION
The WHERE clause filters records in a SOQL query that has no aggregate
function.
The HAVING clause filters the results after data is aggregated by an
aggregate function.
What Is a SOSL Search?
SOSL (Salesforce Object Search Language) is a language that performs text
searches in records. Unlike SOQL, SOSL can query multiple types of objects at
the same time. SOSL can also use a word match to match fields, while SOQL
needs the exact phrase.
When you run a SOSL search for contact records using the word “Crisis,” your
search looks through all contact fields and returns any record containing that
word. But if you try the same in a SOQL query, you need to specify the fields to
search and a complete word or phrase to search for. You can also use LIKE or
wildcards to narrow down SOQL or SOSL searches
EJEMPLO SOQL QUERY
SELECT Name, Phone, Email, Title FROM Contact
WHERE (Department = 'Specialty Crisis
Management')
EJEMPLO MISA SOQL QUERY EN APEX
Contact[] theseContacts = [SELECT Name, Phone, Email, Description FROM
Contact
WHERE (Department='Specialty Crisis
Management')
ORDER BY Name];
// Log a count of how many contacts were found
System.debug(theseContacts.size() + ' contact(s) returned.');
// Log all values in the array of contacts
System.debug(theseContacts);
EJEMPLO SOSL QUERY
FIND {Crisis} IN ALL FIELDS RETURNING Contact(FirstName, LastName,
Phone, Email, Title)
EJEMPLO MISMA SOSL QUERY EN APEX
List<List<sObject>> searchList = [FIND 'Crisis' IN ALL FIELDS
RETURNING Contact(FirstName,
LastName,
Phone, Email, Description)];
Contact[] searchContacts = (Contact[])searchList[0];
System.debug('Found the following contacts:');
for (Contact c : searchContacts) {
System.debug(c.LastName + ', ' + c.FirstName);
}
Un record es un registro es decir como cuando entramos los datos para
crear un contacto, ese contacto que guardamos es un record, los fields
son los campos, ósea las columnas. Al crear un record basicamente
creamos un sObject.
Every record in Salesforce is natively represented as an sObject in Apex. For example,
the Acme account record corresponds to an Account sObject in Apex. The fields of the
Acme record that you can view and modify in the user interface can be read and
modified directly on the sObject as well.
Para declarar un sObject:
tipodesObject nombre = new tipodesObject();
The fields of a generic sObject can be accessed only through the put() and get() methods.
Se le puede asignar a un sObject genérico cualquier sObject especifico si se tiene un método
del cual no se sabe que sObject especifico pide:
sObject sobj1 = new Account(Name='Trailhead');
sObject sobj2 = new Book__c(Name='Workbook 1');
Para castear un sObject genérico a un sObject especifico:
En este caso acct es una variable sObject genérica y la casteamos a sObject Account asi puede
usar el llamado de métodos con punto lo cual no puede hacerse con sObject genérico.
Account acct = (Account)myGenericSObject;
// Now, you can use the dot notation to access fields on Account
String name = acct.Name;
String phone = acct.Phone;
The upsert DML operation creates new records and updates sObject records
within a single statement, using a specified field to determine the presence of
existing objects, or the ID field if no field is specified.
The merge statement merges up to three records of the same sObject type into
one of the records, deleting the others, and re-parenting any related records.
Al insertar un sObject el sistema le asigna una id a ese record insertado, y además se lo asigna
a la variable sObject auxiliar que se creo en el código esa misma id, por lo que para saber la id
del sObject que insertamos, podemos simplemente hacer un system.debug de esa variable
auxiliar después de que la insertamos para poder verla. También puedes seguir usándola en el
código para hacer otras operaciones DML.
// Create the account sObject
Account acct = new Account(Name='Acme', Phone='(415)555-1212',
NumberOfEmployees=100);
// Insert the account by using DML
insert acct;
// Get the new ID on the inserted sObject argument
ID acctID = acct.Id;
// Display this ID in the debug log
System.debug('ID = ' + acctID);
BULK DML
Básicamente se usan las operaciones DML sobre una lista de sObjects de forma que solo
cuente como una operación DML sola y no pasar el limite de 150 operaciones DML.
El upsert no solo actualiza e inserta los objetos sino que chequea la id del objeto por default o
el field que tu elijas para evitar duplicados. Para custom objects se especificia un custom field
marcado como external id. Para los standard cualquiera que tenga idLookup en true. Si el id es
matched se modifica, si no es matched se inserta y si se matchea muchas veces no hace nada
porque hay muchos duplicados existentes.
Upsert Syntax
upsert sObject | sObject[] field(el field es opcional)
Por ej:
upsert sObjectList Account.Fields.MyExternalId;
DELETE
El delete no borra sino que los deja en la papelera por 15 dias por si se quiere restaurar. A
continuacion un ejemplo de como borrar una cantidad de contactos filtrados por una consulta.
Contact[] contactsDel = [SELECT Id FROM Contact WHERE
LastName='Smith'];
delete contactsDel;
DATABASE METHODS
Database.insert()
Database.update()
Database.upsert()
Database.delete()
Database.undelete()
Database.merge()
Estos métodos tienen un parámetro para que sea todo o nada, si hay errores no hace nada, si
este se pone en false, si hay errores se pasan los que no tuvieron errores y los que tuvieron
errores se muestran.
Database.insert(recordList, false);
Estos errores son devueltos como objetos llamados SaveResult, y pueden ser guardados en un
array.
Database.SaveResult[] results = Database.insert(recordList, false);
Upser devuelve UpsertResults y delete DeleteResults.
¿Entonces debo usar DML O DATABASE?
Se usa dml si se quiere que el bloque se interrumpa usando herramientas de Flow control
como try y catch. Se usa Database si se quiere que se haya éxito parcial en los records que no
presenten errores y sean insertados igual o la operación que sea. Y con los que fallen después
se puede inspeccionar a ver por que fallaron para no tener errores de excepción de DML
nunca.
INSERTAR RECORDS RELACIONADOS
Se puede solo si la relación ya ha sido establecida sea lookup o masterdetail. Estos se asocian
mediante una foreign key.
Account acct = new Account(Name='SFDC Account');
insert acct;
// Once the account is inserted, the sObject will be
// populated with an ID.
// Get this ID.
ID acctID = acct.ID;
// Add a contact to this account.
Contact mario = new Contact(
FirstName='Mario',
LastName='Ruiz',
Phone='415.555.1212',
AccountId=acctID);
insert mario;
RELATED RECORD FIELD NO PUEDE SER MODIFICADOS EN LA MISMA OPERACIÓN DML SE
NECESITAN DOS
El delete sin embargo si el padre es borrado borra a los hijos también si estos pueden ser
eliminados.
QUERY RECORD IN BATCHES BY USING SOQL FOR LOOPS
for (variablelistosingle : [soql_query]) {
code_block
}
Use SOQL to retrieve records for a single object.
Use SOSL to search fields across multiple objects. SOSL queries can
search most text fields on an object.
En el query editor se busca FIND {talcosa} pero cuando lo pones en ápex va con FIND ‘talcosa’
SOSL allows you to specify the following search criteria:
Text expression (single word or a phrase) to search for
Scope of fields to search
List of objects and fields to retrieve
Conditions for selecting rows in the source objects
FIND 'SearchQuery' [IN SearchGroup] [RETURNING ObjectsAndFields]
SearchQuery is the text to search for (a single word or a phrase). Search terms can be
grouped with logical operators (AND, OR) and parentheses. Also, search terms can
include wildcard characters (*, ?). The * wildcard matches zero or more characters at the
middle or end of the search term. The ? wildcard matches only one character at the
middle or end of the search term.
SearchGroup is optional. It is the scope of the fields to search. If not specified, the
default search scope is all fields. SearchGroup can take one of the following values
ALL FIELDS
NAME FIELDS
EMAIL FIELDS
PHONE FIELDS
SIDEBAR FIELDS
ObjectsAndFields is optional. It is the information to return in the search result—a list of
one or more sObjects and, within each sObject, list of one or more fields, with optional
values to filter against. If not specified, the search results contain the IDs of all objects
found.
A SearchQuery contains two types of text:
Single Word— single word, such as test or hello . Words in
the SearchQuery are delimited by spaces, punctuation, and changes from
letters to digits (and vice-versa). Words are always case insensitive.
Phrase— collection of words and spaces surrounded by double quotes
such as "john smith" . Multiple words can be combined together with
logic and grouping operators to form a more complex query.
Trigger Syntax
La sintaxis del trigger es trigger elnombredeltrigger on objetoquequieras y entre
paréntesis las condiciones que lo triggerean.
trigger TriggerName on ObjectName (trigger_events) {
code_block
}
Posibles eventos para poner en trigger_events:
before insert
before update
before delete
after insert
after update
after delete
after undelete
Before triggers are used to update or validate record values before
they’re saved to the database.
After triggers are used to access field values that are set by the system
(such as a record's Id or LastModifiedDate field), and to affect changes in
other records. The records that fire the after trigger are read-only.
To access the records that caused the trigger to fire, use context
variables. For example, Trigger.New contains all the records that were
inserted in insert or update triggers. Trigger.Old provides the old version
of sObjects before they were updated in update triggers, or a list of
deleted sObjects in delete triggers. Triggers can fire when one record is
inserted, or when many records are inserted in bulk via the API or Apex.
Therefore, context variables, such as Trigger.New , can contain only one
record or multiple records. You can iterate over Trigger.New to get each
individual sObject.
Este trigger itera por cada cuenta en un for loop y modifica el description field
trigger HelloWorldTrigger on Account (before insert) {
for(Account a : Trigger.New) {
a.Description = 'New description';
}
}
Se usan variables de context que retornan booleanos por ejemplo para indicar si el trigger si
disparo por algún evento, estos son un buen uso cuando el trigger maneja muchos eventos.
LOS TRIGGERS PUEDEN LLAMAR METODOS PUBLIC
Calling addError() in a trigger causes the entire set of operations to roll back,
except when bulk DML is called with partial success.
If a DML statement in Apex spawned the trigger, any error rolls back the
entire operation. However, the runtime engine still processes every
record in the operation to compile a comprehensive list of errors.
If a bulk DML call in the Lightning Platform API spawned the trigger, the
runtime engine sets the bad records aside. The runtime engine then
attempts a partial save of the records that did not generate errors.
Las llamadas a web services externos se llaman CALLOUTS.
Cuando se hace un callout desde un trigger se tiene que hacer asincrónicamente para que el
proceso del trigger no te bloquee de trabajar mientras esperas la respuesta del servicio
externo. Por lo que se hace en un background process. Para hacer un callout desde un trigger
se llama a un método futuro que es un método que se ejecuta asincrónicamente y se anota
con @future(callout=true)
BULKIFY IMPORTANTE
1) Siempre se opera en record sets no en single record en los triggers.
2) Aprovechar al máximo las SOQL queries para no pasar el limite de 100 queries para el
ápex sincrónico y 200 para el asincrónico.
3) NO HACER QUERIES DENTRO DE UN FOR loop que vaya por cada sObject de un set que
haya activado el trigger, sino que hacer la query ANTES en el set para filtrar los valores
que se quieran y después ahí realizar el for. Así logrando realizar UNA query y no una
por cada record del set que hizo saltar el trigger.
4) Hacer las operaciones DML en listas fuera de los for, hacerlo al final. Usar un for para
iterar sobre el set que triggereo y usar una lista auxiliar para ir guardando los records y
después hacer la operación DML en la lista auxiliar.
Sintaxis TESTING
@isTest static void testName() {
// code_block
}
Los test no llevan public o private en su sintaxis ya que se puede acceder de todas formas a los
métodos de testing.
Los test deben estar definidos dentro de clases de test.
@isTest
private class MyTestClass {
@isTest static void myTest() {
// code_block
}
}
System.assertEquals() method, Se usa para el testing unitario. which takes two
parameters: the first is the expected value, and the second is the actual value. There is
another version of this method that takes a third parameter—a string that describes the
comparison being done, which is used in testBoilingPoint(). This optional string is
logged if the assertion fails.
Test Suite
A test suite is a collection of Apex test classes that you run together.
Even though it is not a best practice to do so, there are times when a test method
needs access to pre-existing data. To access org data, annotate the test method with
@isTest(SeeAllData=true). The test method examples in this unit don’t access org
data and therefore don’t use the SeeAllData parameter.
SOSL searches performed in a test return empty results. To ensure
predictable results, use Test.setFixedSearchResults() to define the records
to be returned by the search.
@testsetup
StartTest y StopTest
The test method contains the Test.startTest() and Test.stopTest() method pair, which
delimits a block of code that gets a fresh set of governor limits. In this test, test-data
setup uses two DML statements before the test is performed. To test that Apex code
runs within governor limits, isolate data setup’s limit usage from your test’s. To isolate
the data setup process’s limit usage, enclose the test call within the Test.startTest() and
Test.stopTest() block. Also use this test block when testing asynchronous Apex.