tyoshikawa1106のブログ

- Force.com Developer Blog -

SFDC:Salesforce DXの開発フローを試してみました

Salesforce DXでLightningアプリの開発をやってみました。

CLIの環境構築とエディタについてはこちら。

DevHub組織への接続

DevHub組織へログインします。

$ sfdx force:auth:web:login -d -a DevHub


コマンドを実行するとブラウザでログインページが表示されるのでいつもどおりログインと認証をすれば完了です。下記コマンドで正しく接続できているか確認できます。

$ sfdx force:org:list

f:id:tyoshikawa1106:20180105144004p:plain


上のコマンドでDevHubというエイリアス名で登録できているので、以後は下記コマンドでログイン可能になります。

$ sfdx force:org:open -u DevHub
オプションの見方
  • d : DevHub組織としての登録
  • a : エイリアスを登録する際に指定

Salesforce DXのプロジェクト作成

デスクトップにgeolocationという名前のプロジェクトを作成します。

$ cd desktop
$ mkdir app
$ cd app
$ sfdx force:project:create -n geolocation
$ cd geolocation


VS Codeでgeolocationフォルダを表示します。
f:id:tyoshikawa1106:20180105144838p:plain

スクラッチ組織の作成

下記のコマンドでスクラッチ組織を作成できます。スクラッチ組織のエイリアス名は「GeoAppScratch」を指定。

$ sfdx force:org:create -s -f config/project-scratch-def.json -a GeoAppScratch


作成後はDevHub組織でスクラッチ組織の情報にアクセスできます。
f:id:tyoshikawa1106:20180105145934p:plain

スクラッチ組織作成時の重要なオプション
  • s : このスクラッチ組織をこのプロジェクトのデフォルト組織にすることを指定します。

これでオブジェクト作成コマンドなどはスクラッチ組織を対象となります。


force:org:listコマンドを確認したところこのようになっていました。
f:id:tyoshikawa1106:20180105150711p:plain

スクラッチ組織にアクセス

下記コマンドでスクラッチ組織を開きます。-sでデフォルト指定しているので組織名は指定不可になっていると思います。

$ sfdx force:org:open


正しくスクラッチ組織にアクセスしているかは組織IDで確認できると思います。マイドメインは自動で登録されました。※DevHub組織との関連性なし。
f:id:tyoshikawa1106:20180105151406p:plain

確認中に気になった点

DevHub組織で作成したカスタムオブジェクトはスクラッチ組織に移行されていませんでした。Sandboxのようには使えないのかもしれません。

カスタム項目の作成

スクラッチ組織でカスタム項目を作成します。

  • [取引先] をクリックします。
  • [項目とリレーション] セクションで、[新規] をクリックします。
  • データ型に [地理位置情報] を選択し、[次へ] をクリックします。
  • 次の詳細を入力して、[次へ] をクリックします。
  • 項目の表示ラベル: Location (場所)
  • 緯度および経度表示の表記法: 小数
  • 小数部の桁数: 7
  • 項目名: Location (場所)

f:id:tyoshikawa1106:20180105152105p:plain


次の動作確認のため権限は除外します。
f:id:tyoshikawa1106:20180105152149p:plain


Location項目のアクセス権限を付与する権限セットを作成します。
f:id:tyoshikawa1106:20180105152423p:plain

f:id:tyoshikawa1106:20180105152436p:plain


下記のコマンドでユーザに権限セットの割当ができます。

$ sfdx force:user:permset:assign -n Geolocation

※Geolocationは権限セットの名前を指定

f:id:tyoshikawa1106:20180105152621p:plain


無事に割り当てが実行できていました。
f:id:tyoshikawa1106:20180105152655p:plain

スクラッチ組織の変更をローカル環境に反映

下記のコマンドを実行するとスクラッチ組織の変更をローカルのプロジェクトに反映できます。

$ sfdx force:source:pull

f:id:tyoshikawa1106:20180105152945p:plain


変更箇所のみが反映されるようです。ローカルのプロジェクトをみるとlayoutやobjectフォルダが追加されていました。
f:id:tyoshikawa1106:20180105153057p:plain

Gitでバージョン管理

Salesforce DXはGitなどによるバージョン管理を行なうことが前提の仕組みです。GitHubやBitbucketなどありますが、今回はプライベートリポジトリが利用できるBitbucketを使ってみます。


このような感じでリポジトリの新規作成を行いました。
f:id:tyoshikawa1106:20180106150547p:plain


これでssh urlの部分が取得できます。下記のコマンドを実行します。

$ git init
$ git remote add origin <ssh url>
$ git add -A
$ git commit -m "Add custom object and permset"
$ git push origin master


cdコマンドでgeolocationフォルダの一番上に移動している状態で実行しました。
f:id:tyoshikawa1106:20180106151004p:plain


これでローカルのソースコードをBitbucketのリポジトリに保存できました。
f:id:tyoshikawa1106:20180106151245p:plain


注意しないとプロジェクトごとにバラバラになってしまいそうなフォルダの階層管理もDXでは雛形を用意してくれます。またREADME.mdファイルもテンプレートがあるのでこれに合わせていけばよさそうです。

サンプルデータ

組織作成後に一番困るのはサンプルデータの作成です。Salesforce DXではサンプルデータを簡単にインポートできる仕組みが用意されています。

Salesforce組織でテストデータを作成

まずはスクラッチ組織にアクセスします。未ログインの場合はopenコマンドでアクセス可能です。

$ sfdx force:org:open


今回作成するのは取引先レコードです。
f:id:tyoshikawa1106:20180106151912p:plain


データの準備ができたらdataフォルダをプロジェクトに追加します。

$ mkdir data


次のコマンドでスクラッチ組織のサンプルデータをエクスポートできます。

$ sfdx force:data:tree:export -q "SELECT Name, Location__Latitude__s, Location__Longitude__s FROM Account WHERE Location__Latitude__s != NULL AND Location__Longitude__s != NULL" -d ./data

f:id:tyoshikawa1106:20180106152157p:plain


サンプルデータはJSON形式でエクスポートされます。インポートする際にJSON形式になっていればいいので、手動で作成したりも可能となっています。
f:id:tyoshikawa1106:20180106152252p:plain


作成したサンプルデータをインポートすれば組織作成後に簡単にテストデータを作成できます。わかりやすくなるように先ほどのデータは削除しました。
f:id:tyoshikawa1106:20180106152518p:plain



インポートは次のコマンドになります。

$ sfdx force:data:tree:import --sobjecttreefiles data/Account.json

f:id:tyoshikawa1106:20180106152608p:plain


スクラッチ組織を確認したところ正常に作成できていました。
f:id:tyoshikawa1106:20180106152734p:plain


こうしたインポート用のサンプルデータもGitリポジトリで管理できるようになります。
f:id:tyoshikawa1106:20180106153033p:plain


入力規則やトリガの追加があるので一度つくったサンプルデータをアップデートしていくのは頻繁に発生すると思います。こうした情報も管理できるようになるのはすごく良さそうです。

地理位置情報アプリケーションの作成

ここまでで開発用組織とサンプルデータ、Gitリポジトリの準備ができました。ここからLightningアプリの開発を行います。

Apex コントローラクラスの作成

次のコマンドでApexクラスを作成できます。

$ sfdx force:apex:class:create -n AccountController -d force-app/main/default/classes

f:id:tyoshikawa1106:20180106153416p:plain


エディタで次のように処理を実装します。 (インデントは適当ですが・・)
f:id:tyoshikawa1106:20180106153603p:plain


保存後に下記のコマンドを実行するとスクラッチ組織にアップロードされます。

$ sfdx force:source:push

f:id:tyoshikawa1106:20180106153719p:plain

public with sharing class AccountController {
 @AuraEnabled
 public static List<Account> findAll() {
 return [SELECT Id, Name, Location__Latitude__s, Location__Longitude__s
   FROM Account
   WHERE Location__Latitude__s != NULL AND Location__Longitude__s !=
          NULL
   LIMIT 50];
  }
}

無事に保存できていました。
f:id:tyoshikawa1106:20180106153815p:plain


エラーチェックも当然実施されました。
f:id:tyoshikawa1106:20180106153906p:plain

Lightning コンポーネントの作成

続いてLightning コンポーネントの作成です。下記のコマンドを実行します。

$ sfdx force:lightning:component:create -n AccountListItem -d force-app/main/default/aura

f:id:tyoshikawa1106:20180106154052p:plain


AccountListItemのソースコードはこんな感じ。
【AccountListItem.cmp】

<aura:component>
  <aura:attribute name="account" type="Account"/>
  <li><a>{!v.account.Name}</a></li>
</aura:component>

【AccountListItem.css】

.THIS {
    border-bottom: solid 1px #DDDDDD;
}

.THIS a {
    display: block;
    padding: 20px;
    color: inherit;
}

.THIS a:active {
    background-color: #E8F4FB;
}

コーディングはVS Codeから行っています。
f:id:tyoshikawa1106:20180106154319p:plain



AccountList コンポーネントを作成します。

$ sfdx force:lightning:component:create -n AccountList -d force-app/main/default/aura

f:id:tyoshikawa1106:20180106154412p:plain


【AccountList.cmp】

<aura:component controller="AccountController">

    <aura:attribute name="accounts" type="Account[]"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <ul>
    <aura:iteration items="{!v.accounts}" var="account">
        <c:AccountListItem account="{!account}"/>
    </aura:iteration>
    </ul>

</aura:component>

【AccountListController.js】

({
    doInit : function(component, event) {
        var action = component.get("c.findAll");
        action.setCallback(this, function(a) {
          component.set("v.accounts", a.getReturnValue()); 
        });
        $A.enqueueAction(action);
    }
})

【AccountList.css】

.THIS {
    list-style-type: none;
    padding: 0;
    margin: 0;
    background: #FFFFFF;
     height: 100%;
}


AccountLocatorコンポーネントを作成します。

$ sfdx force:lightning:component:create -n AccountLocator -d force-app/main/default/aura

f:id:tyoshikawa1106:20180106155408p:plain

【AccountLocator.cmp】

<aura:component implements="force:appHostable">
    <div>
        <div>AccountMap goes here</div>
        <div>
          <c:AccountList/>
        </div>
    </div>
</aura:component>

【AccountLocator.css】

.THIS {
  position:absolute;
  height: 100%;
  width: 100%;
  background: #FFFFFF;
}

.THIS>div {
  height: 50%;
}


これでコンポーネントの準備ができました。下記コマンドでスクラッチ組織にプッシュします。

$ sfdx force:source:push

f:id:tyoshikawa1106:20180106155702p:plain


作成したLightningコンポーネントへのアクセスはタブから行います。
f:id:tyoshikawa1106:20180106155905p:plain


このように表示されればひとまずOKです。
f:id:tyoshikawa1106:20180106160009p:plain


スクラッチ組織でタブを作成したのでローカルプロジェクトに持ってきます。

$ sfdx force:source:pull

f:id:tyoshikawa1106:20180106161538p:plain

地図表示処理の実装

Leafletのサイトから最新の安定バージョンをダウンロードします。

Download - Leaflet - a JavaScript library for interactive maps


静的リソースに保存します。
f:id:tyoshikawa1106:20180106161856p:plain


ローカルプロジェクトに反映します。

$ sfdx force:source:pull


試していてちょっとびっくりしたのですがSalesforce DXの機能で管理すればZipファイルの中身がきちんと解凍された状態で取得されました。
f:id:tyoshikawa1106:20180106162210p:plain

AccountMap コンポーネントの作成

AccountMap コンポーネントを作成します。

$ sfdx force:lightning:component:create -n AccountMap -d force-app/main/default/aura

【AccountMap.cmp】

<aura:component>

    <aura:attribute name="map" type="Object"/>

    <ltng:require styles="/resource/leaflet/leaflet.css"
        scripts="/resource/leaflet/leaflet.js"
        afterScriptsLoaded="{!c.jsLoaded}" />

    <div id="map"></div>

</aura:component>

【AccountMapController.js】

({
   jsLoaded: function(component, event, helper) {

      var map = L.map('map', {zoomControl: false}).setView([37.784173, -122.401557], 14);
      L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
        {
              attribution: 'Tiles © Esri'
        }).addTo(map);
      component.set("v.map", map);
  }
})

【AccountMap.css】

.THIS {
   width: 100%;
   height: 100%;
}

【AccountLocator.cmp】

<aura:component implements="force:appHostable">

    <div>
        <div>
            <c:AccountMap />
        </div>

        <div>
            <c:AccountList />
        </div>
    </div>

</aura:component>


スクラッチ組織にアップロード

$ sfdx force:source:push


Leafletの地図が表示されることを確認できます。
f:id:tyoshikawa1106:20180106162930p:plain

地図にマーカーを追加する Lightning イベントの作成

AccountsLoaded イベントを作成します

$ sfdx force:lightning:event:create -n AccountsLoaded -d force-app/main/default/aura

f:id:tyoshikawa1106:20180106163905p:plain

【AccountsLoaded.evt】

<aura:event type="APPLICATION">
    <aura:attribute name="accounts" Type="Account[]"/>
</aura:event>

【AccountList.cmp】

<aura:component controller="AccountController">
    <aura:registerEvent name="accountsLoaded" type="c:AccountsLoaded"/>
    <aura:attribute name="accounts" type="Account[]"/>
    <ltng:require styles="/resource/leaflet/leaflet.css" scripts="/resource/leaflet/leaflet.js" afterScriptsLoaded="{!c.doInit}" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <ul>
    <aura:iteration items="{!v.accounts}" var="account">
        <c:AccountListItem account="{!account}"/>
    </aura:iteration>
    </ul>

</aura:component>

【AccountListController.js 】

({
    doInit : function(component, event) {
        var action = component.get("c.findAll");
        action.setCallback(this, function(a) {
            component.set("v.accounts", a.getReturnValue());
            var event = $A.get("e.c:AccountsLoaded");
            event.setParams({"accounts": a.getReturnValue()});
            event.fire();
        });
    $A.enqueueAction(action);
    }
})

【AccountMap.cmp】

<aura:component>

    <aura:attribute name="map" type="Object"/>
    <aura:handler event="c:AccountsLoaded" action="{!c.accountsLoaded}"/>

    <ltng:require styles="/resource/leaflet/leaflet.css"
        scripts="/resource/leaflet/leaflet.js"
        afterScriptsLoaded="{!c.jsLoaded}" />

    <div id="map"></div>

</aura:component>

【AccountMapController.js】

({
    jsLoaded: function(component, event, helper) {

        var map = L.map('map', {zoomControl: false}).setView([37.784173, -122.401557], 14);
        L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
             {
                 attribution: 'Tiles © Esri'
             }).addTo(map);
        component.set("v.map", map);
    },

    accountsLoaded: function(component, event, helper) {

        // Add markers
        var map = component.get('v.map');
        var accounts = event.getParam('accounts');
        for (var i=0; i<accounts.length; i++) {
            var account = accounts[i];
            var latLng = [account.Location__Latitude__s, account.Location__Longitude__s];
            L.marker(latLng, {account: account}).addTo(map);
        }  
    }
})


更新を反映します。

$ sfdx force:source:push


マーカーが表示されるようになりました。
f:id:tyoshikawa1106:20180106164439p:plain

アプリケーションの検証

Trailheadにアプリケーション検証用のスクラッチ組織を作成してそちらで検証するのがいいと説明がありました。

開発で使用したスクラッチ組織を使用してテストを行うことももちろんできますが、常に新しいスクラッチ組織で開始することをお勧めします。新しいスクラッチ組織を使用することで、すべてのソースが組織の外部に適切に置かれていることを確認できます。


新しいスクラッチ組織を作成します。

$ sfdx force:org:create -f config/project-scratch-def.json -a GeoTestOrg


ローカルのソースとメタデータをスクラッチ組織に転送します。

$ sfdx force:source:push -u GeoTestOrg


権限セットを割り当てます。

$ sfdx force:user:permset:assign -n Geolocation -u GeoTestOrg


組織にサンプルデータを読み込みます。

$ sfdx force:data:tree:import -f data/Account.json -u GeoTestOrg


組織を開きます。

$ sfdx force:org:open -u GeoTestOrg


新しいスクラッチ組織でも問題なく動きました。
f:id:tyoshikawa1106:20180106165816p:plain



今回は基本的な部分なのでその他の部分も覚えないといけないことが多そうです。ひとまずスクラッチ組織の使い方はなんとなく理解できたのでよかったです。(Sandboxみたいに本番組織のコピー組織だと思ってました。)

参考