7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SalesforceAdvent Calendar 2022

Day 7

ChatGPTにApexトリガーを書かせてみたらすごかった

Last updated at Posted at 2022-12-07

今話題の対話型AIチャットのChatGPTにApexトリガーを書かせてみました。

ChatGPTとは何ですか?

ChatGPTとは何かChatGPTに聞いてみましょう。

image.png

このように会話のコンテキストを理解して返事をしてくれます。本当に人間と会話しているレベルの返答を返してくれて衝撃です。

ネットでもかなり話題になっています。

詳しくはたくさん記事があるので他の記事を読んでください。

Apexを書かせてみよう

これはSalesforceの開発もできるのではないかと、とりあえずApexトリガーを書かせてみました。

すっごい。

再度、緯度経度取得Apexトリガーを書いてもらう

質問する度に回答は異なるので、記事を書くために再度同じ質問をしてみました。今回はさらに対話してコードをよりよくしてもらってみます。

Q1. 取引先の請求先住所が変更されたときにGoogle Geocoding APIを実行し緯度経度を取得し保存するApexトリガーのコードを教えてください。

A1. ChatGPTの回答

image.png

Google Geocoding APIを使用するためには、Google Maps APIキーが必要です。まずは、Google Cloud Platformでプロジェクトを作成し、Google Maps Geocoding APIを有効化してAPIキーを取得する必要があります。

次に、Apexトリガーのコードを書く前に、Google Geocoding APIを呼び出すためのApexクラスを作成します。下記のようなコードで実装できます。

// GoogleGeocodingAPI.cls

public with sharing class GoogleGeocodingAPI {
  public static String GEOCODING_API_KEY = 'YOUR_API_KEY';
  public static String GEOCODING_API_ENDPOINT = 'https://maps.googleapis.com/maps/api/geocode/json';

  public static GeocodingResponse getGeocodingResponse(String address) {
    Http h = new Http();
    HttpRequest req = new HttpRequest();
    req.setEndpoint(GEOCODING_API_ENDPOINT + '?address=' + EncodingUtil.urlEncode(address, 'UTF-8') + '&key=' + GEOCODING_API_KEY);
    req.setMethod('GET');
    HttpResponse res = h.send(req);
    return (GeocodingResponse) JSON.deserialize(res.getBody(), GeocodingResponse.class);
  }
}

次に、GeocodingResponseクラスを定義します。このクラスは、Google Geocoding APIから取得したレスポンスを表すためのクラスです。

// GeocodingResponse.cls

public class GeocodingResponse {
  public String status;
  public List<Result> results;
  public String error_message;

  public class Result {
    public List<AddressComponent> address_components;
    public String formatted_address;
    public Geometry geometry;
  }

  public class AddressComponent {
    public String long_name;
    public String short_name;
    public List<String> types;
  }

  public class Geometry {
    public Location location;
  }

  public class Location {
    public Double lat;
    public Double lng;
  }
}

最後に、Apexトリガーを定義します。このトリガーは、取引先の請求先住所が変更されたときに実行され、Google Geocoding APIを呼び出して、請求先住所の緯度経度を取得し保存します。

// GeocodingTrigger.apxt

trigger GeocodingTrigger on Account (after update) {
  Set<Id> accountIds = new Set<Id>();
  for (Account account : Trigger.new) {
    if (account.BillingStreet != Trigger.oldMap.get(account.Id).BillingStreet) {
      accountIds.add(account.Id);
    }
  }

  if (accountIds.isEmpty()) {
    return;
  }

  List<Account> accounts = [
    SELECT Id, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry
    FROM Account
    WHERE Id IN :accountIds
  ];

  for (Account account : accounts) {
    String address = account.BillingStreet + ', ' + account.BillingCity + ', ' + account.BillingState + ' ' + account.BillingPostalCode + ' ' + account.BillingCountry;
    GeocodingResponse response = GoogleGeocodingAPI.getGeocodingResponse(address);
    if (response.status == 'OK') {
      Result result = response.results[0];
      account.BillingLatitude = result.geometry.location.lat;
      account.BillingLongitude = result.geometry.location.lng;
    }
  }

  update accounts;
}

このトリガーは、取引先の請求先住所が変更されたときに、その住所の緯度経度を取得して、取引先の請求先住所の緯度経度を更新します。

A1.の感想

Geocoding API実行クラスが分かれて、さらによくなっている。

しかし、住所が変更されたかの確認に BillingStreet しか見ていないので、直してもらいましょう。

Q2. 住所が変更されたかの確認はBillingStreetのチェックだけでは不足しています。Apexトリガーを修正してください。

A2. ChatGPTの回答

image.png

そうですね。上記のApexトリガーでは、BillingStreet以外のフィールドも更新された場合に、住所の緯度経度を更新するようにする必要があります。

修正するには、トリガー内で更新されたフィールドを確認し、BillingStreet以外のフィールドも更新されている場合は、その住所の緯度経度を更新するようにします。

次のように修正します。

// GeocodingTrigger.apxt

trigger GeocodingTrigger on Account (after update) {
  Set<Id> accountIds = new Set<Id>();
  for (Account account : Trigger.new) {
    Account oldAccount = Trigger.oldMap.get(account.Id);
    if (account.BillingStreet != oldAccount.BillingStreet || account.BillingCity != oldAccount.BillingCity || account.BillingState != oldAccount.BillingState || account.BillingPostalCode != oldAccount.BillingPostalCode || account.BillingCountry != oldAccount.BillingCountry) {
      accountIds.add(account.Id);
    }
  }

  if (accountIds.isEmpty()) {
    return;
  }

  List<Account> accounts = [
    SELECT Id, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry
    FROM Account
    WHERE Id IN :accountIds
  ];

  for (Account account : accounts) {
    String address = account.BillingStreet + ', ' + account.BillingCity + ', ' + account.BillingState + ' ' + account.BillingPostalCode + ' ' + account.BillingCountry;
    GeocodingResponse response = GoogleGeocodingAPI.getGeocodingResponse(address);
    if (response.status == 'OK') {
      Result result = response.results[0];
      account.BillingLatitude = result.geometry.location.lat;
      account.BillingLongitude = result.geometry.location.lng;
    }
  }

  update accounts;
}

A2.の感想

すごい。いい感じに直った。

しかし、Apexトリガーに直接コードを書きたくないので、Apexクラスに分離してもらいましょう。

Q3. Apexトリガーに直接コードを書くのではなく、Apexクラスを呼び出すようにしたいです。

A3. ChatGPTの回答

image.png

そうですね。Apexトリガーでは、トリガーのロジックを直接書くことが多いですが、Apexクラスを呼び出すことで、トリガーのロジックを分離することができます。

Apexクラスを呼び出すためには、Apexトリガー内でApexクラスをインスタンス化し、メソッドを呼び出す必要があります。

具体的には、次のように修正します。

// GeocodingTrigger.apxt

trigger GeocodingTrigger on Account (after update) {
  Set<Id> accountIds = new Set<Id>();
  for (Account account : Trigger.new) {
    Account oldAccount = Trigger.oldMap.get(account.Id);
    if (account.BillingStreet != oldAccount.BillingStreet || account.BillingCity != oldAccount.BillingCity || account.BillingState != oldAccount.BillingState || account.BillingPostalCode != oldAccount.BillingPostalCode || account.BillingCountry != oldAccount.BillingCountry) {
      accountIds.add(account.Id);
    }
  }

  if (accountIds.isEmpty()) {
    return;
  }

  List<Account> accounts = [    SELECT Id, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry    FROM Account    WHERE Id IN :accountIds  ];

  Geocoding geocoding = new Geocoding();
  geocoding.updateGeocoding(accounts);
}
// Geocoding.cls

public with sharing class Geocoding {
  public void updateGeocoding(List<Account> accounts) {
    for (Account account : accounts) {
      String address = account.BillingStreet + ', ' + account.BillingCity + ', ' + account.BillingState + ' ' + account.BillingPostalCode + ' ' + account.BillingCountry;
      GeocodingResponse response = GoogleGeocodingAPI.getGeocodingResponse(address);
      if (response.status == 'OK') {
        Result result = response.results[0];
        account.BillingLatitude = result.geometry.location.lat;
        account.BillingLongitude = result.geometry.location.lng;
      }
    }

    update accounts;
  }
}

上記のように修正することで、Apexトリガー内でApexクラスを呼び出すことができます。Apexクラスを使用することで、トリガーのロジックを分離することができ、コードの見通しがよくなります。

A4.の感想

分けてくれた。すごい。

でも、条件分岐もクラスに分けて欲しい。

Q4. ApexトリガーからはApexクラスを呼び出すだけにして欲しいです。

A4. ChatGPTの回答

image.png

そうですね。ApexトリガーからはApexクラスを呼び出すだけにすることができます。

Apexクラスを呼び出すだけにするためには、Apexトリガーではトリガーのロジックを記述せず、Apexクラスのみでトリガーのロジックを実装するようにします。

具体的には、次のように修正します。

コードは変わらなかったので省略😢

A4.の感想

コード変わらなかった…

依頼文を変えてみよう。

Q5. 住所が変更された取引先レコードを取得するロジックもApexクラスに分離して欲しいです。

A5. ChatGPTの回答

image.png

そうですね。上記のApexトリガーでは、住所が変更された取引先レコードを取得するロジックがApexクラスに分離されていません。

Apexクラスに分離するためには、Apexトリガーでは住所が変更された取引先レコードを取得するロジックを実装せず、Apexクラスのみで実装するようにします。

具体的には、次のように修正します。

// GeocodingTrigger.apxt

trigger GeocodingTrigger on Account (after update) {
  List<Account> accounts = Geocoding.getAccountsForGeocoding(Trigger.new, Trigger.oldMap);
  if (accounts.isEmpty()) {
    return;
  }

  Geocoding geocoding = new Geocoding();
  geocoding.updateGeocoding(accounts);
}
// Geocoding.cls

public with sharing class Geocoding {
  public static List<Account> getAccountsForGeocoding(List<Account> newAccounts, Map<Id, Account> oldAccountMap) {
    List<Account> accounts = new List<Account>();
    for (Account account : newAccounts) {
      Account oldAccount = oldAccountMap.get(account.Id);
      if (account.BillingStreet != oldAccount.BillingStreet || account.BillingCity != oldAccount.BillingCity || account.BillingState != oldAccount.BillingState || account.BillingPostalCode != oldAccount.BillingPostalCode || account.BillingCountry != oldAccount.BillingCountry) {
        accounts.add(account);
      }
    }
    return accounts;
  }

  public static void updateGeocoding(List<Account> accounts) {
    for (Account account : accounts) {
      String address = account.BillingStreet + ', ' + account.BillingCity + ', ' + account.BillingState + ' ' + account.BillingPostalCode + ' ' + account.BillingCountry;
      GeocodingResponse response = GoogleGeocodingAPI.getGeocodingResponse(address);
      if (response.status == 'OK') {
        Result result = response.results[0];
        account.BillingLatitude = result.geometry.location.lat;
        account.BillingLongitude = result.geometry.location.lng;
      }
    }

    update accounts;
  }
}

A5.の感想

うーん。。。期待したのとはちょっと違うけど、まあこんなところかな。終わろう。

終わりに

すごくない?

でも、全然ひどいコードを書いて、いくら言っても直らない時もありましたw

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?