Introdução aos acionadores do Apex
Objetivos de aprendizagem
Após concluir esta unidade, você estará apto a:
- 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:
- Início rápido: Apex
- Noções básicas do Apex e banco de dados
- SOQL para administradores
- Noções básicas do Developer Console
Escrevendo acionadores do Apex
Os acionadores do Apex permitem que você execute ações personalizadas definidas antes ou depois de determinados eventos para registros no Salesforce, tais como inserções, atualizações ou exclusões. Assim como os sistemas de banco de dados suportam acionadores, o Apex fornece suporte a acionadores para o gerenciamento de registros.
Normalmente, os acionadores são usados para realizar operações baseadas em condições específicas, para modificar registros relacionados ou restringir determinadas operações. É possível usar acionadores para fazer tudo o que se pode fazer no Apex, inclusive executar SOQL e DML ou chamar métodos do Apex personalizados.
Use os acionadores para executar tarefas que não podem ser feitas usando as ferramentas de apontar e clicar na interface de usuário do Salesforce. Por exemplo, se estiver validando um valor de campo ou atualizando um campo em um registro, use as regras de validação e os fluxos. Use os acionadores do Apex se o desempenho e a escala forem importantes, se sua lógica for muito complexa para as ferramentas de apontar e clicar ou se estiver executando operações com uso intensivo de CPU.
Os acionadores podem ser definidos para objetos padrão de nível superior, como Conta ou Contato, objetos personalizados e alguns objetos filho padrão. Por padrão, os acionadores são ativados quando criados. O Salesforce dispara automaticamente os acionadores ativos quando os eventos de banco de dados especificados ocorrem.
Sintaxe do acionador
A sintaxe de uma definição de acionador é diferente da sintaxe de uma definição de classe. A definição de um acionador começa com a palavra-chave 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 }
Para executar um acionador antes ou depois de inserir, atualizar, excluir e cancelar exclusão de operações, especifique vários eventos de acionador em uma lista separada por vírgulas. Estes são os eventos que você pode especificar:
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
Este simples acionador dispara antes que uma conta seja inserida e escreve uma mensagem no registro de depuração.
- 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 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!
(Olá, mundo!). 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 |
isInsert | Retorna |
isUpdate | Retorna |
isDelete | Retorna |
isBefore | Retorna |
isAfter | Retorna |
isUndelete | Retorna |
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 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
(Não é possível excluir uma conta com oportunidades relacionadas).
- 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).
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.
Recursos
- Guia do desenvolvedor do Apex: Acionadores
- Guia do desenvolvedor do Apex: Como invocar callouts usando o Apex
- Trailhead: Serviços de integração com o Apex