18
9

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 5 years have passed since last update.

Salesforce:3階層くらいの関連したレコードをスクラッチ組織へインポートする方法

Last updated at Posted at 2019-09-27

事の発端

sfdx force:data:tree:importのお話です。
普通は画面でデータ作ってdata:tree:exportしてそれをimportに渡したらいいんだけど、

データの関連が3階層まで及ぶと厄介である事に気づいてしまった・・・

スクラッチ組織の環境構築で必要なテストデータ作るときに、困ったこと。
なんというか、SOQLが3階層の連携だとエラーになるの。。。

エラー例)

SELECT Id
     , mynamespace__AAA__c
     , mynamespace__BBB__c
     , mynamespace__CCC__c
     ,(
       SELECT Id
            , mynamespace__DDD__c
            , mynamespace__EEE__c
            , mynamespace__FFF__c
            , (
               SELECT Id
                    , mynamespace__GGG__c 
               FROM mynamespace__CObjects__r
               )
       FROM mynamespace__BObjects__r
       )
FROM mynamespace__AObject__c

SOQL statements cannot query aggregate relationships more than 1 level away from the root entity object.

エラーメッセージ内容は書いている通り。

改めて、SOQLには「親-子リレーションの 1 つのレベルだけが 1 つのクエリ内で指定可能」という制約があった。

純粋にApexクラスの中で処理を実装するだけならば、2回に分けて実装すれば解決する。

しかし、

『 スクラッチ組織上でテストデータを作った!
  そのテストデータ環境をメンバー間で使いまわしたい! 』

という場合は、ちょっと面倒であることに気づいた。

ちょっとSOQLを知らない人向けに補足

ちょっとSOQLを知らない人向けに補足(不要な人はスキップ)
SalesforceではSQLとは言わず、SOQLと言います。

SOQLは、Salesforce Object Query Language の略で、
Salesforce独特のデータ問い合わせコマンドです。

SQLに似ているけどSQLではないことがポイント
取得した結果は、ただの配列という形ではなく、JSON形式のようにも取得できるし、Map<Id,Record>形式のように取得することも、List形式で取得する事のも可能。

なお、今回の事例は以下のように実装すると動きます。

SELECT Id
     , mynamespace__AAA__c
     , mynamespace__BBB__c
     , mynamespace__CCC__c
     ,(
       SELECT Id
            , mynamespace__DDD__c
            , mynamespace__EEE__c
            , mynamespace__FFF__c
       FROM mynamespace__BObjects__r
       )
FROM mynamespace__AObject__c

上記のSOQLを実行した結果は、以下のようになります。

mynamespace__AObject__c: [ 
  { Id: 'xxx'
     , mynamespace__AAA__c: 'xxx'
     , mynamespace__BBB__c: 'xxx'
     , mynamespace__CCC__c: 'xxx'
     , mynamespace__BObjects__r: [
         {  Id: 'bbb'
           , mynamespace__DDD__c: 'bbb'
           , mynamespace__EEE__c: 'bbb'
           , mynamespace__FFF__c: 'bbb'
         },
         {...略},
         {...略},
         {...略} ]
  },
  {...略},
  {...略},
  {...略} ]

SOQLは便利な反面、厳密なクエリ制限があるのです。
簡単な補足終了!

では、3階層のデータを入れる為には何をするのか。

ヒントは以下の公式サイトの一番したに記載があります。
https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_test_data_example.htm

もっと複雑な例をお探しですか?  easy-spaces-lwc  サンプルアプリケーションには、アカウント、関連取引先責任者、および 3 レベルの深度のカスタムオブジェクトチェーンをインポートする方法を示すデータプランがあります。

そう!
これだけしか記載がないのである!!!
なんて、クレイジー!

じゃあ easy-spaces-lwc で分かること

紐解いていくと、以下のような構成になっている。
easy-spaces-lwcのデータ階層について (1).png

私の求めている情報が正に、Reservation__cの存在だ。

さて、この指定されたリンク先の Plan2.jsonというファイルは、プラン実行ファイルである。

easy-spaces-lwc/data/Plan2.json
[
    {
        "sobject": "Account",
        "saveRefs": true,
        "resolveRefs": false,
        "files": ["Accounts.json"]
    },
    {
        "sobject": "Contact",
        "saveRefs": true,
        "resolveRefs": true,
        "files": ["Contacts.json"]
    },
    {
        "sobject": "Market__c",
        "saveRefs": true,
        "resolveRefs": false,
        "files": ["Market__cs.json"]
    },
    {
        "sobject": "Space__c",
        "saveRefs": false,
        "resolveRefs": true,
        "files": ["Space__cs.json"]
    },
    {
        "sobject": "Reservation__c",
        "saveRefs": false,
        "resolveRefs": true,
        "files": ["Reservation__cs.json"]
    }
]

中身を読み解くと、以下の属性を持っている。

  • sobject
  • saveRefs
  • resolveRefs
  • files

sobjectは、Salesforce上のオブジェクトマネージャーにも表示されたオブジェクトを表すキーワードであることがわかる。
filesは、実際のテストデータを記載したファイルが含まれている。

easy-spaces-lwc/data/Accounts.jsonの一部抜粋
        {
            "attributes": {
                "type": "Account",
                "referenceId": "AccountRef1"
            },
            "Name": "Thompson, Skidmore and Smith"
        },
easy-spaces-lwc/data/Contacts.jsonの一部抜粋
        {
            "attributes": {
                "type": "Contact",
                "referenceId": "ContactRef1"
            },
            "Email": "phaidra.rayer@sw-global.com",
            "FirstName": "Phaidra",
            "LastName": "Rayer",
            "MailingCity": "San Bruno",
            "MailingCountry": "US",
            "MailingPostalCode": "94066",
            "MailingState": "CA",
            "MailingStreet": "87 Northland Place",
            "MobilePhone": "+1 (408) 505-1578",
            "AccountId": "@AccountRef1",
            "Reservation_Status__c": "Draft"
        },
easy-spaces-lwc/data/Reservation__cs.jsonの一部抜粋
         {
            "attributes": {
                "type": "Reservation__c",
                "referenceId": "Reservation__cRef1"
            },
            "Contact__c": "@ContactRef1",
            "Market__c": "@Market__cRef6",
            "Start_Date__c": "2018-08-30",
            "End_Date__c": "2018-09-30",
            "Status__c": "Draft",
            "Total_Number_of_Guests__c": 14
        },

キーワードはreferenceIdである。そして、referenceIdで指定した値を@で始まる文字列で指定しているところに注目

サンプルでは、
Accountオブジェクトの"referenceId": "AccountRef1"を、
Contactオブジェクトの"AccountId": "@AccountRef1"という形式で参照している。
スクリーンショット 2019-09-27 16.51.48.png

また、Contactオブジェクトの"referenceId": "ContactRef1"を、
Reservation__cオブジェクトの"Contact__c": "@ContactRef1"という形式で参照している。
スクリーンショット 2019-09-27 16.52.06.png

改めて、3階層くらい関連したオブジェクトをスクラッチ組織にインポートするためには、このような関連情報を記載したファイルを作成すれば良いのである!!!

あとは、以下のようなSFDXコマンドの実行でOK!(以下は指定のサンプルに記載がありました)

7.Load sample data:
sfdx force:data:tree:import -p ./data/Plan2.json

でも、じゃあ saveRef と resolveRef って何?

上記のプラン実行ファイルを眺めていると、saveRefresolveRefってキーワードが出てくる。これ何?って思ったから調べたことを以下に整理しました。
なお整理に当たっては、以下やりとりを参考にしております。
https://salesforce.stackexchange.com/questions/212034/what-are-saverefs-and-resolverefs?noredirect=1&lq=1

saveRef

 referenceIdを保存するかどうかのフラグです。
 【具体例】
 基本的に、sfdx force:data:tree:import -p ./data/Plan2.jsonを実行したとき、
 テストデータの値に沿ってスクラッチ組織にデータを登録する。
 その際、登録することで正しいID(0015000000abcde)が発行される。
  ※内部では"Id": "0015000000abcde"が登録されている。
 このときスクラッチ組織上にてreferenceIdの情報を残すかどうかである。
 ■"saveRefs": falseの場合
    "referenceId": "AccountRef1"を削除する。
    ※ただし、後続のファイルで、ルックアップフィールドが@で始まる値(例@AccountRef1)を使用しようとしてもエラーとなります。
 ■"saveRefs": trueの場合
    "referenceId": "AccountRef1"を削除しない。
    "AccountRef1"=0015000000abcdeという情報が保存される。

resolveRef

 後続のファイルで、ルックアップフィールドが@で始まる値(例@AccountRef1)を使用したとき、以前に保存した参照を解決するかどうかのフラグです。
 【具体例】
 ■"resolveRef": falseの場合
    @で始まる値を参照情報として利用しない。入力値として扱う。
    "AccountId": "@AccountRef1"という情報は、そのまま文字列として指定する。
    ※ただし、もしこれが参照制約のあるフィールドの場合、エラーになります。
 ■"resolveRef": trueの場合
    @で始まる値を参照情報として利用する。
    "AccountId": "@AccountRef1"という情報は
    "AccountRef1"=0015000000abcdeという情報として取り扱う。

判断基準

改めておさらい。
スクリーンショット 2019-09-27 17.34.23.png

まとめ

3階層くらいの関連したレコードをスクラッチ組織へインポートする方法は、
以下の3つを意識した テストデータJSONファイル と、 プラン実行ファイル をimportすれば良い!

  • referenceId
    • 重複しないユニークなID文字列を任意に定義してあげる。
    • 別から参照するときは、@を接頭辞に付けてあげる。
  • saveRefs
    • 自身オブジェクトのIDが別オブジェクトから参照されるときTrueと指定が必要
  • resolveRef
    • @を使用して、別オブジェクトのIDを参照しているときTrueと指定が必要)

以上です。ではでは!

18
9
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
18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?