Introdução aos acionadores do Apex
Objetivos de aprendizagem
- Escrever um acionador para um objeto do Salesforce.
- Usar variáveis de contexto de acionadores.
- Chamar um método de classe a partir de um acionador.
- Usar o método
addError()
de sObject em um acionador para restringir operações de salvamento.
Antes de começar
Os acionadores do Apex são úteis, divertidos e empolgantes. Embora este módulo ajude você a começar a usá-los, também faz referência a outros recursos do Salesforce para mostrar o poder dos acionadores do Apex. Para aproveitar ao máximo este módulo, recomendamos que você confira esses módulos primeiro:
Escrevendo acionadores do Apex
Sintaxe do acionador
trigger
. Depois, é seguida pelo nome do acionador, pelo objeto do Salesforce ao qual o acionador está associado e pelas condições sob as quais ele dispara. Um acionador tem a seguinte sintaxe:trigger TriggerName on ObjectName (trigger_events) { code_block }
-
before insert
(antes de inserir) -
before update
(antes de atualizar) -
before delete
(antes de excluir) -
after insert
(após inserir) -
after update
(após atualizar) -
after delete
(após excluir) -
after undelete
(após desfazer exclusão)
Exemplo de acionador
- No Developer Console, clique em File (Arquivo) | New (Novo) | Apex Trigger (Acionador do Apex).
- Digite
HelloWorldTrigger
para o nome do acionador e, em seguida, selecione Conta para o sObject. Clique em Submit (Enviar). - Substitua o código padrão pelo seguinte.
trigger HelloWorldTrigger on Account (before insert) { System.debug('Hello World!'); }
- Para salvar, pressione Ctrl+S.
- Para testar o acionador, crie uma conta.
- Clique em Debug (Depurar) | Open Execute Anonymous Window (Abrir janela Executar anônimo).
- Na nova janela, adicione as informações a seguir e clique em Execute (Executar).
Account a = new Account(Name='Test Trigger'); insert a;
- No registro de depuração, localize a instrução
Hello World!
. O registro também mostra que o acionador foi executado.
Tipos de acionadores
Existem dois tipos de acionadores.
- Pré-acionadores são usados para atualizar ou validar valores de registro antes que eles sejam salvos no banco de dados.
-
Pós-acionadores são usados para acessar os valores de campo que são definidos pelo sistema (tal como a
Id
de um registro ou o campoLastModifiedDate
) e para realizar alterações em outros registros. Os registros que disparam o pós-acionador são de somente leitura.
Usando variáveis de contexto
Para acessar os registros que dispararam o acionador, use variáveis de contexto. Por exemplo, Trigger.new
contém todos os registros que foram inseridos nos acionadores de inserção ou atualização. Trigger.old
fornece a versão antiga de sObjects antes de eles serem atualizados nos acionadores de atualização, ou uma lista de sObjects excluídos nos acionadores de exclusão. Os acionadores podem disparar quando um registro é inserido ou quando muitos registros são inseridos em grandes quantidades via API ou Apex. Portanto, variáveis de contexto, como Trigger.new
, podem conter apenas um registro ou vários registros. Você pode iterar sobre Trigger.new
para obter cada sObject individual.
Este exemplo é uma versão modificada do acionador de exemplo HelloWorldTrigger
. Ele itera sobre cada conta em um loop for e atualiza o campo Description
(Descrição) de cada uma.
trigger HelloWorldTrigger on Account (before insert) { for(Account a : Trigger.new) { a.Description = 'New description'; } }
Algumas outras variáveis de contexto exibem um valor booleano para indicar se o acionador foi disparado devido a uma atualização ou a algum outro evento. Essas variáveis são úteis quando um acionador combina diversos eventos. Por exemplo:
trigger ContextExampleTrigger on Account (before insert, after insert, after delete) { if (Trigger.isInsert) { if (Trigger.isBefore) { // Process before insert } else if (Trigger.isAfter) { // Process after insert } } else if (Trigger.isDelete) { // Process after delete } }
A tabela a seguir apresenta uma lista exaustiva de todas as variáveis de contexto disponíveis para acionadores.
Variável | Uso |
---|---|
isExecuting |
Retorna true se o contexto atual para o código do Apex for um acionador, não uma página do Visualforce, um serviço Web ou uma chamada de API executeanonymous() . |
isInsert |
Retorna true se este acionador tiver sido disparado devido a uma operação de inserção, a partir da interface de usuário do Salesforce, Apex ou API. |
isUpdate |
Retorna true se este acionador tiver sido disparado devido a uma operação de atualização, a partir da interface de usuário do Salesforce, Apex ou API. |
isDelete |
Retorna true se este acionador tiver sido disparado devido a uma operação de exclusão, a partir da interface de usuário do Salesforce, Apex ou API. |
isBefore |
Retorna true se este acionador tiver sido disparado antes de qualquer registro ter sido salvo. |
isAfter |
Retorna true se este acionador tiver sido disparado após todos os registros terem sido salvos. |
isUndelete |
Retorna true se este acionador tiver sido disparado após um registro ter sido recuperado da Lixeira. Essa recuperação pode ocorrer depois de uma operação de cancelamento de exclusão a partir da interface de usuário do Salesforce, Apex ou API. |
new |
Retorna uma lista das novas versões dos registros sObject. Essa lista de sObjects está disponível apenas nos acionadores |
newMap |
Um mapa de IDs para as novas versões dos registros sObject. Esse mapa está disponível apenas nos acionadores |
old |
Retorna uma lista das versões antigas dos registros sObject. Essa lista de sObjects está disponível apenas nos acionadores |
oldMap |
Um mapa de IDs para as versões antigas dos registros sObject. Esse mapa está disponível apenas nos acionadores |
operationType |
Retorna um enum do tipo System.TriggerOperation correspondente à operação atual. Alguns valores possíveis do enum |
size |
O número total de registros em uma invocação do acionador – novos e antigos. |
Chamando um Método de classe a partir de um Acionador
Você pode chamar métodos utilitários públicos a partir de um acionador. Chamar métodos de outras classes permite a reutilização de código, reduz o tamanho dos seus acionadores e melhora a manutenção do seu código do Apex. Também permite usar a programação orientada a objeto.
O exemplo de acionador a seguir mostra como chamar um método estático a partir de um acionador. Se o acionador tiver sido disparado devido a um evento de inserção, o exemplo chama o método estático sendMail()
na classe EmailManager
. Esse método utilitário envia um email ao destinatário especificado e contém o número de registros de contato inseridos.
- No Developer Console, clique em File (Arquivo) | New (Novo) | Apex Class (Classe do Apex).
- Insira
EmailManager
e clique em OK. - Substitua o corpo da classe padrão pelo exemplo da classe
EmailManager
abaixo.public class EmailManager { // Public method public static void sendMail(String address, String subject, String body) { // Create an email message object Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {address}; mail.setToAddresses(toAddresses); mail.setSubject(subject); mail.setPlainTextBody(body); // Pass this email message to the built-in sendEmail method // of the Messaging class Messaging.SendEmailResult[] results = Messaging.sendEmail( new Messaging.SingleEmailMessage[] { mail }); // Call a helper method to inspect the returned results inspectResults(results); } // Helper method private static Boolean inspectResults(Messaging.SendEmailResult[] results) { Boolean sendResult = true; // sendEmail returns an array of result objects. // Iterate through the list to inspect results. // In this class, the methods send only one email, // so we should have only one result. for (Messaging.SendEmailResult res : results) { if (res.isSuccess()) { System.debug('Email sent successfully'); } else { sendResult = false; System.debug('The following errors occurred: ' + res.getErrors()); } } return sendResult; } }
- No Developer Console, clique em File (Arquivo) | New (Novo) | Apex Trigger (Acionador do Apex).
- Digite
ExampleTrigger
para o nome do acionador e, em seguida, selecione Contact (Contato) para o sObject. Clique em Submit (Enviar). - Substitua o código padrão pelo apresentado abaixo e, em seguida, modifique o texto do espaço reservado para o endereço de email em
sendMail()
referente ao seu endereço de email.trigger ExampleTrigger on Contact (after insert, after delete) { if (Trigger.isInsert) { Integer recordCount = Trigger.new.size(); // Call a utility method from another class EmailManager.sendMail('Your email address', 'Trailhead Trigger Tutorial', recordCount + ' contact(s) were inserted.'); } else if (Trigger.isDelete) { // Process after delete } }
- Para salvar, pressione Ctrl+S.
- Para testar o acionador, crie um contato.
- Clique em Debug (Depurar) | Open Execute Anonymous Window (Abrir janela Executar anônimo).
- Na nova janela, adicione as informações a seguir e clique em Execute (Executar).
Contact c = new Contact(LastName='Test Contact'); insert c;
- No registro de depuração, verifique se o acionador foi disparado. Perto do final do registro, localize a mensagem de depuração que foi escrita pelo método utilitário:
DEBUG|Email sent successfully
(DEPURAR|Email enviado com sucesso) - Agora, verifique se você recebeu um email com o corpo de texto
1 contact(s) were inserted
(1 contato foi inserido).
Com seu novo acionador em vigor, você receberá um email sempre que adicionar um ou mais contatos!
Adicionando registros relacionados
Muitas vezes, os acionadores são usados para acessar e gerenciar registros relacionados aos registros no contexto do acionador – registros que dispararam o acionador.
Esse acionador adiciona uma oportunidade relacionada para cada conta nova ou atualizada caso nenhuma oportunidade já esteja associada à conta. O acionador primeiramente realiza uma consulta SOQL para obter todas as oportunidades filho das contas que o acionador disparou. Em seguida, o acionador itera sobre a lista de sObjects em Trigger.new
para obter o sObject de cada conta. Se a conta não tem nenhum sObject de oportunidade relacionado, o loop for cria um. Se o acionador criou alguma nova oportunidade, a instrução final faz a inserção.
- Adicione o seguinte acionador usando o Developer Console (siga os passos do exemplo
HelloWorldTrigger
, mas useAddRelatedRecord
para o nome do acionador).trigger AddRelatedRecord on Account(after insert, after update) { List<Opportunity> oppList = new List<Opportunity>(); // Get the related opportunities for the accounts in this trigger Map<Id,Account> acctsWithOpps = new Map<Id,Account>( [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.new]); // Add an opportunity for each account if it doesn't already have one. // Iterate through each account. for(Account a : Trigger.new) { System.debug('acctsWithOpps.get(a.Id).Opportunities.size()=' + acctsWithOpps.get(a.Id).Opportunities.size()); // Check if the account already has a related opportunity. if (acctsWithOpps.get(a.Id).Opportunities.size() == 0) { // If it doesn't, add a default opportunity oppList.add(new Opportunity(Name=a.Name + ' Opportunity', StageName='Prospecting', CloseDate=System.today().addMonths(1), AccountId=a.Id)); } } if (oppList.size() > 0) { insert oppList; } }
- Para testar o acionador, crie uma conta na interface de usuário do Salesforce e dê a ela o nome
Apples & Oranges
(Maçãs e Laranjas). - Na lista relacionada de Oportunidades, na página da conta, localize a nova oportunidade. O acionador adicionou esta oportunidade automaticamente!
Usando exceções do acionador
Às vezes, você precisa adicionar restrições a determinadas operações de banco de dados, como impedir que registros sejam salvos quando determinadas condições forem atendidas. Para evitar que registros sejam salvos em um acionador, chame o método addError()
sobre o sObject em questão. O método addError()
lança um erro fatal dentro de um acionador. A mensagem de erro é exibida na interface de usuário e é registrada.
O acionador seguinte impede a exclusão de uma conta caso esta tenha oportunidades relacionadas. Por padrão, a exclusão de uma conta provoca uma exclusão em cascata de todos os seus registros relacionados. Este acionador impede a exclusão de oportunidades em cascata. Experimente usar este acionador! Se você já executou o exemplo anterior, sua organização tem uma conta chamada Apples & Oranges
(Maçãs e laranjas) com uma oportunidade relacionada. Este exemplo usa essa conta de exemplo.
- Usando o Developer Console, adicione o acionador a seguir.
trigger AccountDeletion on Account (before delete) { // Prevent the deletion of accounts if they have related opportunities. for (Account a : [SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM Opportunity) AND Id IN :Trigger.old]) { Trigger.oldMap.get(a.Id).addError( 'Cannot delete account with related opportunities.'); } }
- Na interface de usuário do Salesforce, acesse a página da conta
Apples & Oranges
(Maçãs e Laranjas) e clique em Delete (Excluir). - No pop-up de confirmação, clique em OK.
Localize o erro de validação com a mensagem de erro personalizadaCannot delete account with related opportunities
.
- Desabilite o acionador
AccountDeletion
. Se deixar esse acionador ativo, você não poderá verificar seus desafios.- Em Setup (Configuração), pesquise
Apex Triggers (Acionadores do Apex)
. - Na página Apex Triggers (Acionadores do Apex), clique em Edit (Editar), próximo ao acionador
AccountDeletion
. - Desmarque a opção Is Active (Está ativo).
- Clique em Save (Salvar).
- Em Setup (Configuração), pesquise
Acionadores e callouts
O Apex permite fazer chamadas e integrar seu código do Apex aos serviços web externos. As chamadas Apex aos serviços web externos são referidas como callouts. Por exemplo, você pode fazer um callout para um serviço de cotação de ações para receber as cotações mais recentes. Ao fazer um callout a partir de um acionador, o callout deve ser feito de forma assíncrona para que o processo do acionador não o impeça de trabalhar enquanto aguarda pela resposta do serviço externo. O callout assíncrono é feito em um processo de fundo, e a resposta é recebida quando o serviço externo retorna-o.
Para fazer um callout a partir de um acionador, chame um método de classe que execute de forma assíncrona. Esse método recebe o nome de método futuro e é anotado com @future(callout=true)
. Esta classe de exemplo contém o método futuro que faz o callout.
public class CalloutClass { @future(callout=true) public static void makeCallout() { HttpRequest request = new HttpRequest(); // Set the endpoint URL. String endpoint = 'http://yourHost/yourService'; request.setEndPoint(endpoint); // Set the HTTP verb to GET. request.setMethod('GET'); // Send the HTTP request and get the response. HttpResponse response = new HTTP().send(request); } }
Este exemplo mostra o acionador que chama o método na classe para fazer um callout de forma assíncrona.
trigger CalloutTrigger on Account (before insert, before update) { CalloutClass.makeCallout(); }
Esta seção apresenta apenas uma visão geral dos callouts e não tem como objetivo abordá-los detalhadamente. Para obter mais informações, consulte Como invocar callouts usando o Apex no Guia do desenvolvedor do Apex.