Contexto: Trigger.new, Trigger.old, Trigger.newMap
Lo que Salesforce le entrega a tu trigger en cada ejecución: los registros nuevos, los viejos, sus mapas por Id y unas preguntas de sí o no para saber qué está pasando.
✓ En esta sub-lección
- 01Saber qué contiene Trigger.new, Trigger.old, Trigger.newMap y Trigger.oldMap, y en qué eventos existe cada uno.
- 02Detectar si un campo cambió comparando el valor nuevo con el viejo.
- 03Usar Trigger.isInsert, isUpdate, isBefore e isAfter para separar la lógica.
La nutria: ¿Te acuerdas? · Repaso de la lección 1
Un trigger after insert recibe 150 leads. ¿Qué tiene Trigger.new?
Cuando Salesforce ejecuta tu trigger, no lo hace a ciegas: le deja preparadas unas con todo lo que necesita saber. Qué registros llegan, cómo eran antes del cambio y qué operación se está haciendo. Toda la lógica de un trigger se escribe a partir de ellas.
La nutria: El paralelo de Admin
Las cuatro colecciones
- Trigger.new: List con los registros tal como van a quedar. Es tu $Record en plural.
- Trigger.old: List con los registros tal como estaban antes del cambio. Tu $Record__Prior en plural.
- Trigger.newMap: los mismos registros nuevos, en un Map<Id, sObject>. Para buscar por Id sin recorrer, como el VLOOKUP del Módulo 2.
- Trigger.oldMap: los registros viejos en un Map<Id, sObject>. Es la que más usarás: con ella encuentras el «antes» de cada registro de Trigger.new.
La oportunidad de Acme pasa de 'Prospecting' a 'Negotiation'. Elige el evento que está corriendo y mira qué variables existen.
[ Acme · StageName 'Negotiation' ]
{ 006…Acme → StageName 'Negotiation' }
[ Acme · StageName 'Prospecting' ]
{ 006…Acme → StageName 'Prospecting' }
PRUÉBALO DENTRO DE for (Opportunity o : Trigger.new)
No todas existen en todos los eventos
Tiene lógica si piensas en el registro: al crearlo no hay un «antes», así que no hay Trigger.old. Al borrarlo no hay un «después», así que no hay Trigger.new. Y en before insert el registro todavía no tiene Id, así que no puede haber un mapa por Id.
| Evento | new | newMap | old | oldMap |
|---|---|---|---|---|
| before insert | ✓ (editable) | ✗ sin Id | ✗ | ✗ |
| after insert | ✓ lectura | ✓ | ✗ | ✗ |
| before update | ✓ (editable) | ✓ | ✓ | ✓ |
| after update | ✓ lectura | ✓ | ✓ | ✓ |
| before delete | ✗ | ✗ | ✓ | ✓ |
| after delete | ✗ | ✗ | ✓ | ✓ |
| after undelete | ✓ lectura | ✓ | ✗ | ✗ |
¿Cambió el campo? El patrón old frente a new
La pregunta más frecuente en un trigger de update no es «¿cómo está el registro?» sino «¿qué cambió?». Si no la haces, tu lógica se ejecuta cada vez que alguien edita cualquier campo, aunque sea la descripción. Se responde recorriendo Trigger.new y buscando cada registro en Trigger.oldMap por su Id.
trigger OpportunityStage on Opportunity (before update) {
for (Opportunity opp : Trigger.new) {
Opportunity before = Trigger.oldMap.get(opp.Id);
if (opp.StageName != before.StageName) {
opp.NextStep = 'Etapa: ' + before.StageName + ' → ' + opp.StageName;
}
}
}Las preguntas de sí o no
Un mismo trigger puede escuchar varios eventos. Para saber cuál es el de esta ejecución, Salesforce te da unos Boolean: Trigger.isInsert, isUpdate, isDelete, isUndelete, y para el momento, isBefore e isAfter. Con los if del Módulo 2 separas cada rama. También tienes Trigger.size (cuántos registros llegan) y Trigger.operationType, que lo resume todo en un valor como BEFORE_UPDATE.
trigger AccountTrigger on Account (before insert, before update, after insert) {
if (Trigger.isBefore && Trigger.isInsert) {
// solo al crear, antes de guardar
}
if (Trigger.isBefore && Trigger.isUpdate) {
// aquí sí existe Trigger.oldMap
}
if (Trigger.isAfter && Trigger.isInsert) {
// ya hay Ids: Trigger.newMap disponible
}
}Trigger.new solo se puede tocar en before
En before insert y before update, cambiar un campo de un registro de Trigger.new es la forma de modificarlo: el cambio se guarda solo, porque el registro todavía no se ha escrito. En los eventos after, en cambio, Trigger.new es de solo lectura: si intentas asignar un campo, Salesforce lanza System.FinalException: Record is read-only. Y Trigger.old no se puede cambiar nunca. La lección 3 explica por qué y qué se hace entonces.
La nutria: ISCHANGED() y PRIORVALUE(): old y new en tus fórmulas
La nutria: ¿Y por qué no un Flow? Aquí el flow era la herramienta correcta