via pixabay.com
はじめに
この記事では、テキスト項目に入力されている値を「ひらがな ⇋ カタカナ」に変換した場合に、その変更をApex Trigger内の処理で検知する方法について紹介します。
本来やりたかったこと
実装したかった機能の概要
実装したかった機能の概要としては、「取引先オブジェクトの項目の値に変更が加えられたら、その変更をトリガーにApex Triggerを起動し、後続の処理を行う」というものでした。
この機能を実現するためには、Apex Triggerを作成し、afterUpdate()メソッドの中で"oldRecordの項目の値" と "newRecordの項目の値" をそれぞれ取得し、値に差異がある(=変更されている)かを比較する必要がありました。
この機能を実現するためには、Apex Triggerを作成し、afterUpdate()メソッドの中で"oldRecordの項目の値" と "newRecordの項目の値" をそれぞれ取得し、値に差異がある(=変更されている)かを比較する必要がありました。
実際に実装してみると
上記機能を実装するために、以下のようなTriggerクラスを作成しました。
これで、取引先オブジェクトに設定されている項目を更新したらif (oldValue != newValue) の部分で "oldRecordの項目の値" と "newRecordの項目の値" が比較され、変更があればデバッグ上に変更された項目の項目名が表示されます。
これで、取引先オブジェクトに設定されている項目を更新したらif (oldValue != newValue) の部分で "oldRecordの項目の値" と "newRecordの項目の値" が比較され、変更があればデバッグ上に変更された項目の項目名が表示されます。
public class AccountTriggerHandler extends TriggerHandler { private List<Account> triggerOld; private List<Account> triggerNew; private Map<Id, Account> triggerOldMap; private Map<Id, Account> triggerNewMap; /** * コンストラクタ */ public AccountTriggerHandler() { if (Trigger.old != null) this.triggerOld = (List<Account>) Trigger.old; if (Trigger.new != null) this.triggerNew = (List<Account>) Trigger.new; if (Trigger.oldMap != null) this.triggerOldMap = (Map<Id, Account>) Trigger.oldMap; if (Trigger.newMap != null) this.triggerNewMap = (Map<Id, Account>) Trigger.newMap; } /** * After Update の処理タイミングで呼び出されるメソッド */ public override void afterUpdate() { List<AccountColumns__mdt> columns = null; for (Account newRec : this.triggerNew) { Account oldRec = triggerOldMap.get(newRec.Id); // カスタムメタデータ型に設定したカスタム項目を取得 if (columns == null) { columns = [SELECT Column__c FROM AccountColumns__mdt]; } // カスタム項目に設定した「取引先オブジェクトに設定されている項目のAPI参照名」でループ for (AccountColumns__mdt rec : columns) { Object oldValue = oldRec.get(rec.Column__c); Object newValue = newRec.get(rec.Column__c); // 値の変更有無チェック if (oldValue != newValue) { System.debug('★変更を検知しました。項目名:' + rec.Column__c); System.debug('★old:' + oldValue); System.debug('★new:' + newValue); } } } } }
AccountTriggerHandler.clsのサンプルコード(回避策適用前)
<サンプルコードの注意点>
上記サンプルコードは、以下の前提条件を含んでいます。
・AccountTriggerHandler.cls は TriggerHandler.cls を拡張しています。
TriggerHandler.cls にはTriggerによって呼び出されるメインのメソッド(run()メソッド)が記載されています。
・AccountTrigger.triggerを別途作成しています。
AccountTrigger.trigger では TriggerHandler.cls で定義されたrun()メソッドを呼び出しています。
・組織にカスタムメタデータ型(AccountColumns__mdt)を設定しています。
カスタムメタデータ型では、カスタム項目として「API参照名(Column__c)」を設定しています。
「API参照名(Column__c)」では、取引先オブジェクトの項目のAPI参照名を外出しして保持しています。今回は「取引先名(Name)」と「電話(Phone)」のみ設定しています。
上記サンプルコードは、以下の前提条件を含んでいます。
・AccountTriggerHandler.cls は TriggerHandler.cls を拡張しています。
TriggerHandler.cls にはTriggerによって呼び出されるメインのメソッド(run()メソッド)が記載されています。
・AccountTrigger.triggerを別途作成しています。
AccountTrigger.trigger では TriggerHandler.cls で定義されたrun()メソッドを呼び出しています。
・組織にカスタムメタデータ型(AccountColumns__mdt)を設定しています。
カスタムメタデータ型では、カスタム項目として「API参照名(Column__c)」を設定しています。
「API参照名(Column__c)」では、取引先オブジェクトの項目のAPI参照名を外出しして保持しています。今回は「取引先名(Name)」と「電話(Phone)」のみ設定しています。
思わぬ落とし穴について
この AccountTriggerHandler.cls を組織にデプロイして、取引先オブジェクトの項目を変更してみます。
「取引先(Name)」を "株式会社テラスカイ" → "株式会社テラスカイ 更新後" に更新しました。
「電話(Phone)」も "03-5255-3410" → "00-0000-0000" に更新しました。
「電話(Phone)」も "03-5255-3410" → "00-0000-0000" に更新しました。
問題なく変更を検知できていそうですね!( ◜ᴗ◝)ワーイ
と思ったら、思わぬ落とし穴がありました...
と思ったら、思わぬ落とし穴がありました...
単に if (oldValue != newValue) で比較するだけでは「ひらがな ⇋ カタカナ」の変更が検知できていないのです!!
「取引先(Name)」を "株式会社テラスカイ" → "株式会社てらすかい" に更新しました。
トリガーが動いていることを確認するために、「電話(Phone)」も "03-5255-3410" → "00-0000-0000" に更新しました。
トリガーが動いていることを確認するために、「電話(Phone)」も "03-5255-3410" → "00-0000-0000" に更新しました。
このように、デバッグには「電話(Phone)」の変更のみが出力され、
「取引先(Name)」の変更は検知できていないことが分かります。
カタカナ → ひらがな だけでなく、ひらがな → カタカナ も同様の結果でした...
「取引先(Name)」の変更は検知できていないことが分かります。
カタカナ → ひらがな だけでなく、ひらがな → カタカナ も同様の結果でした...
回避策の紹介
public class AccountTriggerHandler extends TriggerHandler { private List<Account> triggerOld; private List<Account> triggerNew; private Map<Id, Account> triggerOldMap; private Map<Id, Account> triggerNewMap; /** * コンストラクタ */ public AccountTriggerHandler() { if (Trigger.old != null) this.triggerOld = (List<Account>) Trigger.old; if (Trigger.new != null) this.triggerNew = (List<Account>) Trigger.new; if (Trigger.oldMap != null) this.triggerOldMap = (Map<Id, Account>) Trigger.oldMap; if (Trigger.newMap != null) this.triggerNewMap = (Map<Id, Account>) Trigger.newMap; } /** * After Update の処理タイミングで呼び出されるメソッド */ public override void afterUpdate() { List<AccountColumns__mdt> columns = null; Map<String, sObjectField> fieldMap = Schema.SObjectType.Account.fields.getMap(); for (Account newRec : this.triggerNew) { Account oldRec = triggerOldMap.get(newRec.Id); // カスタムメタデータ型に設定したカスタム項目を取得 if (columns == null) { columns = [SELECT Column__c FROM AccountColumns__mdt]; } // カスタム項目に設定した「取引先オブジェクトに設定されている項目のAPI参照名」でループ for (AccountColumns__mdt rec : columns) { Object oldValue = oldRec.get(rec.Column__c); Object newValue = newRec.get(rec.Column__c); // カラムの型を取得 Schema.DisplayType fieldType = fieldMap.get(rec.Column__c).getDescribe().getType(); // 値の変更有無チェック Boolean isChanged = false; if (Schema.DisplayType.String == fieldType || Schema.DisplayType.TextArea == fieldType) { String oldStr = oldValue != null ? (String)oldValue : ''; String newStr = newValue != null ? (String)newValue : ''; isChanged = (oldStr.compareTo(newStr) != 0); } else { isChanged = (oldValue != newValue); } if (isChanged) { System.debug('★変更を検知しました。項目名:' + rec.Column__c); System.debug('★old:' + oldValue); System.debug('★new:' + newValue); } } } } }
AccountTriggerHandler.clsのサンプルコード(回避策適用後)
今回採用した回避策は
「Schema.SObjectType.Account.fields.getMap() を用いて、項目の型を取得しString型とTextArea型の項目の場合は、compareTo()メソッドを使用して比較する」
という方法です。(それ以外の型の場合は以前と同様の比較方法です。)
「Schema.SObjectType.Account.fields.getMap() を用いて、項目の型を取得しString型とTextArea型の項目の場合は、compareTo()メソッドを使用して比較する」
という方法です。(それ以外の型の場合は以前と同様の比較方法です。)
compareTo()メソッドのApex 開発者ガイドには、
string の各文字の unicode 値に基づいて、2 つの文字列を辞書編集的に比較します。
と記載されています。
このメソッドを使用することで、「ひらがな ⇋ カタカナ」の変更でも値に差異がある(=変更されている)ことを検知することができました。
この方法以外にも、compareTo()メソッドのみでの比較や、equals()メソッドで比較する方法もありましたが
・項目の値がNullの場合にエラーになる
・Integer型などは比較できない
などの制約があったため、上記の方法を採用しました。
このメソッドを使用することで、「ひらがな ⇋ カタカナ」の変更でも値に差異がある(=変更されている)ことを検知することができました。
この方法以外にも、compareTo()メソッドのみでの比較や、equals()メソッドで比較する方法もありましたが
・項目の値がNullの場合にエラーになる
・Integer型などは比較できない
などの制約があったため、上記の方法を採用しました。
先ほどと同様、「取引先(Name)」を "株式会社テラスカイ" → "株式会社てらすかい" に更新しました。
「電話(Phone)」も "03-5255-3410" → "00-0000-0000" に更新しました。
「電話(Phone)」も "03-5255-3410" → "00-0000-0000" に更新しました。
今度こそ、抜け漏れ無く変更を検知できていそうですね!( ◜ᴗ◝)ワーイワーイ
おわりに
以上がApexで「ひらがな ⇋ カタカナ」の変更を検知する方法です。
こんな落とし穴があるのも、漢字・ひらがな・カタカナの3つの文字種を混用する日本ならではかもしれませんね。
私自身もとてもいい勉強になりました!
こんな落とし穴があるのも、漢字・ひらがな・カタカナの3つの文字種を混用する日本ならではかもしれませんね。
私自身もとてもいい勉強になりました!
関連記事
String クラス compareTo(secondString)
DisplayType 列挙
[Apex] 文字列の比較における「==」と「equals」の違い #Salesforce - Qiita
新検索基盤導入に伴うひらがな、カタカナ検索の動作変更について
サイトが表示できない場合は言語選択にて "日本語" を選択してください。
41 件