Visualforce ワークブック バージョン 6, Summer ’15 @salesforcedocs 最終更新日: 2015/5/21 © Copyright 2000–2015 salesforce.com, inc. All rights reserved. Salesforce およびその他の名称や商標は、salesforce.com, inc. の登録商標です。本ドキュメントに記載されたその他の商標は、各社に所有権があります。 目次 Visualforce ワークブックへようこそ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 このワークブックの対象読者 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Visualforce の概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Visualforce ページの作成と確認 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Visualforce 開発モードを有効にする . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Visualforce ページを作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Visualforce ページを編集する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 作成済みの Visualforce ページを確認する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 ページエディタ以外の方法でページを作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 属性の追加と入力支援機能の使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 入力支援機能を使用して属性を追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 新しいコンポーネントを追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 ネストしたコンポーネントを追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 シンプルな変数と数式の理解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 グローバル変数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 基本数式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 条件式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 標準コントローラの使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 レコードの ID を取得する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 レコードのデータを取得して表示する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 他の項目を表示する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 リレーションが設定されたレコードの項目を表示する . . . . . . . . . . . . . . . . . . . . . 15 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 標準のユーザインターフェースコンポーネントの使用 . . . . . . . . . . . . . . . . . . . . . . . . . 15 特定のレコードに関する詳細と関連リストを表示する . . . . . . . . . . . . . . . . . . . . . 16 表示する項目を指定する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 テーブルを表示する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Ajax による Visualforce ページの更新 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 動的に更新する領域を明示的に指定する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 動的な再表示を追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 ページの上書きと参照 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 ページのデフォルトの表示を上書きする . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 標準のページレイアウトに Visualforce ページを埋め込む . . . . . . . . . . . . . . . . . . . . 21 目次 Visualforce ページにリンクするボタンを作成する . . . . . . . . . . . . . . . . . . . . . . . . . 21 Visualforce ページから他の Visualforce ページや外部 URL を参照するハイパーリン クを作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 フォームを使用したデータの入力 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 基本となるフォームを作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 項目表示ラベルを表示する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 警告やエラーメッセージを表示する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 テンプレートを使用したページの再利用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 テンプレートの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 他のページでテンプレートを使用する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 Visualforce ページを他の Visualforce ページに挿入する . . . . . . . . . . . . . . . . . . . . . 27 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Apex の概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 開発環境を設定する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 拡張 Warehouse データモデルをインストールする . . . . . . . . . . . . . . . . . . . . . . . . 28 モバイルブラウザ Web アプリケーションにアクセスする . . . . . . . . . . . . . . . . . . . 29 Salesforce1 モバイルアプリケーションをダウンロードする . . . . . . . . . . . . . . . . . . 29 開発者コンソールの使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 開発者コンソールの有効化 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 開発者コンソールを使用した Apex コードの実行 . . . . . . . . . . . . . . . . . . . . . . . . . 30 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 クラスの作成とインスタンス化 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 開発者コンソールを使用した Apex クラスの作成 . . . . . . . . . . . . . . . . . . . . . . . . . 33 クラスメソッドのコール . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Salesforce ユーザインターフェースを使用した Apex クラスの作成 . . . . . . . . . . . . . 35 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 WarehouseUtils クラスの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 WarehouseUtils Apex クラスの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 「スタブ」findNearbyWarehouses メソッドの追加 . . . . . . . . . . . . . . . . . . . . . . . . 38 クエリの実行および結果の取得 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 まとめとコードの確認 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 WarehouseUtils クラスのテストおよびデバッグ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Apex テストクラスを作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 テストメソッドを追加してコードを設定する . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 findNearbyWarehouses メソッドをテストする . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 テストを実行してテスト結果を確認する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 バグの検出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 バグのテストの記述 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 バグの修正 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 まとめとコードの確認 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 目次 Visualforce と Apex の連携 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 位置を認識する Visualforce ページの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 WarehouseUtils クラスにリンクする Visualforce ページを作成する . . . . . . . . . . . . . 51 静的リソースをページに追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 地図を表示する場所を追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 JavaScript を Warehouse のクエリに追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 JavaScript を追加して地図を作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 JavaScript を追加して Warehouse マーカーを地図に追加する . . . . . . . . . . . . . . . . . 56 まとめとコードの確認 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 近隣の Warehouse ページを Salesforce1 に追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 ページのタブを作成する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 モバイルナビゲーションにタブを追加する . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 アプリケーションを試す . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Apex コントローラを使用する Visualforce ページ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Visualforce ページでの商品データの表示 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Visualforce ページでのカスタム Apex コントローラの使用 . . . . . . . . . . . . . . . . . . . 66 Apex コントローラでの内部クラスの使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Apex コントローラへの action メソッドの追加 . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 まとめと今後の方向性 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Visualforce ワークブックへようこそ Visualforce は、Force.com プラットフォームでネイティブにホストできる高度なカスタムユーザインターフェー スを、開発者が作成できるようにするフレームワークです。このワークブックでは、Visualforce のさまざまな 機能を紹介し、Apex を使用して Visualforce ページに複雑なロジックを追加する方法を説明します。 Force.com の標準ユーザインターフェースに似たユーザインターフェースのほか、HTML、CSS、JavaScript が提供 するあらゆるコントロールを使用して独自のユーザインターフェースを作成する方法を学習します。合わせ て、Visualforce ページで共通に利用するコンポーネントを作成する方法や、Visualforce とアプリケーションを連 携させる方法なども学びます。さらに、Visualforce の基盤である MVC (Model–View–Controller: モデル–ビュー–コン トローラ) や、Apex コードの基本事項も学習します。 ワークブックのバージョン このワークブックは、Winter '15 リリース向けに更新されており、最終改訂日は 2014 年 9 月 5 日です。Force.com の Winter '15 バージョン (API バージョン 34.0) 以降を使用している場合は、このワークブックのチュートリアル をすべて利用できます。 このワークブックの最新バージョンをダウンロードする場合は、 https://developer.salesforce.com/page/Force.com_workbook にアクセスしてください。 ご利用になる前に 一連のチュートリアルは、Force.com Developer Edition 組織 (または短縮して「DE 組織」) を使用して作業する ことを前提としています。DE 組織とは、アプリケーションの開発、パッケージ化、テスト、インストールを 行うためのすべての機能と権限が備わった環境です。DE 組織は、http://sforce.co/ZfioJ6 から登録を行うと無償で 使用できます。このワークブックでは、DE 組織をはじめとした、開発をサポートするすべての Force.com 環境 で活用できる技法をご紹介していきます。 さらに、Force.com自体について少し学習すると、コンテキストを理解するうえで役立ちます。『Force.comワー クブック』の最初のいくつかのチュートリアルを参照してください。 最後に、「Salesforce でサポートされているブラウザ」が必要です。Chrome、Firefox、Safari のほか、Internet Explorer の最近のバージョンも利用できます。 終了後にできること このワークブックの終了後は、Visualforce および Force.com のはるかに高度な開発を行えるようになります。今 後役立つと思われる資料を次に示します。 • コードではなくクリック操作を用いる宣言型の Force.com 開発についての詳細は、『Force.com ワークブッ ク』(https://developer.salesforce.com/page/Force.com_workbook) を参照してください。 • Visualforce 早見表は https://developer.salesforce.com/page/Cheat_Sheets からダウンロードできます。 1 Visualforce ワークブックへようこそ このワークブックの対象読者 • Visualforce の詳細なドキュメントは、『Visualforce 開発者ガイド』で提供されています。 • Apex プログラミング言語の理解を深める場合は、『Apex ワークブック』を参照してください。 • Force.com の理解を深め、記事やドキュメント、サンプルコードを確認する場合は、Developer Force (http://developer.salesforce.com) をご利用ください。 このワークブックの対象読者 このワークブックは、次の 2 種類の読者を対象に作成されています。 • 経験豊富な Web 開発者: HTML マークアップを十分に理解し、おそらくは JavaScript や PHP、Ruby、C#、Java な どのバックエンド言語でコードを記述する方法を知っている (Apexの知識もあればすでに一歩先を行ってい ます)。 • 経験豊富な Salesforce システム管理者: Salesforce の知識があり、HTML の基本的な使用経験がある (Web 開発者 としてのプログラミングの実績は問わない)。 このどちらの方々も、本書から多くのことを習得できます。 このワークブックは、次の 3 つのセクションで構成されています。 • 「Visualforce の概要」では、Visualforce の基本事項を説明します。HTML マークアップの知識があれば役立ち ますが、プログラマでなくても各レッスンを行うことができます。Account や Contact など Salesforce の組み込 みオブジェクトの操作では、Visualforceや HTML マークアップのみを使用し、プログラミングは行いません。 「Visualforce のみ」で実に多くのことが行えます。 • 「Apexの概要」では、Force.com プラットフォームのプログラミング言語である Apex について、Visualforce で の使い方を中心にわかりやすく説明します。プログラマであれば、既に持っている知識を応用して、Force.com アプリーケーションのカスタムロジックを記述する方法をすぐに理解できます。プログラマでなくとも意 欲のあるシステム管理者であれば、順を追って基本事項を理解できます。もっと新しい知識を学びたくな るかもしれません。 • 「Visualforce および Apex の連携」では、2 つのツールを組み合わせて、カスタムのインターフェースや動 作を備えたアプリケーションを作成する方法について説明します。このセクションには理解すべきコード がたくさんあり、避けて通ることはできません。Visualforce、Apex、JavaScript、Google Maps API、さらにカスタ ムオブジェクトを操作してアプリケーションを作成し、ユーザがSalesforce1のアプリケーションに外出先か らアクセスできるようにします。説明文には多くの専門用語が出てきますが、驚くほど簡単にモバイルユー ザ向けの位置認識ページを作成できます。 経験豊富な開発者の場合、最初のセクションは必要ないと思うかもしれません。最初のセクションの練習問題 を行わない場合でも、マークアップ言語の基本事項を押さえるためにセクションを一読することをお勧めしま す。また、Visualforce のみでも実に多くのことが行えます。自ら記述していないコードは、維持する必要のな いコードです。 プログラマでない読者は、本書に記載されているコードに圧倒されるかもしれません。けれども、心配するこ とはありません。Salesforce を習得したことは 1 つの成果です。Salesforce を習得できた方なら、本書のすべての 練習問題に取り組むことができます。有用な技法を習得するために、コードの全行を理解する必要はありませ ん。 Force.comを使い始めたばかりのシステム管理者には、本書がやや難しく感じられるかもしれません。プラット フォームおよびポイントアンドクリックによるアプリケーション開発の概要については、「Force.com Platform Fundamentals (Force.com プラットフォームの基本)」を参照してください。 2 Visualforce の概要 Visualforceは、Force.comプラットフォーム向けのコンポーネントベースのユーザインターフェースフレームワー クです。Visualforceを使用すると、HTML に似たタグベースのマークアップ言語、拡張と再利用が可能なコンポー ネントのライブラリ、Apexベースのコントローラモデルを含むビューフレームワークにより、高度なユーザイ ンターフェースを構築できます。Visualforce は、Model-View-Controller (MVC) スタイルのユーザインターフェースを サポートするため、非常に柔軟性があります。 このセクションでは、Visualforce マークアップ言語の基本事項を学習します。その基盤に焦点を当てており、 また、Salesforce に含まれる組み込みオブジェクト (Account、Contact など) を操作します。 このセクションでは、次のことを行います。 • 新しい Visualforce ページを作成し、既存のページを編集する。 • 2 種類の Visualforce エディタを使用して、自動推奨ツールで Visualforce のコンポーネントや属性を追加する。 • シンプルな組み込みの Visualforce コンポーネントを大きなページ要素や構造にまとめてページを設計する。 • 組織からデータを読み込んで、詳細ページやリストビューページに表示する。 • データへの変更をキャプチャするフォームを作成して、Salesforce に保存する。 • カスタムページをユーザがアクセスできる Salesforce に追加して、Salesforce 組み込みの、ページの作成、編 集、表示の上書きを行う。 • Ajax を使用してページの変更を実行し、ページ全体を再読み込みすることなく、ページの一部を更新する。 Visualforce ページの作成と確認 このチュートリアルでは、Visualforce ページを新規に作成して編集します。今回作成するページはとてもシン プルなものですが、これをベースに、さまざまな機能を学んでいくことができます。チュートリアルを進める につれ、エディタとページの自動作成に関する知識を身に付けられるでしょう。 作業を開始する前に、前述の「ご利用になる前に」で説明した無料の Force.com Developer Edition 組織を作成して おいてください。 Visualforce 開発モードを有効にする 開発モードを有効にすると、Web ブラウザ内に Visualforce ページエディタが埋め込まれ、コードを確認しなが ら同時にページをプレビューできるようになります。開発モードではまた、コントローラと拡張機能を編集す るための Apex エディタも追加されます。 1. 任意のSalesforceページ上部で、名前の横にある下向き矢印をクリックします。名前の下にあるメニューで、 [設定] または [私の設定] のどちらか表示される方を選択します。 2. 左のペインで、次のいずれかを選択します。 • [設定] をクリックした場合は、[私の個人情報] > [個人情報] を選択します。 • [私の設定] をクリックした場合は、[個人用] > [高度なユーザの詳細] を選択します。 3 Visualforce の概要 Visualforce ページを作成する 3. [編集] をクリックします 4. [開発モード] チェックボックスをオンにして、[保存] をクリックします。 Visualforce ページを作成する Visualforce ページを作成する準備が整いました。では、実際にページを作成してみましょう。 1. ブラウザで、Salesforce インスタンスの URL に「/apex/hello」を追加します。たとえば、Salesforce インス タンスが「https://na1.salesforce.com」の場合、新しい URL は 「https://na1.salesforce.com/apex/hello」となります。すると、次のエラーが表示されます。 2. [Create Page hello (hello ページを作成)] リンクをクリックして、新しいページを作成します。いくつかのデ フォルトのマークアップが使用された新しいページが表示されます。 メモ: ページの下部にページエディタが表示されていない場合は、ステータスバーの [hello] タブをク リックします。 完成です。ページにはデフォルトのテキストと、ソースコードを表示する埋め込みのページエディタが表示さ れます。以上が、ワークブックのこのセクションでページを作成するときの基本的な手順です。 Visualforce ページを編集する Visualforce ページを作成したら、次はページをカスタマイズしてみましょう。このステップでは、リアルタイ ムに変更をプレビューできる方法でページを編集します。 4 Visualforce の概要 作成済みの Visualforce ページを確認する 1. ページのヘッダーに表示される「Congratulations」を変更するために、<h1> タグの内容を「Hello World」に 変更し、コメントと「This is your new page」というテキストを削除します。コードは次のようになります。 <apex:page> <h1>Hello World</h1> </apex:page> 2. ページエディタの上部にある [Save (保存)] ボタンをクリックします。 変更が反映されたページが再読み込みされます。「Hello World」の文字は大きく (太く) 表示されます。これは <h1> タグが標準の HTML タグであるためです。Visualforce ページは通常 2 種類のタグで構成されます。1 つは Visualforce コンポーネントを表すタグ (<apex:page> など) で、もう 1 つは標準の HTML タグです。 ステップ 1 で開発モードを有効にしているため、開発はすばやく簡単に進められるようになっています。変更 も容易で、保存ボタンをクリックすると、ただちにページに変更内容が反映されます。また、キーボードショー トカットの CTRL+S を使用していつでも保存できます。エディタの最小化ボタンをクリックすると、ページ部 分の表示領域を最大化できます。 なお、作成したページを本番環境でリリースしたり、開発モードをオフにしたりすると、エディタは利用でき なくなります。 作成済みの Visualforce ページを確認する Visualforce ページを作成したら、そのページにどこからアクセスできるかを把握しておきましょう。 1. [設定] で、[開発] > [ページ] をクリックします。 2. 画面を下へスクロールすると、ステップ 2 で作成したページhelloがリストされています。 以上の手順で、作成したページを確認できます。ここで、[編集] リンクをクリックしてページを編集すること も可能です。ただし、このページの編集機能は前のステップで使ったページエディタとは異なり、別のタブで ページを開いておかない限り、変更内容をすぐに確認することはできません。 ページエディタ以外の方法でページを作成する この [Visualforce ページ] の画面からでも、ステップ 2 と同様に、新しいページを作成し、編集することが可能で す。手順としては、作成したページの名前を使用して、正しい URL に移動します。実際に試してみましょう。 1. [設定] から、[開発] > [ページ] をクリックした後、[新規] をクリックします。 2. hello2 という名前のページを作成します。 3. [保存] をクリックします。 4. ステップ 2 と同様に URL (https://your-salesforce-instance/apex/hello2) を使用して新しいページ に移動します。 [設定] のVisualforceエディタは、すべてのページを確認できる便利な機能ですが、前のステップで使用した開発 モードのエディタのほうが強力で、適用した変更内容をただちに表示できます。ワークブックの残りのセク ションでは、この開発モードのエディタを使用します。 5 Visualforce の概要 まとめ まとめ このチュートリアルでは、開発モードを有効にする方法、Visualforce ページを作成する方法、作成したページ を確認する方法を学びました。次のチュートリアルでは、ページエディタの機能についてもう少し学習し、 Visualforce ページの構成要素であるコンポーネントの基本を学びます。 属性の追加と入力支援機能の使用 チュートリアル 1 で作成したページでは、先頭と末尾に <apex:page> タグが使用されていますが、これはす べての Visualforce ページに共通する特徴です。<apex:page> は、実際には Visualforce コンポーネントであり、 ページに必ず含める必要があります。すべての Visualforce ページは次のように記述されます。 <apex:page> Your Stuff Here </apex:page> 山かっこの使い方、コンポーネントの開始と終了の指定方法に注意してください。開始タグでは、<apex:page> のように、コンポーネント名を山かっこで囲みます。終了タグでは、</apex:page> のように、コンポーネ ント名の前にスラッシュ (/) を挿入して山かっこで囲みます。作成する Visualforce ページは整形式の XML 文書で ある必要があるため、一部の例外を除き、常にこの規則に従います。一部のコンポーネントには、<apex:detail /> の形式のように自己完結するものがあります (「/」の位置に注意してください)。これは、1 つのタグ内に 開始タグと終了タグが含まれているものと考えてください。 コンポーネントの動作や外観を変更するには、通常、属性を追加します。属性は名前と値のペアで構成され、 開始タグ内に含まれます。たとえば、属性は sidebar="false" のようになります。 入力支援機能を使用して属性を追加する hello ページを使用して、他のことを試してみましょう。このステップでは、<apex:page> コンポーネントの 有効な属性である sidebar 属性を追加します。 1. 次のように、<apex:page> コンポーネントの開始タグ内に sidebar="false" を追加します。 <apex:page sidebar="false"> 2. [Save (保存)] をクリックします。 ページの左側の領域を見ると、サイドバーが削除されています。sidebar 属性により <apex:page> コンポー ネントの動作と外観を変更できました。 6 Visualforce の概要 新しいコンポーネントを追加する 3. 末尾の引用符 (") の直後にカーソルを置き、スペースバーを押すと、<apex:page> コンポーネントに設定 できる属性のリストがポップアップ表示されます。showHeader 属性を選択します。 4. showHeader 属性が自動的にページに追加され、この属性に値を指定する必要があります。false を追加し ます。これで 1 行目が次のように記述されます。 <apex:page sidebar="false" showHeader="false"> 5. [Save (保存)] をクリックします (ショートカットキーの CTRL+S を使用することもできます)。 ページの外観が大きく変わりました。showHeader 属性を false に設定したことで、上部のヘッダーだけで なく、ページのデフォルトのスタイルがすべて削除されています。 開発では、上部のヘッダーがあったほうが便利なので、元に戻しておきましょう。 6. showHeader 属性の値を true に変更します。 7. [Save (保存)] をクリックします。 新しいコンポーネントを追加する これまでのステップでは、ページを作成し、<apex:page> コンポーネントを使用してページの動作を変更し ました。続いて、別のコンポーネントを使用して機能を追加してみましょう。 Visualforce には、複数のコンポーネントがあらかじめ組み込まれていますが、コンポーネントを追加したり独 自に作成したりすることで、このセットを拡張できます。このステップでは、追加コンポーネントの探し方と 利用方法を学びます。 1. ページエディタの [Component Reference (コンポーネントリファファレンス)] リンクをクリックします。使 用可能なコンポーネントをリストしたポップアップウィンドウが表示されます。 7 Visualforce の概要 ネストしたコンポーネントを追加する 2. <apex:pageBlock> をクリックします。[Component Details (コンポーネントの詳細)] タブに、コンポーネン トの機能と、動作を変更するために追加できる属性についての説明が表示されます。 3. [Usage (使用法)] タブをクリックすると、コンポーネントの使用例が表示されます。<apex:pageBlock> コ ンポーネントは、<apex:pageBlockSection> コンポーネントと一緒に使用されることが多いコンポーネ ントです。このコンポーネントについての詳細を参照するには、<apex:pageBlockSection> をクリック します。 通常、必要なコンポーネントはコンポーネントリファレンスから見つけることができます。このリファレ ンスを使えば、主要なコンポーネントの機能を容易に確認できます。多数の属性をとるコンポーネントも ありますが、実際に使用する属性の数はそれほど多くありません。 では、この 2 つのコンポーネントをページに追加してみましょう。手順を簡単に説明します。コード例は 最後に示していますが、その例を見ないで記述してみてください。 4. <apex:page> コンポーネントの内側に、<apex:pageBlock> コンポーネントを追加し、title 属性の値 に「A Block Title」を指定します。 5. <apex:pageBlock> コンポーネント内側に <apex:pageBlockSection> コンポーネントを追加し、title 属性の値に「A Section Title」を指定します。 6. <apex:pageBlockSection> 内に、「I'm three components deep!」などのテキストを追加します (こ れ以外の任意のテキストを追加しても構いません)。 7. [Save (保存)] をクリックします。コードは次のようになります。 <apex:page sidebar="false"> <apex:pageBlock title="A Block Title"> <apex:pageBlockSection title="A Section Title"> I'm three components deep! </apex:pageBlockSection> </apex:pageBlock> </apex:page> ページの表示は次のようになります。 「A Section Title」の横にある小さな三角のボタンをクリックすると、その下のセクションが折りたたまれます。 ネストしたコンポーネントを追加する このステップでは、コンポーネントをさらに追加する方法を紹介しましょう。手順は簡単です。 1. <apex:pageBlockSection> コンポーネントの末尾にカーソルを移動し、<apex:pageBlockSection> コンポーネントをもう 1 つ追加して別のタイトルを入力します。両方の <apex:pageBlockSection> コン ポーネントは、同じ <apex:pageBlock> コンポーネント内に含める必要があります。 8 Visualforce の概要 まとめ 2. [Save (保存)] をクリックして結果を確認します。 <apex:page sidebar="false"> <apex:pageBlock title="A Block Title"> <apex:pageBlockSection title="A Section Title"> I'm three components deep! </apex:pageBlockSection> <apex:pageBlockSection title="A New Section"> This is another section. </apex:pageBlockSection> </apex:pageBlock> </apex:page> ここで、いくつかのコンポーネントのネストの状態を確認してみましょう。<apex:pageBlockSection> の 開始タグと終了タグは、どちらも <apex:pageBlock> コンポーネントの開始タグと終了タグの内側にありま す。最初の <apex:pageBlockSection> は、2 つ目の <apex:pageBlockSection> が開始する前に終了し ています (終了タグ </apex:pageBlockSection> が次の <apex:pageBlockSection> の開始タグの前にあ ります)。Visualforce ページでは、すべてのコンポーネントがこのような方法でネストされます。タグの指定に エラー (終了タグが欠けているなど) があると、エディタに警告メッセージが表示されます。 まとめ このチュートリアルでは、属性を追加して Visualforce コンポーネントの動作と外観を変更する方法、エディタ の入力支援機能を使用する方法、[Component Reference (コンポーネントリファレンス)]を使用して追加コンポー ネントを参照する方法について学びました。また、Visualforce コンポーネントは、多くの場合、相互にネスト して使用されるということについても触れました。 補足 プラットフォームのビジュアルスタイルに合わせてページを構築するために使用できる、その他の Visualforce コンポーネントは次のとおりです。 • <apex:pageBlockButtons>: 標準のユーザインターフェースボタンのようなスタイルが適用されたボタン のセットを指定できます。 • <apex:pageBlockSectionItem> (省略可能): <apex:pageBlockSection> 内の 1 つのデータを表します。 • <apex:tabPanel>、<apex:toolbar>、および <apex:panelGrid>: ページに表示される情報のグループ 化に使用できます。 シンプルな変数と数式の理解 これまで作成した Visualforce ページは静的なものでしたが、Visualforce ページは通常、データベースから取得し たデータを反映する、ログインするユーザに応じて異なるデータを表示するなど、動的な性質を備えていま す。動的な Visualforce ページを作成するには、変数と数式を使用します。 このチュートリアルでは、Visualforce で使用する変数、数式、および数式言語の構文について解説します。変 数には通常、Force.com データベース内のオブジェクトから取得した情報 (Force.com プラットフォームでユーザ が利用できる情報) が格納されます。例としては、ログインユーザ名を取得する変数などがあります。Visualforce 9 Visualforce の概要 グローバル変数 には、ページに機能を追加するための組み込みの数式が豊富に用意されています。このチュートリアルでも、 基本的な数式をいくつか取り上げます。 グローバル変数 Force.com は、ログインしたユーザの情報を User という変数に保持しています。この User 変数の項目にアクセス して値を取得するには、{! $<global variable>.<field name>} という専用の数式言語構文を使用しま す (User 変数以外の変数でも同様)。 1. 作成した Visualforce ページに{! $User.FirstName}という行を追加します。ページに表示するコンテンツ は必ず <apex:page> コンポーネント内 (開始タグと終了タグの間) に含めるように注意してください。 2. [保存] をクリックします。 Visualforce ページは次のようになります。 <apex:page sidebar="false"> {! $User.FirstName} </apex:page> 皆さんは今後、<apex:page> タグ内であらゆるVisualforceマークアップを使用して自由にページを作成できる ようになるでしょう。保存ボタンをクリックするとすぐに編集結果を確認できる Visualforce の機能は、便利に お使いいただけると思います。 {! ... } は、中かっこの内側がすべて動的な要素であり、数式言語で記述されることを示しています。値 は、ユーザがページを表示する実行時に計算され、挿入される必要があります。Visualforce では大文字小文字 は区別されず、{! ... } 構文内のスペースも無視されます。たとえば、{!$USER.firstname}のようにス ペースが混じっていても影響はありません。 ログインユーザの姓と名を表示する場合は、{! $User.FirstName} {! $User.LastName}と入力します。 基本数式 Visualforce では、変数以外の要素も数式言語に含めることができます。また、ユーザが値を操作するための数 式もサポートされます。では、文字列を連結する数式言語の演算子である&を使用してみましょう。 1. Visualforce ページに{! $User.firstname & ' ' & $User.lastname}という行を追加してみてください。 この構文は、グローバル変数である User オブジェクトから Firstname 項目と Lastname 項目を取得し、両者を 空白文字で結合することを示します。ページ上では、「Joe Bloggs」のように表示されます。 通常は、数式ではもう少し高度な機能を使用しますが、常に関数名、かっこのペア、オプション指定のパ ラメータから成る単純な構文をとるという点に変わりはありません。 2. 続いて、次の構文を追加してみてください。 <p> Today's Date is {! TODAY()} </p> <p> Next week it will be {! TODAY() + 7} </p> Visualforce ページ上の表示は次のようになります。 Today's Date is Wed Feb 08 00:00:00 GMT 2012 Next week it will be Wed Feb 15 00:00:00 GMT 2012 10 Visualforce の概要 条件式 <p> タグは段落の区切りを示す標準の HTML タグです。上記の 2 つの文は、このタグによって 1 行ではなく 2 つの独立した行として表されます。TODAY() 関数は、現在の日付を日付のデータ型で返します。時間の 値がすべて 0 として返されている点に注意してください。なお、2 つ目の構文では日付の関数に + 演算子が 追加されています。これは所定の日数の加算として解釈され、現在の日付に 7 日を加えた値が返されます。 3. 関数を別の関数のパラメータとして使用することや、関数に複数のパラメータをとることなども可能です。 さらに、次のようなコードを追加してみてください。 <p>The year today is {! YEAR(TODAY())}</p> <p>Tomorrow will be day number {! DAY(TODAY() + 1)}</p> <p>Let's find a maximum: {! MAX(1,2,3,4,5,6,5,4,3,2,1)} </p> <p>The square root of 49 is {! SQRT(49)}</p> <p>Is it true? {! CONTAINS('salesforce.com', 'force.com')}</p> Visualforce ページ上の表示は次のようになります。 The year today is 2012 Tomorrow will be day number 9 Let's find a maximum: 6 The square root of 49 is 7.0 Is it true? true CONTAINS() 関数は boolean 値 (true または false のいずれか) を返します。2 つのテキストの引数を比較し、 最初の引数に 2 番目の引数が含まれる場合には true を返します。含まれない場合は false を返します。上記 の例では、「force.com」の文字列が「salesforce.com」に含まれているため、true が返されています。 条件式 ここで、数式の値に応じて情報の表示を動的に変更することが必要になるシナリオを考えてみましょう。例と しては、請求書の項目がないときに、空の明細行を表示する代わりに「なし」と表示したい場合や、支払期限 が過ぎたときに、期日を示す代わりに「遅延」と表示したい場合などが考えられます。 Visualforce ページ上では、IF() をはじめとする条件式を使用することで、こうしたシナリオに対応できます。 IF() 式は次の 3 つの引数をとります。 • 1 つ目は、boolean 値 (true または false) です。この例はステップ 2 の CONTAINS() 関数で説明しています。 • 2 つ目は、boolean 値が true のときに返される値です。 • 3 つ目は、boolean 値が false のときに返される値です。 では、ページエディタの編集画面に次のような条件式を追加してみましょう。保存を実行する前に、Visualforce ページ上でどのような表示になるか予想してみてください。 {! IF ( CONTAINS('salesforce.com','force.com'), 'Yep', 'Nah') } {! IF ( DAY(TODAY()) > 14, 'After the 14th', 'On or before the 14th') } 実際のページの表示は次のようになります。 Yep On or before the 14th もちろん、コードが実行される日付によってこの表示は変わります。その月の 14 日を過ぎた場合、これとは 異なるテキストが表示されます。 11 Visualforce の概要 まとめ まとめ Visualforce では、専用の数式言語の構文である {! expression} を使用して、実行時に値を評価する処理を埋 め込むことができます。グローバル変数には、$VariableName という構文を使用してアクセスします。数式 言語を使用すると、文字列、数値、テキスト、日付などの表示を操作したり、条件に応じて異なる処理を実行 したりできます。 補足 • 『数式早見表』は、Visualforce で利用できるさまざまな数式をコンパクトにまとめたガイドです。 • 『Visualforce 開発者ガイド』では、数式の詳細な説明を確認できます。 標準コントローラの使用 Visualforce の MVC (Model–View–Controller: モデル-ビュー-コントローラ) フレームワークでは、表示やスタイルを、 基盤となるデータベースとロジックから簡単に分離することができます。MVC では、ビュー (Visualforce ページ) はコントローラと連動します。Visualforce の場合、コントローラは通常 Apex クラスであり、このクラスがペー ジに機能を追加する役割を果たします。たとえば、コントローラにはボタンをクリックしたときに実行される ロジックが実装されています。また、多くの場合、コントローラはビューに表示するデータを利用可能にする モデル (データベース) とのやり取りも行います。 大部分の Force.com オブジェクトには、デフォルトで、オブジェクトに関連付けられたデータを操作するため の標準コントローラが用意されており、ほとんどの場合、コントローラのコードを自分で記述しなくても済む ようになっています。必要に応じて、標準コントローラを拡張して新しい機能を追加したり、カスタムコント ローラをゼロから作成したりすることも可能です。このチュートリアルでは、標準コントローラについて学び ます。 レコードの ID を取得する Visualforce ページをアプリケーションの他のページに連携させる場合、レコードの ID が自動的に渡されるよう にすることで、Visualforce ページにそのページのデータを表示できます。これまでのチュートリアルで作成し たページは単体で実行されているため、データベースのレコードのデータをページに表示するには、レコード の ID 情報が必要になります。 Developer Edition 環境では、そのまま使用できる、データ格納済みのオブジェクトが多数用意されています。 1. ここでは、取引先オブジェクトを例に、レコードの ID を取得してみましょう。右上のドロップダウンリス トから [セールス] を選択して、セールスアプリケーションに切り替えます。 2. [取引先] タブを選択します。ビュー名が [すべての取引先] になっていることを確認し、[Go!] をクリックし て取引先レコードの一覧を表示します。 12 Visualforce の概要 レコードのデータを取得して表示する 3. 取引先名 [Burlington Textiles] (別の名前を選択してもかまいません) のリンクをクリックすると、その取引先の 詳細情報が次のような画面に表示されます。 ここで、ブラウザのアドレスバーの URL が、「https://<利用中の salesforce インスタンス >.salesforce.com/0018000000MDfn1」のように変わっていることを確認してください。 この URL の末尾にある「0018000000MDfn1」がレコードの ID です。Force.com データベース内のすべてのレコード は一意の ID を持ちます。あるレコードの ID がわかっていてユーザがレコードに対するアクセス権限を持って いる場合は、そのレコードの ID を URL に追加することでレコードにアクセスできます。 「https://<利用中の salesforce インスタンス>.salesforce.com/0018000000MDfn1」へアクセスする と、Force.comは自動的に 0018000000MDfn1 の ID を持つレコードをデータベースから取得し、そのレコードのユー ザインターフェースを作成します。手動でユーザインターフェースを作成する方法については、後続のチュー トリアルで学んでいきます。 レコードのデータを取得して表示する accountDisplayという名前で新しい Visualforce ページを作成して、次のようなコードを記述します。 <apex:page standardController="Account"> <p>Hello {! $User.FirstName}!</p> 13 Visualforce の概要 他の項目を表示する <p>You are viewing the {! account.name} account.</p> </apex:page> 前のチュートリアルで学習した数式構文 {! } が使用されています。$User.FirstName は、グローバル変数 User の Firstname 項目を参照します。今回のコードでは、その他に、次のような新しい要素が使用されていま す。 1. standardController="Account" 属性: この属性は、取引先 (Account) オブジェクトに対して自動作成済み の標準コントローラを使用するよう Visualforce に指示します。これにより、作成する Visualforce ページでこ のコントローラを利用できるようになります。 2. {! account.name} 式: この式では、account 変数の name 項目の値を取得します。account 変数は標準コント ローラによって自動的に使用可能になります (変数の値は標準コントローラの名前に準じます)。 通常、コントローラにはボタンのクリックに応じてデータベースを操作するロジックが実装されています。 standardController 属性を指定することで、自動的に提供される高機能なコントローラをVisualforceページ から使用できるようになります。 取引先オブジェクトの標準コントローラは、ID がページで使用されるタイミングを特定します。ID がページで 使用されると、データベースにクエリを行い、その ID に関連付けられたレコードを取得して account 変数に割 り当てます。このようにして、Visualforce ページで必要なデータを利用できるようになります。 ここで[保存]ボタンをクリックしてみます。自分の名前が表示されますが、取引先名の部分は空になっていま す。これはVisualforceページでどの取引先レコードを取得するかを指定していないためです。URL を編集して、 ステップ 1 で確認したレコード ID を追加しましょう。現在の URL は次のようになっています。 https://na3.salesforce.com/apex/accountDisplay これを変更して、次のようにレコードの ID を追加します。 https://na3.salesforce.com/apex/accountDisplay?id=0018000000MDfn1 「0018000000MDfn1」の部分は、ステップ 1 で確認した ID に置き換えてください。また、「na3」の部分には、 現在利用中の salesforce インスタンス名が入ります。 保存ボタンをクリックすると、次のように取引先名が表示されます。 他の項目を表示する 現在、accountDisplay ページには、取引先オブジェクトの [名前] 項目しか表示されていません。その他の 項目も表示してみましょう。[設定] から、[カスタマイズ] > [取引先] > [項目] の順にクリックし、オブジェクト の項目リストを表示します。[株式コード] という項目をクリックして詳細を確認します。Visualforce ページで は、[項目名] の値を使用して項目を表示します。ここでは、この [項目名] の値が「TickerSymbol」となっていま す。 以上の情報を使用して accountDisplay ページを編集します。現在のコードに次の行を追加します。 <p>Here's the Ticker Symbol field: {! account.TickerSymbol}</p> 14 Visualforce の概要 リレーションが設定されたレコードの項目を表示する リレーションが設定されたレコードの項目を表示する 続いて、リレーションが設定されたレコードのデータをページに表示できるようにしてみましょう。取引先レ コードの詳細ページに [取引先 所有者] という項目があります。ステップ 3 で参照した取引先オブジェクトの項 目のリストを確認すると、この項目のデータ型は [参照関係 (ユーザ)] となっています。これは、この項目が ユーザレコードとリレーションを持つことを示しています。続いて、[取引先所有者] 項目表示ラベルのリンク をクリックして [項目名] を確認します。値は「Owner」です。 Owner はユーザとリレーションを持っています。[カスタマイズ] > [ユーザ] > [項目] の順にクリックして、ユー ザオブジェクトの項目のリストを表示します。[名前] という項目があり、[項目名] の値が「Name」であること も確認します。この情報を使用して表示してみましょう。 1. 以上の情報を使用して accountDisplay ページを編集します。現在のコードに次の行を追加します。 <p>Here's the owner of this account: {! account.Owner.Name}</p> account.Owner.Nameのドット (.) 表記は、各レコードが参照関係にあることを示しています。account.Owner は、取引先レコードの [取引先 所有者] 項目を示します。「Owner」の後に「Name」が続いているのは、Owner 項目が単純に文字列を格納する項目ではなく、別のレコードを参照して値を取得する項目であるためです (こ れは、先ほど確認したデータ型 [参照関係 (ユーザ)] に対応します)。accountDisplay ページが [取引先 所有者] 項目 の値を表示するには、その項目の参照先となるユーザレコードの [名前] 項目の値を参照することが必要です。 ヒント: 取引先オブジェクトなどの標準オブジェクトではなく、独自に作成したカスタムオブジェクトの 項目を参照する場合は、上記とは異なる手順を実行する必要があります。[設定] から、[作成] > [オブジェ クト] の順にクリックして対象となるカスタムオブジェクトを選択し、項目の詳細を確認します。カスタ ムオブジェクトの場合、Visualforce ページでの設定で使用する項目の名前は、[API 参照名] 項目に表示され ます。たとえば、「Foo」という項目の API 参照名が「Foo__c」である場合、コード内で「{! myobject__c.foo__c}」のように指定を行えば、項目を参照できます。 まとめ 標準コントローラでは、特別な設定を行うことなく、レコードの自動表示をはじめとするコントローラの基本 機能を使用できます。このチュートリアルでは、レコードの ID の確認方法、標準コントローラを使用したレ コードデータの表示方法などを学びました。標準コントローラは、レコードの保存、更新といったさまざまな 機能を備えています。これらについては、後続のチュートリアルで説明します。 補足 Visualforce は、標準リストコントローラもサポートしています。このコントローラでは、ページ送り機能を含 む Visualforce ページによってレコードのリストを扱うことができます。 標準のユーザインターフェースコンポーネントの使用 「属性の追加と入力支援機能の使用」では、<apex:pageBlockSection> コンポーネントについて学び、 チュートリアル 4 では、数式言語を使用して取引先レコードのデータを表示する方法を学習しました。この チュートリアルでは、新たなVisualforceコンポーネントを追加して、外観や動作がデフォルトのユーザインター フェースに類似したインターフェースを作成します。 15 Visualforce の概要 特定のレコードに関する詳細と関連リストを表示する 特定のレコードに関する詳細と関連リストを表示する 特定のレコードに関する詳細は、1 つのコンポーネントを記述するだけで簡単に取得できます。 1. accountDisplay Visualforce ページを編集して、次のコードを記述します。 <apex:page standardController="Account"> <apex:detail/> </apex:page> 前のチュートリアルで説明した手順に基づいて、有効なレコード ID をパラメータに追加した URL を指定し ます (例: https://na3.salesforce.com/apex/AccountDisplay?id=0018000000MDfn1)。すると、 ページにレコードの詳細情報が表示されます。 2. <apex:detail/> コンポーネントは、レコードの標準の参照用ページを表示します。ここには、取引先責 任者などの関連リストも含まれます。こうした関連リストを非表示にするには、<apex:detail/> に relatedList="false" 属性を追加します。追加後、[保存] をクリックして、ページの表示がどのように 変わるか確認してみてください。 3. 特定の関連リストのみを表示することもできます。たとえば、表示中の取引先レコードに関連付けられた ケースレコードの関連リストのみを表示する場合は、次のようなタグを追加します。 <apex:relatedList list="Cases" /> これらのタグは非常にシンプルなものですが、標準コントローラを使用してデータにアクセスしたり、データ を取得したりする場合に、さまざまな処理を実行できます。ただし、ページレイアウトの細かな調整が追加で 必要になることもあります。 表示する項目を指定する <apex:outputField> コンポーネントを使用すると、レコードでどの項目の値を表示するかを個別に指定で きます。このコンポーネントを <apex:pageBlock> コンポーネントにネストすると、項目の値と一緒に項目 名も表示されるようになります。 1. 次のコードを追加して、ページ上の表示を確認します。 <apex:pageBlock title="Custom Output"> <apex:pageBlockSection title="Custom Section Title"> <apex:outputField value="{!account.Name}"/> <apex:outputField value="{!account.Owner.Name}"/> </apex:pageBlockSection> </apex:pageBlock> 16 Visualforce の概要 テーブルを表示する account.Name では、現在の取引先レコードの [取引先名] 項目の値を取得します。一方、 account.Owner.Name では、取引先レコードの [取引先 所有者] 項目を参照し、そのレコードのユーザ名 を取得します。 テーブルを表示する これまでのステップでは、特定の項目の 1 件のレコードの情報をそのまま取得していました。では、多数のレ コードにまたがって複数の項目の情報を取得する場合にはどうすればよいでしょう。今回のステップでは、 「取引先に関連付けられた取引先責任者のリストを表示する」というシナリオを使用して、その手順を紹介し ます。取引先責任者のリストを反復処理してレコードを個別に表示するというやり方で、1 件の取引先レコー ドに関連付けられた複数の取引先責任者レコードのリストを作成します。複数のタグがネストされるため最初 は複雑に感じるかもしれませんが、すぐにマスターできるでしょう。各コンポーネントの詳細を確認するに は、[Component Reference (コンポーネントリファファレンス)] リンクをクリックしてください。 1. ページエディタで <apex:pageBlock> コンポーネントを追加します。 <apex:pageBlock title="My Account Contacts"> </apex:pageBlock> 2. この時点で結果を確認する場合は、保存ボタンをクリックしてください。続いて、このコンポーネントの 内側に <apex:pageBlockTable> コンポーネントをネストします。 <apex:pageBlockTable value="{! account.contacts}" var="item"> </apex:pageBlockTable> このコンポーネントは、value 属性をチェックし、指定されているレコードのリストを取得します。今回 は、取引先レコードと、その取引先レコードに関連付けられた取引先責任者レコードのリストを表す contacts 項目を取得します。次に、個々の取引先責任者レコードを、item という変数に割り当てます。 この処理が、リストの最後のレコードに到達するまで反復されます。ここでは、反復処理の結果が出力さ れるコンポーネントの本文が重要になります。その出力結果により、各取引先レコードに対して任意の処 理を効率よく行うことが可能になります。 3. では、<apex:pageBlockTable> コンポーネントの内側に、取得したアイテムに対する処理を行うコン ポーネントを挿入してみましょう。次のコードを追加します。 <apex:column value="{! item.name}"/> <apex:column> は、テーブルに新しい列を追加するコンポーネントです。このコンポーネントは、項目 の名前に基づくヘッダーを自動的に作成し、項目の値をテーブルの行に挿入します (レコード 1 件につき 1 行)。ここでは、{! item.name}, を指定することにより、各取引先責任者の [名前 (Name)] 項目を表示しま す。 17 Visualforce の概要 まとめ 最終的なコードは次のようになります。 <apex:pageBlock title="My Account Contacts"> <apex:pageBlockTable value="{! account.contacts}" var="item"> <apex:column value="{! item.name}"/> </apex:pageBlockTable> </apex:pageBlock> 取引先責任者レコードには、[電話 (Phone)] という項目もあります。電話番号を表示する列も追加してみてくだ さい。指定した取引先レコードに関連付けられた取引先責任者レコードが存在しない場合や、取引先レコード の ID を指定していない場合は、何も表示されません。 まとめ <apex:detail> コンポーネントと <apex:relatedList> コンポーネントを使用すると、標準コントローラ を利用して特定のレコードのデータを自動で取得し、そのレコードと関連リストを簡単に表示できます。 <apex:pageBlockTable> では、レコードのリストに対して反復処理を実行することで、リストに含まれる レコードの出力を生成できます。 補足 • <apex:facet> コンポーネントを使用すると、テーブルのキャプション、ヘッダー、フッターをカスタマ イズできます。 • <apex:enhancedList> コンポーネントと <apex:listViews> コンポーネントを使用すると、オブジェク トのレコードの標準のリストビューを埋め込むことが可能になります。 Ajax による Visualforce ページの更新 Visualforce では、複雑な JavaScript ロジックを実装せずに Ajax 機能を使用して、ページの部分的な更新などを実 行できます。これを行うには、まず動的に更新する必要のある領域を明示的に指定し、その後、rerender 属 性を使用してページ内で該当領域の表示を動的に更新します。 動的に更新する領域を明示的に指定する Visualforceで Ajax を使用する場合の標準的なテクニックとして、動的に更新する領域をグループ化して明示的に 指定するという方法があります。このときによく使用されるのが <apex:outputPanel> コンポーネントと、 領域を識別する id 属性です。 18 Visualforce の概要 動的な再表示を追加する 1. 「Dynamic」という名前で新しい Visualforce ページを作成し、次のコードを記述します。 <apex:page standardController="Account"> <apex:pageBlock title="{!account.name}"> <apex:outputPanel id="contactDetails"> <apex:detail subject="{!$CurrentPage.parameters.cid}" relatedList="false" title="false"/> </apex:outputPanel> </apex:pageBlock> </apex:page> 2. この Visualforce ページの URL に、有効な取引先レコードの ID を渡します。 ページには、取引先の名前以外には何も表示されません。<apex:outputPanel> には、id 属性として 「contactDetails」という値が指定されています。<apex:detail> コンポーネントには、subject 属性が 指定されています。この属性では、詳細を表示するレコードの ID を取得します。属性に続いて指定されてい る式 {! $CurrentPage.parameters.cid} は、ページに渡される cid パラメータを返します。ただし、今 回はそうしたパラメータをまだ渡していないため、何も表示されません。 動的な再表示を追加する このステップでは、パラメータを設定する要素をページに追加し、detail という名前を付けた領域を動的に 表示します。 1. 前のステップで作成したコードの <pageBlock> コンポーネントの下に、次の <pageBlock> コンポーネントを 追加します。 <apex:pageBlock title="Contacts"> <apex:form> <apex:dataList value="{! account.Contacts}" var="contact"> {! contact.Name} </apex:dataList> </apex:form> </apex:pageBlock> このコードは、この取引先レコードに関連付けられた取引先責任者レコードのリストを反復処理し、各取 引先責任者の名前のリストを作成します。 2. [保存] をクリックします。 ページには、取引先責任者のリストが表示されます。次に、各取引先責任者の名前をクリックできるよう にします。 3. {! contact.Name} 式を編集します。次のようにして、<apex:commandLink> コンポーネントで式を囲 みます。 <apex:commandLink rerender="contactDetails"> {! contact.Name} <apex:param name="cid" value="{! contact.id}"/> </apex:commandLink> この <apex:commandLink> コンポーネントには、2 つの重要な役割があります。1 つは、 rerender="contactDetails" 属性を使用して、前のステップの <apex:outputPanel> コンポーネントで指定し 19 Visualforce の概要 まとめ た領域を参照することです。これにより、取引先責任者の名前がクリックされたときに、該当の領域に対する 部分的なページ更新を行うよう Visualforce に指示できます。もう 1 つは、<apex:param> コンポーネントを使 用してパラメータ (ここでは、取引先責任者レコードの id) を渡すことです。 ページ上の任意の取引先責任者の名前の上でクリックしてみましょう。ページ全体が更新される代わりに、ク リックした領域のみが動的に更新され、取引先責任者レコードの詳細が表示されます。 まとめ Visualforceは、ページを部分的に更新する Ajax の機能をネイティブでサポートしています。ポイントは領域を明 示的に指定することと、rerender 属性を使用してその領域の動的な更新を指示することです。 補足 Visualforce では、上記のほかにも、Ajax や JavaScript をサポートするコンポーネントを複数提供しています。 • <apex:actionStatus> では、Ajax 要求の状況を表示できます。進行中であるか完了しているかに応じて、 異なる値が表示されます。 • <apex:actionSupport> では、任意のコンポーネントに対して Ajax アクションをトリガするユーザの操作 を指定できます。たとえば、<apex:commandLink> コンポーネントをクリックしなくても、ラベルにマウ スを合わせたときのシンプルなロールオーバーによって Ajax アクションをトリガするように設定できます。 • <apex:actionPoller> では、指定された間隔に従って、Force.com に対して Ajax の更新要求を送信するタ イマーを設定できます。 • <apex:actionFunction> では、Ajax 要求を使用して、JavaScript コードからコントローラのアクションメ ソッドを直接呼び出せるようにします。 • <apex:actionRegion> では、Ajax 要求の生成時に Force.com が処理するコンポーネントの領域を明示的に 指定できます。 ページの上書きと参照 Visualforce では、ボタン、タブ、リンクなどのさまざまなユーザインターフェースを上書きできます。 このチュートリアルでは、作成した Visualforce ページで標準の Salesforce ユーザインターフェースの動作を変更 する方法を説明します。 ページのデフォルトの表示を上書きする 「標準コントローラの使用」で作成した Visualforce ページを使用して、取引先レコードの標準の詳細ページを 上書きしてみます。Salesforce のデフォルトのページの代わりに、今回作成した Visualforce ページが表示される ようにしてみましょう。 1. [設定] から、[カスタマイズ] > [取引先] > [ボタン、リンク、およびアクション] をクリックします。 2. [参照] 項目の横にある [編集] をクリックします。 3. [上書き手段] で、[Visualforce ページ] を選択します。 4. [Visualforce ページ] ドロップダウンリストから、[accountDisplay] を選択します。 20 Visualforce の概要 標準のページレイアウトに Visualforce ページを埋め込む 5. [保存] をクリックします。 結果を確認します。[取引先] タブに移動して任意の取引先レコードをクリックします。デフォルトのペー ジの代わりに、作成したページが表示されます。ID パラメータを自動的にページに渡すためのプラット フォームの設定はこれで完了です。 6. 次のステップでデフォルトページを表示できるように、同じ手順を実行して上書きしたページをデフォル トに戻しておきます。 標準のページレイアウトに Visualforce ページを埋め込む 作成した Visualforce ページを表示するもう 1 つの方法は、他のページの標準のページレイアウト内に Visualforce ページを埋め込むことです。たとえば、取引先に関する興味深い分析データが含まれる accountDisplay ページを取引先レコードの詳細ページに埋め込んで表示したい場合には、次の手順を実行します。 1. [設定] から、[カスタマイズ] > [取引先] > [ページレイアウト] をクリックします。 2. [Account Layout (取引先レイアウト)] の横にある [編集] リンクをクリックします。 3. 上部のユーザインターフェース要素のパレットの左列で、[Visualforce ページ] を選択します。 4. 右側に、[accountDisplay] ページが表示されます (取引先の標準コントローラを用いてページが表示されます)。 5. [accountDisplay] を [取引先情報] パネルへドラッグアンドドロップします。 6. [保存] をクリックします。 7. 結果を確認します。[取引先] タブに移動して任意の取引先レコードをクリックします。デフォルトのペー ジ内に Visualforce ページが埋め込まれています。単に追加しただけなのでレイアウト的には改善が必要かも しれませんが、今回は、Visualforce ページにデフォルトのページのレコードに対応するデータが自動的に表 示されていること、ID パラメータが渡されていることを確認できれば問題ありません。 Visualforce ページにリンクするボタンを作成する これまでのステップで取り上げた取引先レコードの詳細ページのような標準のページには、[編集]、[削除] な どのボタンが組み込まれています。このステップでは、作成した Visualforce ページを表示する新しいボタンを 標準のページに追加してみましょう。 1. [設定] から、[カスタマイズ] > [取引先] > [ボタン、リンク、およびアクション] をクリックします。 21 Visualforce の概要 Visualforce ページから他の Visualforce ページや外部 URL を参照するハイパーリンクを作成する 2. [新規ボタンまたはリンク] をクリックします。 3. [表示ラベル] に「MyButton」と入力します。 4. [名前] に「My_Button」と入力します。 5. [表示の種類] で [詳細ページボタン] を選択します。 6. [内容のソース] 選択リストで [Visualforce ページ] を選択します。 7. 表示された [コンテンツ] 選択リストで accountDisplay ページを選択します。 8. [保存] をクリックします。 9. 新しいカスタムボタンを表示するにはページレイアウトに追加を行う必要があるというメッセージが表示 されたら、[OK] をクリックして確定し、ページレイアウトへの追加を実行します。「標準のページレイア ウトに Visualforce ページを埋め込む」の手順を繰り返しますが、ここでは [Visualforce ページ] を選択する代わ りに、ボタンを追加して [MyButton] を選択します。 メモ: ブラウザの設定によっては、プライバシーに関する警告が表示されることがあります。この警 告が表示されないようにするには、ブラウザでVisualforceドメインからページを読み込むことができる ように設定します。 同様の手順を使用して、ボタンの代わりにリンクを作成できます。さらに、多数のボタンやリンクを標準ペー ジとカスタムページに追加して、アプリケーションに適切なナビゲーションとユーザインターフェースを作成 することもできます。 Visualforce ページから他の Visualforce ページや外部 URL を参照する ハイパーリンクを作成する Visualforce ページから他の Visualforce ページや外部 URL を参照するハイパーリンクを作成するには、次の手順を 実行します。 22 Visualforce の概要 まとめ 1. ハイパーリンクを作成するには、Visualforceページのページエディタで <apex:outputlink> コンポーネン トを追加します。 <apex:outputLink value="http://developer.salesforce.com/">Click me!</apex:outputLink> 2. ページを参照する場合は、ページの URL を特定する式 {! $Page.pagename} を使用します。 3. たとえば、accountDisplay ページを参照する場合は、次のように指定します。 <apex:outputLink value="{! $Page.AccountDisplay}">I am me!</apex:outputLink> $Page は、作成したあらゆるページの任意の項目を格納するグローバルオブジェクトとみなすことができま す。 まとめ 作成した Visualforce ページは、さまざまな方法で表示できます。単にその URL を指定して表示する以外に、標 準のページと置き換える、標準のページのレイアウト内に埋め込む、Visualforce ページを表示するボタンやリ ンクを埋め込むといった方法があります。 補足 • Force.com のサイト機能を利用して Visualforce ページを公開 Web サイトに表示することもできます。例につい ては、「Force.com ワークブック」を参照してください。 • 取引先レコードの新規作成といったデフォルトの動作にリンクを埋め込むには、<apex:outputLink> コ ンポーネント、URLFOR() 関数、グローバル変数 $Action を使用します。次に例を示します。 <apex:outputLink value="{! URLFOR($Action.Account.new)}">Create</apex:outputLink> フォームを使用したデータの入力 このチュートリアルでは、標準コントローラを使用して、レコードの保存や更新が可能な入力画面をデフォル トで提供する方法を学びます。また、主な入力機能とそのコンテナである <apex:form> コンポーネントにつ いても解説します。なお、「カスタムコントローラの作成と使用」では、ここで取り上げる内容をさらに発展 させた、独自のコントローラと連動するフォームの作成方法について説明しています。 基本となるフォームを作成する フォームは、データ入力にとって重要なポイントです。このステップでは、もっとも基本的なフォームを作成 します。 1. MyFormという名前で新しい Visualforce ページを作成し、取引先オブジェクトの標準コントローラを使用し ます。 <apex:page standardController="Account"> </apex:page> 23 Visualforce の概要 項目表示ラベルを表示する 2. <apex:form> コンポーネントを挿入します。入力項目はすべてこのコンポーネント内に記述します。 <apex:form> </apex:form> 3. このフォーム内に、取引先の名前を入力する項目と、クリック操作に基づいてフォームの保存を実行する ボタンを追加します。 <apex:inputField value="{! account.name}"/> <apex:commandButton action="{! save}" value="Save!"/> このフォームはきわめて基本的なものですが、フォーム、入力項目、フォームを処理するボタン、という重要 な要素がすべて含まれています。 この場合は、ボタンを生成する <apex:commandButton> コンポーネントを使用します。アクション要素は、 {! save} にバインドされています。この式言語による構文は、レコード内の項目を指定するために使用した 構文に似ていますが、この利用方法では、「save」というコードで表されるメソッドを参照します。すべての 標準コントローラは、デフォルトで save() メソッド、および update() メソッドを提供します。上のコー ドでは、ボタンをクリックすることにより標準コントローラの save() メソッドが呼び出されます。 値を入力して [Save (保存)] をクリックすると、入力した項目の値が、新規レコード内の対応する名前を持つ項 目の値にバインドされ、そのレコードが挿入されます。つまり、<apex:inputField> コンポーネントによ り、新しい取引先レコードの名前項目に対応する入力項目が作成され、[Save (保存)] をクリックすると、入力 した値がその項目に自動的に保存されます。 [Save (保存)] をクリックすると、新規作成されたレコードが表示されます。Visualforce ページの URL (https://na6.salesforce.com/apex/MyForm など) を入力して、先ほどのページに戻ります。 項目表示ラベルを表示する Visualforce は、入力項目を新規レコードの項目にバインドしながら、バックグラウンドでさまざまな処理を行 います。項目表示ラベルを自動で表示する (「標準のユーザインターフェースコンポーネントの使用」で説明 した <apex:outputField> と同様の動作)、データ型が一致するように入力要素を自動で変更する (たとえ ば、入力ボックスの代わりに選択リストを表示するなど) といった処理が可能です。 <apex:form> 要素の内容を、次のように変更します。 <apex:form> <apex:pageBlock> <apex:pageBlockSection> <apex:inputField value="{!account.name}"/> <apex:inputField value="{!account.industry}"/> <apex:commandButton action="{!save}" value="Save!"/> </apex:pageBlockSection> </apex:pageBlock> </apex:form> 24 Visualforce の概要 警告やエラーメッセージを表示する <apex:pageBlock> コンポーネントと <apex:pageBlockSection> コンポーネントの内部に入力項目をカ プセル化すると、Visualforceでは、項目表示ラベル (「取引先名」と「業種」)、および値の入力が必須かどうか を示すインジケータが、すべてプラットフォームのスタイルを使用して自動的に挿入されます。 警告やエラーメッセージを表示する <apex:pageMessages> コンポーネントを使用すると、現在のページを構成するすべてのコンポーネントに ついて生成された、通知、警告、エラーなどのメッセージがすべて表示されます。前のステップで作成した フォームでは、[取引先名] は必須項目でした。ユーザが取引先名を指定せずにフォームを送信しようとした場 合に標準的なエラーメッセージを表示する手順は、次のとおりです。 1. <apex:pageBlock> タグの後に次の行を挿入して、ページを更新します。 <apex:pageMessages/> 2. フォームの [Save (保存)] をクリックします。次のようなエラーパネルが表示されます。 まとめ Visualforceの標準コントローラには、レコードの保存や更新を簡単に実行できるメソッドが用意されています。 <apex:form> コンポーネントと <apex:inputField> コンポーネントを組み合わせて使用すると、標準コン トローラを利用して入力項目を新しいレコードにバインドできます。ユーザインターフェースでは、項目の データ型に対応する入力形式が自動的に適用されます (たとえば、日付データ型の項目では、カレンダーを使 用して日付を指定します)。ページ内のすべてのコンポーネントを対象に、通知、警告、エラーなどのメッセー ジを表示するには <apex:pageMessages> コンポーネントを使用します。 補足 • フォーム内で <apex:commandButton> の代わりに、<apex:commandLink> コンポーネントを指定する と、フォームの処理に使用するリンクを作成できます。 • save() メソッドの代わりに quicksave() メソッドを使用すると、新規レコードのページを表示せずにレ コードを挿入したり、既存のレコードを更新したりできます。 • <apex:pageBlock> コンポーネントと共に <apex:pageBlockButtons> コンポーネントを使用すると、 コマンドボタンをページに追加できます。 25 Visualforce の概要 テンプレートを使用したページの再利用 • <apex:pageMessage> コンポーネントでは、カスタムメッセージを作成できます (複数形の <apex:pageMessages> コンポーネントとは異なりますので注意してください)。 テンプレートを使用したページの再利用 多くの Web サイトには、バナーやサイドバーなど、すべてのページに共通して表示されるデザイン要素があ ります。Visualforceでは、基盤となるテンプレートを作成することで、Web ページと同じように、多様なコンテ ンツを含むVisualforceページに標準の構成を適用することが可能です。各ページでは、テンプレート内のプレー スホルダをさまざまなコンテンツで置き換えることができます。 テンプレートの作成 テンプレートは、プレースホルダのテキストの挿入位置を指定する特殊なタグを含む Visualforce ページです。 このステップでは、シンプルなテンプレートを作成します。挿入位置を指定するタグとして、<apex:insert> コンポーネントを使用します。 1. BasicTemplate という新しい Visualforce ページを作成します。 2. ページに次のコードを入力します。 <apex:page> <h1>My Fancy Site</h1> <apex:insert name="body"/> </apex:page> ここでのポイントは、<apex:insert> コンポーネントです。テンプレートは直接アクセスして使用するもの ではなく (そのように設定することも可能ですが)、複数の Visualforce ページにテンプレートを埋め込み、各 Visualforce ページで <apex:insert> コンポーネントにさまざまな値を挿入する、という形で運用します。 <apex:insert> コンポーネントを使用する場合は、必ず name 属性を指定します。上記のコードでは、body とい う 1 つの挿入位置のみが指定されていますが、挿入位置を複数指定することも可能です。 他のページでテンプレートを使用する このステップでは、新しいページにテンプレートを埋め込み、1 つずつ値を入力していきます。 1. MainPage という新しい Visualforce ページを作成します。 2. ページエディタで、コードを以下のように変更します。 <apex:page sidebar="false" showHeader="false"> <apex:composition template="BasicTemplate"> <apex:define name="body"> <p>This is a simple page demonstrating that this text is substituted, and that a banner is created.</p> </apex:define> </apex:composition> </apex:page> <apex:composition> コンポーネントは、前のステップで作成した Visualforce テンプレートを取得し、 <apex:define> コンポーネントは、テンプレートのプレースホルダに値を入力します。同様の手順で複数の 26 Visualforce の概要 Visualforce ページを他の Visualforce ページに挿入する ページを作成できます。その場合は、コンポーネントは同じものを使用し、プレースホルダ内のテキストをそ の都度変更するようにします。 Visualforce ページを他の Visualforce ページに挿入する あるページのコンテンツを他のページに適用するもう 1 つの方法として、<apex:include> コンポーネント を使用する方法があります。テンプレートを使用する場合とは異なり、元のページのコンテンツはそのまま複 製され、変更を加えることはできません。 1. EmbedsAnother という新しい Visualforce ページを作成します。 2. ページエディタで、コードを以下のように変更します。 <apex:page sidebar="false" showHeader="false"> <p>Test Before</p> <apex:include pageName="MainPage"/> <p>Test After</p> </apex:page> 元の MainPage ページがそのまま挿入されます。 まとめ テンプレートは、複数の Visualforce ページで共通に使用する必要のあるページ要素をカプセル化するのに適し た方法です。Visualforceページで記述する必要があるのは、テンプレートを埋め込み、テンプレート内のプレー スホルダのコンテンツを定義する処理のみです。<apex:include> コンポーネントを使用して他のページに ページを挿入する方法はさらに簡単です。 27 Apex の概要 Force.comApexは、強く型付けされたオブジェクト指向プログラミング言語で、Force.comプラットフォーム上で 実行するコードの記述に使用します。Force.comでは、Web サービス、コード実行のスケジュール、一括処理、 トリガ、そして Visualforce のバックエンドロジックなど、高度なサービスを標準で提供します。このすべてを Apex で記述する必要があります。 このセクションでは、一定の設定を行った後、ごく簡単な Apex を記述しながらこのツールについて説明しま す。続いて、Apex の難度の高い内容を学習した後、最初の「本格的」な Apex クラスを作成します。組織に追 加するコードにはカスタム機能があり、次のセクションでは、この機能を使用して Salesforce1 にリリースする モバイル対応の Visualforce ページを作成します。 このセクションは、プログラミングに関するごく基本的な知識がある方を対象としています。前提知識がなく ても練習問題に取り組むことはできますが、難しい部分もあるかもしれません (が、問題ありません)。 このセクションでは、次のことを行います。 • Force.com の高度な開発ツールである開発者コンソールを開いて、Apex コードを作成、編集、および実行す る。 • [Execute Anonymous Apex (匿名 Apex コードを実行)] ウィンドウで、Apex コードスニペットを実行する (「匿名 Apex」の意味もわかるようになります)。 • Apex のクラスやメソッドを作成する。 • Apex と他のプログラミング言語 (Java、C#、PHP など) の類似点や相違点を認識する。 • Apex の SOQL クエリを実行し、その結果を処理する。 • Apexコードの正しい動作を検証するテストを作成して実行し、コードカバー率やその確認方法を理解する。 開発環境を設定する この短いレッスンでは、この後の練習問題用に Develope Edition 組織を準備します。補助リソースを含むパッ ケージをインストールし、Salesforce1 ブラウザテスト環境を読み込み、Salesforce1 モバイルアプリケーションを 任意のモバイルデバイスにインストールします。 拡張 Warehouse データモデルをインストールする このセクションと次のセクションの練習問題用に Developer Edition 組織を準備するには、Warehouse データモデ ルとサンプルデータをインポートする必要があります。 他のワークブックやハンズオンワークショップのチュートリアルの経験があれば、Warehouse アプリケーショ ンの理解に役立つでしょう。ここで使用する Warehouse アプリケーションは、拡張バージョンで、追加のカス タムオブジェクトとデータ、および補助コードが含まれています。 1. ブラウザで http://bit.ly/warehouse_schema11 にアクセスします。 2. すでにログインしている場合、[パッケージインストールの詳細] ページにリダイレクトされます。ログイ ンしていない場合は、Developer Edition のログイン情報を使用してログインします。 28 Apex の概要 モバイルブラウザ Web アプリケーションにアクセスす る 3. [次へ]、[次へ]、[次へ]、[インストール] の順にクリックします。 4. インストールが終了したら、Force.com アプリケーションメニューをクリックして [Warehouse (倉庫)] を選択 します。 5. [データ] タブをクリックし、次に [データを作成] ボタンをクリックします。 パッケージには、あらかじめ用意されている Visualforce ページといくつかのサポートリソースが含まれます。 これらについては、開発環境とテスト環境を設定した後すぐに学習します。 モバイルブラウザ Web アプリケーションにアクセスする Salesforce1 モバイルアプリケーションの Visualforce ページを開発する場合、使い慣れた https://<instance>/apex/<pageName> URL を使用してページを表示するのではなく、ページの外観はモ バイルアプリケーションで確認します。 ページをテストするのに最適な方法は、現実性のある操作が可能な、実際のモバイルアプリケーションを使用 することです。ただし、変更を確認するために毎回携帯端末を使用するのは手間がかかるので、新しいブラウ ザタブを開いて、one.app モバイルブラウザバージョンを使用することができます。 1. ブラウザで、新しいタブを開きます。 2. Salesforce インスタンスのホーム URL をコピーして、新しいタブのアドレスバーに貼り付け、末尾に /one/one.app を追加します。 たとえば、Salesforce インスタンスの URL が https://na4.salesforce.com の場合、 https://na4.salesforce.com/one/one.app を使用します。 3. Enter キーを押して編集した URL を読み込みます。 これで、Saleforce1 のモバイルブラウザバージョンが表示されます。このワークブックの練習問題を進めなが ら、一方のタブで開発を行い、もう一方のタブでテストできます。 重要: /one/one.app バージョンは、開発には便利ですが、サポートする予定の実際のデバイスとブラウ ザで必ずテストを行ってください。 Salesforce1 モバイルアプリケーションをダウンロードする 完成間近のアプリケーションの最終テストのために、Salesforce1 モバイルアプリケーションをデバイスにイン ストールする必要があります。 Salesforce1モバイルアプリケーションをすでにダウンロードしてある場合、このステップはスキップできます。 1. モバイルデバイスのブラウザを使用して www.salesforce.com/mobile に移動し、該当のプラットフォー ムを選択して Salesforce1 をダウンロードします。 2. モバイルデバイスから Salesforce1 を開きます。 3. Salesforce ログイン情報を入力し、[Salesforce へのログイン] をタップします。 4. データへのアクセスを許可するように要求された場合、[OK] をタップして続行します。 29 Apex の概要 開発者コンソールの使用 まだ Salesforce1 を操作したことがなくても、この機会に体験することができます。Salesforce1 の機能に慣れてお くと、その内部で適切に動作するアプリケーションを開発するうえで役立ちます。 開発者コンソールの使用 開発者コンソールでは、Apex コードステートメントを実行できます。また、Apex クラスまたはオブジェクト 内の Apex メソッドを実行することもできます。このチュートリアルでは、開発者コンソールを開き、基本的 な Apex ステートメントをいくつか実行し、ログ設定を切り替えます。 開発者コンソールの有効化 Salesforce 環境にログインすると、画面には現在使用しているアプリケーション (下図では Warehouse) とユーザ の名前が表示されます。 1. あなたの名前 > [開発者コンソール] をクリックします。 開発者コンソールは別のウィンドウで開きます。 メモ: [Developer Console (開発者コンソール)] オプションが表示されない場合、使用している Force.com 環 境の種別が適切ではない可能性があります。詳細は、このワークブックの冒頭にある「ご利用になる 前に」を参照してください。 2. 開発者コンソールを初めて開く場合、開発者コンソールの機能を紹介するツアーを見ることができます。 [ツアーの開始] をクリックすると、開発者コンソールに関する説明が表示されます。 開発者コンソールはいつでも開くことができます。 開発者コンソールを使用した Apex コードの実行 開発者コンソールの外観には圧倒されるかもしれませんが、これはコード操作に役立つツールのコレクション にすぎません。このレッスンでは、Apexコードを実行して、ログインスペクタに結果を表示します。ログイン スペクタは、頻繁に使用する便利なツールです。 1. [Debug (デバッグ)] > [Open Execute Anonymous Window (実行匿名ウィンドウを開く)] をクリックするか、Ctrl キーを押しながら E キーを押します。 2. [Enter Apex Code (Apex コードを入力)] ウィンドウに、テキスト「System.debug( 'Hello World' );」と 入力します。 メモ: System.debug() は、Java で System.out.println() (または printf()) を使用するのに似て います。ただし、クラウドでコーディングする場合、出力方法が異なります。次のとおりです。 30 Apex の概要 開発者コンソールを使用した Apex コードの実行 3. [Open Log (ログを開く)] を選択解除し、[Execute (実行)] をクリックします。 コードを実行するたびに、ログが作成されて [Logs (ログ)] パネルの一覧に表示されます。 ログをダブルクリックするとログインスペクタで開きます。一度に複数のログを開いて結果を比較できます。 ログインスペクタは、操作のソース、その操作のトリガ、その後の状況を表示する、状況に対応する実行ビュー アです。このツールを使用して、データベースイベント、Apex処理、ワークフロー、および入力規則ロジック を含むデバッグログを検査できます。 ログインスペクタには、特定の用途のために事前定義されたパースペクティブが含まれます。[Debug (デバッ グ)] > [Switch Perspective (パースペクティブを切り替え)] をクリックして別のビューを選択するか、Ctrl キーを 押しながら P キーを押して個別のパネルを選択します。通常、[Execution Log (実行ログ)] パネルを最も頻繁に使 用します。このパネルには、コードを実行したときに発生した一連のイベントが表示されます。1 つのステー トメントでも多くのイベントが生成されます。ログインスペクタでは、メソッドの入口と出口、データベース や Web サービスの操作、リソースの制限など、多くの種別のイベントがキャプチャされます。イベント種別 USER_DEBUG は、System.debug() ステートメントの実行を示します。 31 Apex の概要 開発者コンソールを使用した Apex コードの実行 1. [Debug (デバッグ)] > [Open Execute Anonymous Window (実行匿名ウィンドウを開く)] をクリックするか、Ctrl キーを押しながら E キーを押し、次のコードを入力します。 System.debug( 'Hello World' ); System.debug( System.now() ); System.debug( System.now() + 10 ); 2. [Open Log (ログを開く)] を選択して、[Execute (実行)] をクリックします。 3. [Execution Log (実行ログ)] パネルで、[Exe (実行)] を選択します。これにより、実行されたステートメントを表 す項目のみが表示されるように制限されます。たとえば、累積制限情報は除外されます。 4. リストを絞り込んで USER_DEBUG イベントのみを表示するには、[Debug Only (デバッグのみ)] を選択する か、[Filter (検索条件)] 項目に 「USER」と入力します。 メモ: 検索条件のテキストでは、大文字と小文字が区別されます。 これで、Force.com プラットフォームでコードを実行して、結果を表示できました。 もうひとこと... 開発者コンソールのヘルプ 開発者コンソールの詳細を参照するには、開発者コンソールで [Help (ヘルプ)] > [Help Docs… (ヘルプドキュ メント...)] をクリックします。さまざまなガイドツアーを参照することもできます。[Help (ヘルプ)] > [Take the tour (ツアーを開始)] を選択して開始し、機能を選択して詳細を学習できます。 匿名ブロック 開発者コンソールでは、コードステートメントを簡単に実行できます。[Logs (ログ)]パネルで結果をすぐに 評価できます。開発者コンソールで実行するコードは、匿名ブロックと呼ばれます。匿名ブロックは現在 のユーザとして実行されるため、コードがユーザのオブジェクトレベルや項目レベルの権限に違反すると コンパイルに失敗する場合があります。これは、Apex クラスとトリガには該当しません。 32 Apex の概要 まとめ まとめ Apexコードを実行して実行結果を表示するには、開発者コンソールを使用します。詳細な実行結果には、コー ドで生成された出力だけでなく、実行パスに沿って発生したイベントも含まれます。こうしたイベントとし て、別のコードをコールした結果やデータベース操作の結果などがあります。 クラスの作成とインスタンス化 Apexはオブジェクト指向プログラミング言語であるため、記述したApexの多くはクラスに包含され、オブジェ クトのブループリントまたはテンプレートと呼ばれることがあります。このチュートリアルでは、2 つのメ ソッドを持つ単純なクラスを作成し、開発者コンソールから実行します。 開発者コンソールを使用した Apex クラスの作成 開発者コンソールで Apex クラスを作成する手順は、次のとおりです。 1. 開発者コンソールを開くには、あなたの名前 > [開発者コンソール] をクリックします。 2. [File (ファイル)] > [New (新規)] > [Apex Class (Apex クラス)] をクリックします。 3. 新しいクラスの名前として「HelloWorld」と入力し、[OK] をクリックします。 4. 新しい空の HelloWorld クラスが作成されます。静的メソッドをクラスに追加するには、中括弧の間に次 のテキストを追加します。 public static void sayYou() { System.debug( 'You' ); } 5. インスタンスメソッドを追加するには、次のテキストを最後の閉じ中括弧の直前に追加します。 public void sayMe() { System.debug( 'Me' ); } 6. [ファイル] > [保存] をクリックします。 33 Apex の概要 開発者コンソールを使用した Apex クラスの作成 もうひとこと... • ここでは、静的メソッド sayYou() とインスタンスメソッド sayMe() を持つ HelloWorld というクラス を作成しました。メソッドの定義を見ると、これらのクラスが別のクラス System をコールして、そのク ラスの debug() メソッドを呼び出し、そのメソッドが文字列を出力することがわかります。 • 作成したクラスの sayYou() メソッドを呼び出すと、そのメソッドは System クラスの debug() メソッ ドを呼び出し、出力が表示されます。 • 開発者コンソールは、バックグラウンドでコードを検証して、コードが構文的に正しく、正常にコンパイ ルされるかどうかを確認します。コードの入力ミスなど、間違いはどうしても起こります。コードに間違 いがあると、[Problems (問題)] ペインにエラーが表示され、感嘆符がペインヘッダーの横に追加されます ([Problems! (問題!)])。 • エラーのリストを表示するには、[Problems (問題)] パネルを展開します。エラーをクリックすると、エラー が発生したコード行に移動します。たとえば、System.debug ステートメントの最後の閉じ括弧を付け忘 れると、次のようにエラーが表示されます。 閉じ括弧を再度追加すると、エラーが消えます。 34 Apex の概要 クラスメソッドのコール クラスメソッドのコール HelloWorld クラスを作成できたので、次の手順に従ってそのメソッドをコールします。 1. 開発者コンソールの [Execute Anonymous Window (実行匿名ウィンドウ)] で次のコードを実行して HelloWorld クラスの静的メソッドをコールします (この手順を忘れた場合は、「開発者コンソールの有効化」を参照し てください)。入力パネルに前のコードが残っている場合は、削除します。静的メソッドをコールする場 合、クラスのインスタンスを作成する必要はありません。 HelloWorld.sayYou(); 2. 結果のログを開きます。 3. USER_DEBUG イベントを表示するように検索条件を設定します (「開発者コンソールの有効化」 でも説明 されています)。ログには「You」が表示されます。 4. 次のコードを実行して HelloWorld クラスのインスタンスメソッドをコールします。インスタンスメソッ ドをコールする場合、最初に HelloWorld クラスのインスタンスを作成する必要があります。 HelloWorld hw = new HelloWorld(); hw.sayMe(); 5. 結果のログを開いて検索条件を設定します。 [Details (詳細)] 列に「Me」が表示されます。このコードは HelloWorld クラスのインスタンスを作成し、そ のインスタンスを hw という変数に割り当てます。次にそのインスタンス上の sayMe() メソッドをコール します。 6. 両方のログの検索条件をクリアして、2 つの実行ログを比較します。最も明らかな違いは、HelloWorld インスタンスの作成と変数 hw への割り当てに関する部分です。それ以外にも違いがあるか探してみてく ださい。 これで、Force.com プラットフォームで新しいコードを作成して実行できました。 Salesforce ユーザインターフェースを使用した Apex クラスの作成 Apex クラスは、Salesforce ユーザインターフェースで作成することもできます。 1. [設定] で、[開発] > [Apex クラス] をクリックします。 2. [新規] をクリックします。 3. エディタペインに、次のコードを入力します。 public class MessageMaker { } 35 Apex の概要 まとめ 4. [Quick Save (適用)] をクリックします。代わりに [Save (保存)] をクリックすることもできますが、その場合は クラスエディタが閉じて、[Apex クラス] リストに戻ります。[Quick Save (適用)] をクリックすると、Apex コー ドが保存されて実行可能になりますが、そのまま編集を続行することもでき、コードの追加や変更がしや すくなります。 5. 次のコードをクラスに追加します。 public static string helloMessage() { return('You say "Goodbye," I say "Hello"'); } 6. [Save (保存)] をクリックします。 作成したクラスを開発者コンソールに表示して編集することもできます。 1. 開発者コンソールで、[ファイル] > [Open (開く)] をクリックします。 2. [Setup Entity Type (設定エンティティ種別)] パネルで、[クラス] をクリックし、[Entities (エンティティ)] パネル の [MessageMaker] をダブルクリックします。 ソースコードエディタに MessageMaker クラスが表示されます。エディタ内に直接コードを入力して編集 し、クラスを保存できます。 まとめ このチュートリアルでは、Apexクラスを作成してそのリストを表示する方法を学習しました。作成したクラス とメソッドは、開発者コンソールから、また、記述した他のクラスとコードからコールできます。 もうひとこと... • また、Apex コードの作成と実行に、Force.com IDE を使用することができます。詳細は、サイト (Developer Force サイト: https://developer.salesforce.com/) で「Force.com IDE」を検索してください。 36 Apex の概要 WarehouseUtils クラスの作成 WarehouseUtils クラスの作成 この練習問題では、実際の作業を行います。Salesforce を検索してクエリと一致するレコードを見つけ、これら のレコードを Visualforce ページで利用できるようにする新しい Apex クラスを記述します。 次のようなシナリオです。これから小さいアプリケーションを作成して、Acme Wireless 組織に勤務するモバイ ル技術者に、近隣の倉庫を検出する手段を提供します。たとえば、モバイル技術者が依頼を受けて出掛けた先 で部品が必要になった場合、このページを使用して半径 20 マイル以内にある倉庫を探すことができます。倉 庫ごとに、倉庫の名前、住所、電話番号と共にピンが地図に表示されます。 これから記述する Apex および Visualforce コードが、モバイルデバイスの Salesforce 1 内でこのすべてを行います。 なかなかの優れものです。 WarehouseUtils Apex クラスの作成 最初に新しいクラスを定義し、コンストラクタメソッドを追加する必要があります。 Apexクラスを使用する場所によっては、該当するインターフェースや規則に適合させる必要がある場合があり ます。たとえば、WarehouseUtils クラスには 2 通りの使用方法があります。1 つは Visualforce ページから Visualforce コントローラ拡張として使用する方法で、もう 1 つは Visualforce JavaScript Remoting からリモートアク ションとして使用する方法です。 コントローラ拡張は、Visualforce ページからコール可能なメソッドとして機能を追加することで、Visualforce コ ントローラの機能を拡張する場合に使用します。Visualforceページにはコントローラを 1 つしか設定できません が、コントローラ拡張は 1 つ設定することも、設定しないことも、多数設定することも可能です。 コントローラ拡張として使用するには、Apex クラスに、その唯一のパラメータとして Visualforce コントローラ を受け入れるコンストラクタが必要です (リモートアクションの要件については後述します)。 1. [設定] > [開発] > [Apex クラス] に移動して、[新規] をクリックします。 2. エディタに、次のコードを入力します。 global with sharing class WarehouseUtils { public WarehouseUtils(ApexPages.StandardSetController controller) { } // findNearbyWarehouses method goes here } 3. [Quick Save (適用)] をクリックします。 コンストラクタメソッドは、その唯一のパラメータとして ApexPages.StandardSetController オブジェ クトを取ります。これによって、標準リストコントローラでクラスを Visualforce コントローラ拡張として使用 できるようになります。標準コントローラでも使用するには、異なるパラメータ型を取るようにコンストラク タをオーバーロードします。つまり、ApexPages.StandardController パラメータを取る 2 つ目のコンス トラクタメソッドを追加します。 public WarehouseUtils(ApexPages.StandardController controller) { } 37 Apex の概要 「スタブ」findNearbyWarehouses メソッドの追加 これらのコンストラクタは空ですが、より複雑なコントローラ拡張ではコントローラをインスタンス変数とし て保存します。これで、Apexの上記のような使い方を理解できたのではないでしょうか。ぜひ試してみてくだ さい。 「スタブ」findNearbyWarehouses メソッドの追加 次に、Visualforce ページで使用するメソッドにスタブを作成します。 コントローラ拡張にある public メソッドおよび global メソッドはそれぞれ関連付けられた Visualforce ページで使 用できます。メソッドをコールする場合、ページが式内のメソッドを参照することも、JavaScript Remoting を使 用してメソッドを直接コールすることも可能です。 ここでは、アプリケーションを使用しているモバイル技術者の最寄の倉庫をクエリするメソッドを作成しま す。メソッドでは技術者の所在地を把握する必要があるため、緯度と経度の値を渡します。これらの値は、 Salesforce1 を実行しているデバイスに組み込まれた地理位置情報機能から提供されます。Visualforce の式は直接 パラメータを取ることができないため、JavaScript Remoting の使用を予定しています。現時点では、緯度と経度 のパラメータを取る「スタブ」メソッドを記述して、倉庫レコードのリストを返します。 1. コードエディタで、コメントライン // findNearbyWarehouses method goes here を次のコードで置 き換えます。 // Find warehouses nearest a geolocation @RemoteAction global static List<Warehouse__c> findNearbyWarehouses(String lat, String lon) { // Initialize results to an empty list List<Warehouse__c> results = new List<Warehouse__c>(); // method implementation goes here // Return the query results return(results); } 2. [Quick Save (適用)] をクリックします。 このメソッド定義は Apex メソッドの要点を示しています。 • global: メソッドのスコープ。JavaScript Remoting でコールされるメソッド (リモートアクション) は、global または public である必要があります。 • static: インスタンスメソッドではなく、クラスメソッドです。つまり、このクラスのオブジェクトをイ ンスタンス化しなくても、メソッドをコールできます。リモートアクションのメソッドは静的である必要 があります。 • List<Warehouse__c>: メソッドの戻り値のデータ型。 • findNearbyWarehouses: メソッドの名前。 • (String lat, String lon): メソッドのパラメータ。 メソッドの残りの部分 (括弧内) が実装です。次にこの部分を記述します。 38 Apex の概要 クエリの実行および結果の取得 クエリの実行および結果の取得 次は実際のメソッド実装を記述します。このメソッドは、ユーザのデイバスが提供した緯度と経度の値を取 り、最寄の倉庫を検索します。 関連性の高いレコードを検索するには、指定したパラメータを完全なSOQL クエリに変換する必要があります。 SOQL は、Force.com プラットフォームのクエリの第一言語です。この場合のように、SOQL は Apex で使用できま すが、他の Salesforce API とも併用できます。 ここでは、文字列の連結によって必要な SOQL 要素をパラメータ値と組み合わせ、クエリを動的に構築します。 続いてクエリを実行して結果を取得します。 1. メソッド実装ブロック内で、コメントライン // method implementation goes here を次のコードで 置き換えます。 // SOQL query to get the nearest warehouses String queryString = 'SELECT Id, Name, Location__Longitude__s, Location__Latitude__s, ' + 'Street_Address__c, Phone__c, City__c ' + 'FROM Warehouse__c ' + 'WHERE DISTANCE(Location__c, GEOLOCATION('+lat+','+lon+'), \'mi\') < 20 ' + 'ORDER BY DISTANCE(Location__c, GEOLOCATION('+lat+','+lon+'), \'mi\') ' + 'LIMIT 10'; // Run the query results = database.Query(queryString); 2. [Quick Save (適用)] をクリックします。 SOQL は標準 SQL とよく似ているため、SQL の知識があれば SOQL も簡単に使えるようになります。クエリ言語 についての詳細は、『Force.com SOQL および SOSL リファレンス』を参照してください。 このクエリは次の 2 点がやや分かり難いかもしれません。 • GEOLOCATION() 関数は、緯度と経度から地理位置情報を作成します。地理位置情報は、具体的な物理的 位置を表します。ここでは、この関数で緯度パラメータと経度パラメータを組み合わせ、ユーザの位置を 表す値を作成します。 • DISTANCE() 関数は、2 つの地理位置情報の距離を計算します。ここでは、Warehouse__c.Location__c 地理位置情報項目とユーザの地理位置情報間の距離を計算します。クエリの WHERE 句は、20 マイル以内の DISTANCE() 値を検索します。 まとめとコードの確認 これで完了です。Visualforce ページで使用できる新しい Apex ユーティリティクラスを記述しました。 完成したクラスは次のようになります。 global with sharing class WarehouseUtils { public WarehouseUtils(ApexPages.StandardSetController controller) { } // Find warehouses nearest a geolocation @RemoteAction 39 Apex の概要 WarehouseUtils クラスのテストおよびデバッグ global static List<Warehouse__c> findNearbyWarehouses(String lat, String lon) { // Initialize results to an empty list List<Warehouse__c> results = new List<Warehouse__c>(); // SOQL query to get the nearest warehouses String queryString = 'SELECT Id, Name, Location__Longitude__s, Location__Latitude__s, ' + 'Street_Address__c, Phone__c, City__c ' + 'FROM Warehouse__c ' + 'WHERE DISTANCE(Location__c, GEOLOCATION('+lat+','+lon+'), \'mi\') < 20 ' + 'ORDER BY DISTANCE(Location__c, GEOLOCATION('+lat+','+lon+'), \'mi\') ' + 'LIMIT 10'; // Run the query results = database.Query(queryString); // Return the query results return(results); } } Visualforce ページでこの新しいコードをすぐに使ってみたいところですが、このコードにはバグがあります (す でにお分かりでしょうか)。そのため、先に進む前に、Apex コードのテストおよびデバッグについても少し説 明しておきます。 WarehouseUtils クラスのテストおよびデバッグ この練習問題では、新しい Apex クラスを取り上げ、意図したとおりに機能するかどうかを検証します。Apex 単体テストフレームワークを使用してテストを記述し、新しいコードをデバッグします。 コードの単体テストを記述することは、Apex コード開発の基本です。本番組織に Apex コードをリリースする には、75% 以上のテストカバー率が必要です。さらに、テストカバー率にカウントされるテストに合格する必 要があります。テストの実施は、アプリケーションの品質を確保するためには非常に重要です。さらに、将 来、コードに変更を加えた場合に再実行できるテストのセットがあると、変更によって既存のコードに発生す る潜在的な不具合を検出できます。 テストはプロジェクトの中であまり楽しい工程ではないかもしれません。しかし、テストが簡単であるという ことがわかれば、考えが変わるかも知れません。 Apex テストクラスを作成する 単体テストは Apex クラスに含まれており、いくつか小規模な追加をするだけで通常の Apex クラスのようにな ります。 テストクラスは、アノテーションを使用してテストクラスとマークします。テストクラスは、組織のコードサ イズ制限にはカウントされません。 1. 開発者コンソールで、[ファイル] > [新規] > [Apex クラス] をクリックします。 2. クラス名として「TestWarehouseUtils」と入力し、[OK] をクリックします。 40 Apex の概要 テストメソッドを追加してコードを設定する 3. エディタで、自動生成されたコードを削除し、次のコードで置き換えます。 @isTest private class TestWarehouseUtils { // test methods go here } @isTest アノテーションは、Force.com に Apex クラス内のコードはすべてテストコードであると伝えます。こ れは、テストコードを private に維持するためのベストプラクティスです。Apex テストフレームワークのみ がテストを検索して実行できます。 メモ: 別個のテストクラスで使用されるテストヘルパーまたはユーティリティクラスを作成する場合、そ れらは public にする必要があります。 テストメソッドを追加してコードを設定する テストクラス内にテストメソッドを定義して、組織のテストスイートに追加します。 WarehouseUtils クラスのメソッドは 1 つだけですが、いくつかの機能をテストする必要があります。この メソッドは、コールされたら特定の位置から 20 マイル以内にあるすべての倉庫を返す必要があります。また、 半径 20 マイルより外側にある倉庫は返してはなりません。さらに、ある位置の近隣として表示された場所は、 要求された位置が遠くに変更されたら、近隣ではなくなる必要があります。こうした期待事項をテストするた めに、2 つのテストメソッドを作成します。 これらの期待事項をテストするには、テスト位置から既知の距離内にあるテスト倉庫がいくつか必要です。 Apexテストフレームワークを使用すると、テスト実行中に、テストデータを使用するテストやテストデータの みを容易に作成できます。これはテストの分離と呼ばれます。組織のデータは、デフォルトではテストから認 識できず、テストデータとテストで行ったデータへの変更は、テスト実行の終了時にすべてロールバックされ ます。ただし、ここでテストするのは組織のデータではないため、テスト独自のデータを作成する必要があり ます。そのデータを処理するヘルパーメソッドもいくつか作成します。 1. 前回のステップで作成したテストクラスに、2 つの新しいテストメソッドスタブといくつかのヘルパーメ ソッドを追加します。クラス定義ブロック内のコメント行 // test methods go here を次のコードで 置き換えます。 // test that we find only warehouses that are within 20 miles static testMethod void testFindWarehousesWithinTwentyMiles() { // test for when close to warehouses here } // test that we don't find anything when further than 20 miles static testMethod void testDontFindWarehousesFurtherThanTwentyMiles() { // test for when far from warehouses here } 41 Apex の概要 findNearbyWarehouses メソッドをテストする // helper methods to create test data static Warehouse__c createTestWarehouse(String name, Decimal lat, Decimal lon) { Warehouse__c w = new Warehouse__c( Name = name, Location__Latitude__s = lat, Location__Longitude__s = lon ); insert w; return w; } static Warehouse__c createClosestTestWarehouse() { // Federal Reserve Bank of SF, next door to Salesforce HQ return(createTestWarehouse('Warehouse1', 37.7927731, -122.4010922)); } static Warehouse__c createCloseTestWarehouse() { // Moscone Center, home of Dreamforce return(createTestWarehouse('Warehouse2', 37.783944, -122.401289)); } static Warehouse__c createTooFarTestWarehouse() { // Mount Rushmore, South Dakota return(createTestWarehouse('TooFarWarehouse', 43.879102, -103.459067)); } テストメソッド定義は、パラメータなしの static testMethod void testName() です。現時点では、こ のテストクラスは何もテストしませんが、実際のテストコードを挿入する必要があります。 ヘルパーメソッドの定義には testMethod がなく、パラメータと戻り値を取ることができます。static であ るほかに、必要に応じてどの種類のメソッドにもすることができます。ここでの唯一の機能は、事前定義済み 倉庫オブジェクトを新しく作成してデータベースに保存することです。 テストとヘルパーでは、レコードの挿入、変更、および削除を必要なだけ行うことができ、十分にコーディン グ練習ができます。前述のとおり、すべてのデータベース操作は、分離されたテスト専用環境で行われます。 テストによって行われた変更が永続的に保存されることはありません。 findNearbyWarehouses メソッドをテストする 最小限のコードを作成して、コーディングを練習し、その動作をテストします。機能を 1 つずつテストしま す。 作成するテストメソッドの実装は 2 つあります。一方は、何か所かの倉庫に近い場所から WarehouseUtils.findNearbyWarehouses コールをテストし、もう一方はどの倉庫にも近くない場所から WarehouseUtils.findNearbyWarehouses コールをテストします。 1. testFindWarehousesWithinTwentyMiles メソッド内のコメント行 // test for when close to warehouses here を次のコードで置き換えます。 // Salesforce HQ String myLat = '37.793731'; String myLon = '-122.395002'; 42 Apex の概要 findNearbyWarehouses メソッドをテストする // Create test warehouse data Warehouse__c closestWarehouse = createClosestTestWarehouse(); Warehouse__c closeWarehouse = createCloseTestWarehouse(); Warehouse__c tooFarWarehouse = createTooFarTestWarehouse(); // Perform the test execution Test.startTest(); List<Warehouse__c> nearbyWarehouses = WarehouseUtils.findNearbyWarehouses(myLat, myLon); Test.stopTest(); // Make assertions about expected results // We expect two warehouses System.assert(nearbyWarehouses.size() == 2); // We expect two SPECIFIC warehouses, in order of proximity System.assert(nearbyWarehouses[0].Name == closestWarehouse.Name); System.assert(nearbyWarehouses[1].Name == closeWarehouse.Name); // We do NOT expect to see the warehouse that's too far away if(0 < nearbyWarehouses.size()) { for (Warehouse__c wh : nearbyWarehouses) { System.assert(wh.Name != tooFarWarehouse.Name); } } 2. testDontFindWarehousesFurtherThanTwentyMiles メソッド内のコメント行 // test for when far from warehouses here を次のコードで置き換えます。 // Eiffel Tower, Paris, France String myLat = '48.85837'; String myLon = '2.294481'; // Create test warehouse data Warehouse__c closestWarehouse = createClosestTestWarehouse(); Warehouse__c closeWarehouse = createCloseTestWarehouse(); Warehouse__c tooFarWarehouse = createTooFarTestWarehouse(); // Perform the test execution Test.startTest(); List<Warehouse__c> nearbyWarehouses = WarehouseUtils.findNearbyWarehouses(myLat, myLon); Test.stopTest(); // We expect to see NO warehouses System.assert(nearbyWarehouses.size() == 0); このテストメソッドは、単純なパターンに従っています。 • テストデータの作成など、必要な設定を実行します。 • Test.startTest() および Test.endTest() テストフレームワークコール内にラップされたテストを実 行します。 43 Apex の概要 テストを実行してテスト結果を確認する • テストの実行結果を既知のデータと比較します。つまり、実際の動作と期待される動作を比較します。 これは、独自のテストコードで使用するのに適したパターンです。また、一度に 1 つの機能をテストし、テス トごとにメソッドを分ける場合のベストプラクティスでもあります。 テストを実行してテスト結果を確認する Force.com テストフレームワークを使用すると容易にテストを実行でき、テストコードについてテストの実行結 果とテストカバー率分析が提供されます。 優れたテストスイートで正しい結果を出すことは、非常に喜ばしいものです。さっそくテストを始めましょ う。 1. 開発者コンソールで、[Test (テスト)] > [New Run (新規実行)] をクリックします。 2. [TestWarehouseUtils] をクリックし、[>] をクリックしてテストクラスを残りの実行に追加します。 3. [Run (実行)] をクリックしてテストを実行します。 [Tests (テスト)] タブにテスト結果が表示されます。テスト実行フォルダを展開し、[Test (テスト)] タブでテ ストクラスを展開すると、実行されたメソッドを確認できます。今回、クラスには 2 つのテストメソッド が含まれています。 4. [Overall Code Coverage (全体のコードカバー率)] ペインに、このテストクラスのコードカバー率が 83% と表示 されます。出力は次のようになります。WarehouseUtils クラスのコードカバー率の概要が表示されてい ます。 結果から、いくつか重要なことがわかります。 • テストが合格したかどうかを示します。テストの System.assert ステートメントの Boolean 条件が失 敗の場合、つまり、アサーションが false の場合は、ここに failure と警告されます。多くのアサーショ ンを追加することで、コードが期待どおりに動作しているか検証しやすくなります。 • テスト実行の詳細を提供します。[Logs (ログ)] タブで関連するデバッグログに目を通すと、実行された メソッド、作成または変更されたレコード、実行されたクエリの数などがわかります。 • コードカバー率と影響された各クラスで実行されたコード行数を示します。 結果ページには、WarehouseUtils クラスの達成カバー率が 83% と表示されています。リリースするには 十分なコードカバー率ですが、どうせなら完璧を目指しましょう。何がもれているかを確認します。 5. [Overall Code Coverage (全体のコードカバー率)] ペインで、WarehouseUtils クラスカバー率の行をダブルク リックします。 [Code Coverage (コードカバー率)] ページが開きます。青の強調表示は、テスト実行中にカバー (実行) された コード行を示します。赤で強調表示された行は、実行されなかったコード行を示します。 44 Apex の概要 テストを実行してテスト結果を確認する この場合、3 行目 (空のコンストラクタメソッド) は実行されませんでした。これは、静的メソッドのみを コールし、このクラスをインスタンス化しなかったためです。空のコンストラクタは大きな問題ではあり ませんが、それをカバーするのも簡単です。 6. 次の新しいテストメソッドをテストクラスに追加します。 // test the class constructor static testMethod void testClassConstructor() { Test.startTest(); WarehouseUtils utils = new WarehouseUtils(null); Test.stopTest(); // We expect that utils is not null System.assert(utils != null); } テストを再実行すると、コードカバー率が 100% になるはずです。すばらしいですね。 コードカバー率とは、本番コードのうち (この場合は WarehouseUtils クラス)、テストコード (作成したテス トクラス) で実行される割合です。つまり、テストコードを実行したとき、本番コードの全部が実行されたの か、部分だけなのかを示します。コードを部分的にしか実行していない場合、本番コードのテストされていな い部分にまだバグがあるかもしれません。コードカバー率ビューでは、それを簡単に視覚化できます。 メモ: コードには、青や赤で強調表示されていないものもあります。これはどういう意味でしょうか。た とえば、クラス宣言、@RemoteAction アノテーション、およびコメントが強調表示されないのは当然で すが、queryString 式の追加の行も強調表示されていません。これには理由があります。 強調表示処理を行うコンパイラによって、これらの行はすべて実行可能ではないとみなされています。 いずれにしても、エディタで 1 行のコードを複数行に分割した場合、強調表示されるのは最初の行だけで す。 45 Apex の概要 バグの検出 バグの検出 テストスイートを完了したとしても、バグがないとは言い切れません。バグが見つからなかっただけかもしれ ません。まだ、見つかっていないということです。 現時点の WarehouseUtils にもバグは存在します。厳密に言えば各自の DE 組織に存在する各自のバグという ことなりますが、それは論点ではありません。ここではバグを検出して修正する方法を見ていきます。 すでに対処法を見い出しているかもしれませんが、念のために役立つヒントをご紹介します。緯度または経度 に無効な値を指定して WarehouseUtils.findNearbyWarehouses をコールしたらどうなるでしょうか。 試してみましょう。[Execute Anonymous (匿名実行)] ウィンドウで Apex の短いスニペットを実行して、どうなる か見てみます。 1. 開発者コンソールで、[Debug (デバッグ)] > [Open Execute Anonymous Window (匿名実行ウィンドウを開く)] を クリックします。 2. 次のコードを追加して、[実行] をクリックします。 List<Warehouse__c> warehouses = WarehouseUtils.findNearbyWarehouses(null, null); for(Warehouse__c wh : warehouses) { System.debug(wh.Name); } この結果、[Execute Anonymous (匿名実行)] ウィンドウに「System.QueryException: unexpected token: 'null'」というエ ラーが表示されます。緯度または経度が無効な値であれば、当然 GEOLOCATION 関数は機能しません。 これを踏まえて、まずテストを記述し、レベルの高い方法でバグを修正します。 バグのテストの記述 テストファースト開発とは、機能を実装するコードを記述する前にテストを記述する手法です。 まだ機能が実装されていないため、テストは失敗します。続いて機能を実装し、もう一度テストを実行しま す。テストに合格すれば、機能が正しく実装されたという一定の確信が得られます。新しい機能を開発するた びにこのサイクルを繰り返せば、ソフトウェアの実装に対する自信が深まります。 1. TestWarehouseUtils テストクラスに、次の新しいテストメソッドを追加します。 // test that we use a default location if the lat or long is invalid static testMethod void testFindWarehousesDefaultLocation() { // Trigger the default location, which should be SF String myLat = null; String myLon = null; // Create test warehouse data Warehouse__c closestWarehouse = createClosestTestWarehouse(); Warehouse__c closeWarehouse = createCloseTestWarehouse(); Warehouse__c tooFarWarehouse = createTooFarTestWarehouse(); // Perform the test execution Test.startTest(); List<Warehouse__c> nearbyWarehouses = WarehouseUtils.findNearbyWarehouses(myLat, myLon); Test.stopTest(); 46 Apex の概要 バグの修正 // Make assertions about expected results // We expect two warehouses System.assert(nearbyWarehouses.size() == 2); // We expect two SPECIFIC warehouses, in order of proximity System.assert(nearbyWarehouses[0].Name == closestWarehouse.Name); System.assert(nearbyWarehouses[1].Name == closeWarehouse.Name); // We do NOT expect to see the warehouse that's too far away if(0 < nearbyWarehouses.size()) { for (Warehouse__c wh : nearbyWarehouses) { System.assert(wh.Name != tooFarWarehouse.Name); } } } 2. 更新したテストクラスを保存して、もう一度テストを実行します。 新しいテストを追加したテストスイートを再実行すると失敗します。これは、テストが機能し、本番コードが 意図したとおりに機能していないことを検出したことになります。 バグの修正 コードの不適切な動作を明らかにしその部分を切り離すテストがあれば、たいていはすぐに問題を修正できま す。そして問題が修正されれば、テストスイートに合格します。 緯度または経度の値を空白にしたまま findNearbyWarehouses メソッドをコールすると、失敗してエラーが 生じることを確認しました。未指定の値や空の値がないかどうかは簡単に確認できます。 1. WarehouseUtils クラスで、results を空のリストに初期化した後、クエリ文字列をアセンブルする前に、次 のコードを追加します。 // If geolocation parameters are invalid, use San Francisco if(String.isBlank(lat) || String.isBlank(lon)) { lat = '37.793731'; lon = '-122.395002'; } 2. 変更内容を保存して、テストスイートを再度実行します。 このテストは正常に実行されるはずです。これで、テストスイートは合格です。 Apex およびテストのトピックを終える前に、新しく追加したコードを見てみましょう。この if 条件で、無効 の可能性がある緯度と経度の値を網羅できているでしょうか。何を書き加えることはありませんか。この if 条件で十分でしょうか。lat と lon の有効性をチェックするヘルパーメソッドをクラスに追加してみましょ う。どのように追加すればよいでしょうか。ヘルパーメソッドは private と public のどちらにすべきで しょうか。 ここで簡単な練習問題をもう 1 つ行います。この場合は、WarehouseUtils と TestWarehouseUtils の両方 にハードコード化された緯度と経度の値がいくつかあります。どうすればこの数値が常に同期されているとい えるでしょうか。非同期になった場合には何が起こるでしょうか。次の場合について考えてみます。緯度また 47 Apex の概要 まとめとコードの確認 は経度の値に入力ミスがあり、テスト位置が本来の最寄のテスト倉庫から離れ、別のテスト倉庫付近に設定さ れたとしたらどうなるでしょうか。 まとめとコードの確認 ここまでで、Apexクラスのテストスイートを書き上げました。また、テスト実行の作成方法や、テストスイー トのコードカバー率の確認方法も学習しました。 コードをリリースするためには、コードの正しい動作を検証する一連のテストを完成させておく必要がありま す。またこれは、長期間の開発を成功させるためにも重要です。 完全なテストクラスは次のようになります。 @isTest private class TestWarehouseUtils { // test that we find only warehouses that are within 20 miles static testMethod void testFindWarehousesWithinTwentyMiles() { // Salesforce HQ String myLat = '37.793731'; String myLon = '-122.395002'; // Create test warehouse data Warehouse__c closestWarehouse = createClosestTestWarehouse(); Warehouse__c closeWarehouse = createCloseTestWarehouse(); Warehouse__c tooFarWarehouse = createTooFarTestWarehouse(); // Perform the test execution Test.startTest(); List<Warehouse__c> nearbyWarehouses = WarehouseUtils.findNearbyWarehouses(myLat, myLon); Test.stopTest(); // Make assertions about expected results // We expect two warehouses System.assert(nearbyWarehouses.size() == 2); // We expect two SPECIFIC warehouses, in order of proximity System.assert(nearbyWarehouses[0].Name == closestWarehouse.Name); System.assert(nearbyWarehouses[1].Name == closeWarehouse.Name); // We do NOT expect to see the warehouse that's too far away if(0 < nearbyWarehouses.size()) { for (Warehouse__c wh : nearbyWarehouses) { System.assert(wh.Name != tooFarWarehouse.Name); } } } // test that we don't find anything further than 20 miles static testMethod void testDontFindWarehousesFurtherThanTwentyMiles() { // Eiffel Tower, Paris, France String myLat = '48.85837'; 48 Apex の概要 まとめとコードの確認 String myLon = '2.294481'; // Create test warehouse data Warehouse__c closestWarehouse = createClosestTestWarehouse(); Warehouse__c closeWarehouse = createCloseTestWarehouse(); Warehouse__c tooFarWarehouse = createTooFarTestWarehouse(); // Perform the test execution Test.startTest(); List<Warehouse__c> nearbyWarehouses = WarehouseUtils.findNearbyWarehouses(myLat, myLon); Test.stopTest(); // We expect to see NO warehouses System.assert(nearbyWarehouses.size() == 0); } // test the class constructor static testMethod void testClassConstructor() { Test.startTest(); WarehouseUtils utils = new WarehouseUtils(null); Test.stopTest(); // We expect that utils is not null System.assert(utils != null); } // test that we use a default location if the lat or long is invalid static testMethod void testFindWarehousesDefaultLocation() { // Trigger the default location, which should be SF String myLat = null; String myLon = null; // Create test warehouse data Warehouse__c closestWarehouse = createClosestTestWarehouse(); Warehouse__c closeWarehouse = createCloseTestWarehouse(); Warehouse__c tooFarWarehouse = createTooFarTestWarehouse(); // Perform the test execution Test.startTest(); List<Warehouse__c> nearbyWarehouses = WarehouseUtils.findNearbyWarehouses(myLat, myLon); Test.stopTest(); // Make assertions about expected results // We expect two warehouses System.assert(nearbyWarehouses.size() == 2); // We expect two SPECIFIC warehouses, in order of proximity System.assert(nearbyWarehouses[0].Name == closestWarehouse.Name); System.assert(nearbyWarehouses[1].Name == closeWarehouse.Name); // We do NOT expect to see the warehouse that's too far away 49 Apex の概要 まとめとコードの確認 if(0 < nearbyWarehouses.size()) { for (Warehouse__c wh : nearbyWarehouses) { System.assert(wh.Name != tooFarWarehouse.Name); } } } // helper methods to create test data static Warehouse__c createTestWarehouse(String name, Decimal lat, Decimal lon) { Warehouse__c w = new Warehouse__c( Name = name, Location__Latitude__s = lat, Location__Longitude__s = lon ); insert w; return w; } static Warehouse__c createClosestTestWarehouse() { // Federal Reserve Bank of SF // Next door to Salesforce HQ return(createTestWarehouse('Warehouse1', 37.7927731, -122.4010922)); } static Warehouse__c createCloseTestWarehouse() { // Moscone Center, home of Dreamforce return(createTestWarehouse('Warehouse2', 37.783944, -122.401289)); } static Warehouse__c createTooFarTestWarehouse() { // Mount Rushmore, South Dakota return(createTestWarehouse('TooFarWarehouse', 43.879102, -103.459067)); } } 50 Visualforce と Apex の連携 このワークブックの前のセクションで、Visualforce と Apex をそれぞれ学習しました。Visualforce と Apex を組み合 わせると、機能性、柔軟性、汎用性などが総じて向上します。このセクションでは、Visualforce と Apex を併用 して、モバイルデバイス上の Salesforce1 で使用できる本格的なアプリケーションを作成します。 最初に、モバイル技術者が携帯電話で最寄の部品倉庫を検索するためのモバイルアプリケーションを完成させ ます。Visualforce ページを作成し、Visualforce の JavaScript Remoting を使用して Apex メソッドをコールする JavaScript を記述して、取得したすべての結果を地図上に表示します。ここまで出来たら、パッケージ化して Salesforce1 にリリースします。驚くほど簡単に実行できます。 このセクションでは、次のことを行います。 • Visualforce ページをバックエンドの Apex コードにリンクする。 • Apex メソッドをコールして、Visualforce ページで結果を使用する。 • Apex を使用して、Visualforce コントローラおよびコントローラ拡張を記述する。 • Visualforce JavaScript Remoting を使用して、Apex コードをコールし、結果をページに表示するデータに変換す る。 • 作成したアプリケーションをSalesforce1に追加して、モバイルユーザが各自の電話やタブレットで利用でき るようにする。 位置を認識する Visualforce ページの作成 特定の緯度と経度に近い倉庫を返す Apex 拡張を作成しました。次に、ユーザがそのクエリをコールして結果 を表示するインターフェースが必要です。 ここでシナリオを再確認しましょう。これから小さいアプリケーションを作成して、Acme Wireless 組織に勤務 するモバイル技術者に、近隣の倉庫を検出する手段を提供します。たとえば、モバイル技術者が依頼を受けて 出掛けた先で部品が必要になった場合、このページを使用して半径 20 マイル以内にある倉庫を探すことがで きます。倉庫ごとに、倉庫の名前、住所、電話番号と共にピンが地図に表示されます。 このアプリケーションを作成するにはさまざまな方法がありますが、モバイルで使いやすい動的なページを作 成するために、ここでは Google Maps API を使用します。API にアクセスして地図を表示するために必要な JavaScript はすでに拡張 Warehouse に静的リソースとして含まれています。必要なのは、そのデータを読み込んで地図を 表示するページを作成することです。 WarehouseUtils クラスにリンクする Visualforce ページを作成する 最初に、新しい Visualforce ページを作成し、サーバ側の Apex ロジックに接続します。標準リストコントローラ と拡張をページに接続することになります。 このページを Salesforce1 にリリースするため、設定を編集してページをモバイル対応にする必要があります。 この設定は、[設定] の Visualforce 用エディタでのみ行うことができます。 1. [設定] から、[開発] > [ページ] をクリックします。 51 Visualforce と Apex の連携 静的リソースをページに追加する 2. [新規] をクリックします。 3. [表示ラベル] と [名前] に「FindNearbyWarehouses」と入力します。 4. [Salesforce モバイルアプリケーションでの使用が可能] のチェックボックスをオンにします。 5. コードエディタで、生成されたコードを次のコードで置き換えます。 <apex:page sidebar="false" showheader="false" standardController="Warehouse__c" recordSetVar="warehouses" extensions="WarehouseUtils"> <!-- resources and styles go here --> <!-- JavaScript custom code goes here --> <!-- Google Maps target [div] goes here --> </apex:page> 6. [Quick Save (適用)] をクリックします。 ページが作成されてモバイルアプリケーションに対応したので、開発者コンソールまたは開発モードフッター に切り替えてページの編集を続行できます。どちらでも自分が使いやすい方のツールを使用してください。 静的リソースをページに追加する ページのシェルを作成できましたが、JavaScript の記述を開始する前に、ページで使用する複数のリソースへの 参照を追加する必要があります。 これらは Salesforce に静的リソースとして保存されており、<apex:includeScript> コンポーネントを使用し てページに関連付けることができます。このコンポーネントにより、JavaScript ライブラリが、表示される HTML のヘッダーに正しく含まれます。また、全幅バージョンの地図を表示するために、いくつかの CSS もページに 追加します。 1. <apex:page> コンポーネント内のコメント行 <!-- resources and styles go here --> を次のコー ドで置き換えます。 <!-- Include in Google's Maps API via JavaScript static resource. This is for development convenience, not production use. See next comment. --> <apex:includeScript value="{!$Resource.GoogleMapsAPI}" /> <!-- Set YOUR_API_KEY to fix JavaScript errors in production. See https://developers.google.com/maps/documentation/javascript/tutorial for details of how to obtain a Google Maps API key. --> <!-- <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=false"> </script> --> <!-- Set the map to take up the whole window --> 52 Visualforce と Apex の連携 地図を表示する場所を追加する <style> html, body { height: 100%; } .page-map, .ui-content, #map-canvas { width: 100%; height:100%; padding: 0; } #map-canvas { height: min-height: 100%; } </style> 2. [Quick Save (適用)] をクリックします。 追加したコードでは、Google Maps API を 2 つの異なる方法で参照します。一方はコメントアウトされています。 有効なバージョンは静的リソースに含まれており、開発で動作します。実際の地図アプリケーションを開発す る準備ができたら、独自の Google Maps API キーを取得して、YOUR_API_KEY 文字列を実際のキーで置き換える 必要があります。その後、<script> タグのコメントを解除し、<apex:includeScript> コンポーネントを コメントアウトするか、削除します。 Google Maps API についての詳細は、『Google Maps JavaScript API スタートガイド』を参照してください。 地図を表示する場所を追加する Google Maps API では、グラフィックを表示する場所を認識するための HTML <div> タグの「ターゲット」が必 要です。 Google Maps API は、地図を表示してから、ページの指定された場所に挿入します。そのため、そのプレースホ ルダを作成する必要があります。 1. </apex:page> 終了タグの直前にあるコメント行 <!-- Google Maps target [div] goes here --> を次のコードで置き換えます。 <!-- All content is rendered by the Google Maps code This minimal HTML just provides a target for GMaps to write to --> <body style="font-family: Arial; border: 0 none;"> <div id="map-canvas"></div> </body> 2. [Quick Save (適用)] をクリックします。 これで、ページのマークアップが完了しました。次は、JavaScript と JavaScript Remoting に取りかかります。 JavaScript を Warehouse のクエリに追加する ページに JavaScript をいくつか追加して動作させる準備ができました。最初に、関数を追加してページが読み込 まれたらコールされるようにします。この関数は、すでに作成した Apex リモートアクションメソッドをコー ルして、表示する倉庫のリストを取得します。 追加しようとしているコードは JavaScript で記述されていますが、バックグラウンドで Visualforce フレームワー クを使用しています。この機能は、JavaScript Remoting と呼ばれ、Visualforce を JavaScript で作成された動的で対話 型のページと組み合わせる場合に適しています。 1. <style> タグの後にあるコメント行 <!-- JavaScript custom code goes here --> を次のコードで 置き換えます。 <script> 53 Visualforce と Apex の連携 JavaScript を Warehouse のクエリに追加する function initialize() { var lat, lon; // If we can, get the position of the user via device geolocation if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position){ lat = position.coords.latitude; lon = position.coords.longitude; // Use Visualforce JS Remoting to query for nearby warehouses Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.WarehouseUtils.findNearbyWarehouses}', lat, lon, function(result, event){ if (event.status) { console.log(result); createMap(lat, lon, result); } else if (event.type === 'exception') { //exception case code } else { } }, {escape: true} ); }); } else { // Set default values for the map if the device // doesn't have geolocation capabilities. // This is San Francisco: lat = 37.77493; lon = -122.419416; var result = []; createMap(lat, lon, result); } } // createMap function goes here </script> 2. [Quick Save (適用)] をクリックします。 追加した JavaScript 関数は 3 つの処理を行います。 • 1 つ目は、JavaScript の navigator.geolocation 機能を使用して、ハードウェアデバイスに地理位置情報 の座標を提供できるかどうか問い合わせます。このコードが実行されると、デバイスからユーザに位置情 報の共有を許可するように要求するメッセージが表示されます。 • 2 つ目は、デバイスのクエリが成功した場合、Visualforce JavaScript Remoting を使用してリモートアクションメ ソッドをコールします。参照しているのは、コードの {!$RemoteAction.WarehouseUtils.findNearbyWarehouses} 部分で、その後にリモートアクション が要求する緯度および経度パラメータが指定されています。とても簡単で洗練された方法です。 54 Visualforce と Apex の連携 JavaScript を追加して地図を作成する • 最後は、デバイスのクエリが失敗した場合、おそらくはユーザが位置情報の共有を許可することを拒否し た場合、代わりにデフォルトの位置を定義します (ここでも Salesforce.com の本拠地であるサンフランシスコ が使用されます)。 JavaScript を追加して地図を作成する コードで近隣の倉庫のコレクションをクエリして取得したので、後は未加工データを地図に変換するだけで す。 前のステップで、コードに createMap 関数への参照がいくつかありました。次は、それを追加します。 1. </script> 終了タグの前にあるコメント行 // createMap function goes here を次のコードで置き 換えます。 function createMap(lat, lon, warehouses){ // Get the map div, and center the map at the proper geolocation var currentPosition = new google.maps.LatLng(lat,lon); var mapDiv = document.getElementById('map-canvas'); var map = new google.maps.Map(mapDiv, { center: currentPosition, zoom: 13, mapTypeId: google.maps.MapTypeId.ROADMAP }); // Set a marker for the current location var positionMarker = new google.maps.Marker({ map: map, position: currentPosition, icon: 'https://maps.google.com/mapfiles/ms/micons/green.png' }); // Keep track of the map boundary that holds all markers var mapBoundary = new google.maps.LatLngBounds(); mapBoundary.extend(currentPosition); // Set markers on the map from the @RemoteAction results var warehouse; for(var i=0; i<warehouses.length ; i++) { warehouse = warehouses[i]; console.log(warehouses[i]); setupMarker(); } // Resize map to neatly fit all of the markers map.fitBounds(mapBoundary); // setupMarker function goes here } 2. [Quick Save (適用)] をクリックします。 55 Visualforce と Apex の連携 JavaScript を追加して Warehouse マーカーを地図に追加 する この関数は、地図の中心 (ユーザの位置) の緯度と経度、およびクエリの結果を取得します。期待どおりに中心 を設定した新しい Google 地図を作成し、結果を反復処理し、setupMarker を使用して地図に追加します。残 りの処理を次の最終ステップで追加します。 JavaScript を追加して Warehouse マーカーを地図に追加する ページはもう少しで完成です。JavaScript は Apex をコールして、近隣の倉庫のリストを取得し、Google を使用し て現在位置の地図を作成しています。あとは、結果マーカーを地図に配置するだけです。 前回のコードスニペットの最後では、検出された倉庫を反復処理しながら setupMarker 関数をコールしてい ました。次に、その関数のコードを示します。 1. map.fitBounds() 関数コールの下で、終了角括弧の前にあるコメント行 // setupMarker function goes here を次のコードで置き換えます。 function setupMarker(){ var warehouseNavUrl; // Determine if we are in Salesforce1 and set navigation // link appropriately try{ if(sforce.one){ warehouseNavUrl = 'javascript:sforce.one.navigateToSObject(\'' + warehouse.Id + '\')'; } } catch(err) { console.log(err); warehouseNavUrl = '\\' + warehouse.Id; } var warehouseDetails = '<a href="' + warehouseNavUrl + '">' + warehouse.Name + '</a><br/>' + warehouse.Street_Address__c + '<br/>' + warehouse.City__c + '<br/>' + warehouse.Phone__c; // Create a panel that appears when the user clicks on the marker var infowindow = new google.maps.InfoWindow({ content: warehouseDetails }); // Add the marker to the map var marker = new google.maps.Marker({ map: map, position: new google.maps.LatLng( warehouse.Location__Latitude__s, warehouse.Location__Longitude__s) }); mapBoundary.extend(marker.getPosition()); // Add the action to open the panel when its marker is clicked 56 Visualforce と Apex の連携 まとめとコードの確認 google.maps.event.addListener(marker, 'click', function(){ infowindow.open(map, marker); }); } // page initialization goes here 2. 最後に、そのメソッドのすぐ下にあるコメント行 // page initialization goes here を次のコード で置き換えます。 // Fire the initialize function when the window loads google.maps.event.addDomListener(window, 'load', initialize); 3. [Quick Save (適用)] をクリックします。 これで地図が完成しました。 メモ: 提供されているサンプルデータの倉庫は、すべてサンフランシスコ地区に存在しています。他の場 所からページをテストする場合は、必ず自分の場所から 20 マイル以内に存在する倉庫をいくつか追加し てください。 まとめとコードの確認 これで、ブラウザのインスタンス URL (https://na15.salesforce.com/ など) に移動 し、/apex/FindNearbyWarehouses を付加すればページをテストできるようになりました。 最終的なページには、多くの JavaScript と少しの HTML が含まれています。標準の Visualforce は最小限ですが、す べてのデータアクセスは Visualforce フレームワークを通じ、JavaScript Remoting を使用して実行されました。 次がページ全体です。最終バージョンで地図が表示されない場合は参考にしてください。 <apex:page sidebar="false" showheader="false" standardController="Warehouse__c" recordSetVar="warehouses" extensions="WarehouseUtils"> <!-- Include in Google's Maps API via JavaScript static resource. This is for development convenience, not production use. See next comment. --> <apex:includeScript value="{!$Resource.GoogleMapsAPI}" /> <!-- Set YOUR_API_KEY to fix JavaScript errors in production. See https://developers.google.com/maps/documentation/javascript/tutorial for details of how to obtain a Google Maps API key. --> <!-- <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&sensor=false"> </script> --> <!-- Set the map to take up the whole window --> <style> html, body { height: 100%; } .page-map, .ui-content, #map-canvas { width: 100%; height:100%; padding: 0; } #map-canvas { height: min-height: 100%; } </style> 57 Visualforce と Apex の連携 まとめとコードの確認 <script> function initialize() { var lat, lon; // If we can, get the position of the user via device geolocation if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position){ lat = position.coords.latitude; lon = position.coords.longitude; // Use Visualforce JS Remoting to query for nearby warehouses Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.WarehouseUtils.findNearbyWarehouses}', lat, lon, function(result, event){ if (event.status) { console.log(result); createMap(lat, lon, result); } else if (event.type === 'exception') { //exception case code } else { } }, {escape: true} ); }); } else { // Set default values for the map if the device // doesn't have geolocation capabilities. // This is San Francisco: lat = 37.77493; lon = -122.419416; var result = []; createMap(lat, lon, result); } } function createMap(lat, lon, warehouses){ // Get the map div, and center the map at the proper geolocation var currentPosition = new google.maps.LatLng(lat,lon); var mapDiv = document.getElementById('map-canvas'); var map = new google.maps.Map(mapDiv, { center: currentPosition, zoom: 13, mapTypeId: google.maps.MapTypeId.ROADMAP }); // Set a marker for the current location var positionMarker = new google.maps.Marker({ map: map, position: currentPosition, 58 Visualforce と Apex の連携 まとめとコードの確認 icon: 'https://maps.google.com/mapfiles/ms/micons/green.png' }); // Keep track of the map boundary that holds all markers var mapBoundary = new google.maps.LatLngBounds(); mapBoundary.extend(currentPosition); // Set markers on the map from the @RemoteAction results var warehouse; for(var i=0; i<warehouses.length ; i++) { warehouse = warehouses[i]; console.log(warehouses[i]); setupMarker(); } // Resize map to neatly fit all of the markers map.fitBounds(mapBoundary); function setupMarker(){ var warehouseNavUrl; // Determine if we are in Salesforce1 and set navigation // link appropriately try{ if(sforce.one){ warehouseNavUrl = 'javascript:sforce.one.navigateToSObject(\'' + warehouse.Id + '\')'; } } catch(err) { console.log(err); warehouseNavUrl = '\\' + warehouse.Id; } var warehouseDetails = '<a href="' + warehouseNavUrl + '">' + warehouse.Name + '</a><br/>' + warehouse.Street_Address__c + '<br/>' + warehouse.City__c + '<br/>' + warehouse.Phone__c; // Create a panel that appears when the user clicks on the marker var infowindow = new google.maps.InfoWindow({ content: warehouseDetails }); // Add the marker to the map var marker = new google.maps.Marker({ map: map, position: new google.maps.LatLng( warehouse.Location__Latitude__s, warehouse.Location__Longitude__s) }); mapBoundary.extend(marker.getPosition()); 59 Visualforce と Apex の連携 近隣の Warehouse ページを Salesforce1 に追加する // Add the action to open the panel when its marker is clicked google.maps.event.addListener(marker, 'click', function(){ infowindow.open(map, marker); }); } } // Fire the initialize function when the window loads google.maps.event.addDomListener(window, 'load', initialize); </script> <!-- All content is rendered by the Google Maps code This minimal HTML just provides a target for GMaps to write to --> <body style="font-family: Arial; border: 0 none;"> <div id="map-canvas"></div> </body> </apex:page> 開発環境で動作したので、Salesforce1 に追加して確認します。次に進みましょう。 近隣の Warehouse ページを Salesforce1 に追加する 正常に動作する近隣の倉庫ページができたので、ページをモバイルアプリケーションに追加できます。 手順は 2 つのステップで構成されます。最初に、ページを保持するタブを作成して、Salesforce ユーザインター フェースで使用できるようにします。次に、タブを Salesforce1 ナビゲーションメニューに追加します。 ページのタブを作成する Visualforce ページは、ページを保持するタブを作成することで、Salesforce ユーザインターフェースに追加されま す。 開発者は直接 URL を使用して Visualforce ページにアクセスすることに慣れていますが、ユーザが日常的にペー ジにアクセスする方法は異なります。ユーザが必要としているのは、標準の Salesforce ユーザインターフェース からページにアクセスできることです。これを実現するには、まずページを保持する新しいタブを作成しま す。 1. [設定] で、[作成] > [タブ] をクリックします。 2. [Visualforce タブ] セクションで、[新規] をクリックします。 3. [Visualforce ページ] ドロップダウンリストから [FindNearbyWarehouses] を選択します。 4. [タブの表示ラベル] 項目に、「Nearby Warehouses」(近くの倉庫) と入力します。 フルサイトとモバイルアプリケーションの両方でこの [表示ラベル] 項目がユーザに表示されます。その点 を考慮して、表示ラベルは短くします。 5. [タブスタイル] 項目をクリックし、[地球] スタイルを選択します。 60 Visualforce と Apex の連携 モバイルナビゲーションにタブを追加する このスタイルのアイコンは、Salesforce1 モバイルアプリケーションのナビゲーションメニューのページのア イコンとして表示されます。 6. [次へ] をクリックし、[次へ] を再度クリックします。 7. サイトのデスクトップバージョンでアプリケーションにこのタブが含まれないように、[タブを含める] チェックボックスをオフにします。このタブを表示する必要があるのは、ユーザがモバイルデバイスで Salesforce1 を表示しているときのみです。 8. [保存] をクリックします。 モバイルナビゲーションにタブを追加する Visualforce ページを保持するタブを作成できたので、この新しいタブを Salesforce1 ナビゲーションメニューに追 加する準備が整いました。 このステップでは、Salesforce1 モバイルアプリケーションのナビゲーションメニュー項目としてタブを追加し ます。メニュー項目へのアクセス権を持つモバイルアプリケーションユーザは、メニュー項目を使用できるよ うになります。 1. [設定] から、[モバイル管理] > [モバイルナビゲーション] をクリックします。 2. [Nearby Warehouses (近くの倉庫)] を選択済みリストに移動し、[保存] をクリックします。 アプリケーションを試す 新しいモバイルアプリケーションが完成しました。デバイスで近隣の倉庫を検索してみましょう。 開発中にデスクトップブラウザ内でモバイルページのテストができるのは非常に便利です。ただし、もうペー ジが完成して Salesforce1 に追加されているので、ユーザが使用している実際のデバイスでテストすることが重 要です。 1. モバイルデバイスで Salesforce1 アプリケーションを開きます。プルダウンしてアプリケーションを更新しま す。 2. をタップしてナビゲーションメニューにアクセスします。 [アプリケーション] セクションの下に [Nearby Warehouses (近くの倉庫)] が表示されます。 メモ: • /one/one.app ブラウザバージョンを使用している場合、ブラウザを更新しないと、ナビゲーショ ンメニューにこのページが表示されないことがあります。 • インストールしたモバイルアプリケーションを使用している場合、ログアウトしてから再度ログイ ンしないと変更を確認できないことがあります。 3. [Nearby Warehouses (近くの倉庫)] をタップします。 61 Visualforce と Apex の連携 アプリケーションを試す 4. 現在の場所の使用を確認するメッセージが表示されたら、[OK] をクリックします。 20 マイル以内にある倉庫の場所を示す地図が表示されます。 メモ: 自分の位置を共有するためのプロンプトが表示されない場合、デバイスの設定が関係している 可能性があります。その場合は、地理的領域をデフォルトのサンフランシスコに設定してください。 62 Visualforce と Apex の連携 まとめ また、パッケージのサンプルデータの倉庫は、すべてサンフランシスコ地区に存在しています。他の 場所からこのページをテストする場合は、必ず自分の場所から 20 マイル以内に存在する倉庫をいくつ か追加してください。 まとめ 完成です。このように、とても簡単に、モバイルユーザが標準ページと標準タブを利用できるようにすること ができます。 アプリケーションは、ポイント & クリックのような操作で Salesforce1 に追加できます。 Apex コントローラを使用する Visualforce ページ このワークブックですでに学習したように、Visualforce には、組織で使用可能なすべての sObject に標準コント ローラが含まれています。標準コントローラにより、Visualforce 以外のコードを記述することなく、一般的な 機能を処理するVisualforceページを容易に作成できます。高度にカスタマイズされたアプリケーションの場合、 Visualforce では標準コントローラを拡張したり、独自の Apex コードに置き換えたりできます。Visualforce アプリ ケーションの使用を社内のみに制限したり、Web に公開したりできます。 このチュートリアルでは、Visualforce を使用して、単純な店舗ページを作成します。このページでは、販売す る商品のリストを表示し、単純なショッピングカートを提供します。また、アプリケーションとそのバックエ ンドコードで Visualforce が Apex で記述されたコントローラに接続する方法を説明します。 Visualforce ページでの商品データの表示 このレッスンでは、最初の Visualforce ページを拡張して、販売する商品のリストを表示します。このページは かなり単純に見えますが、たくさんの作業が必要なため Apex を使用するレッスンまで手早く進めましょう。 1. ブラウザで、https://<your-instance>.salesforce.com/apex/Catalog にある商品カタログページ を開き、[Create Page Catalog (ページカタログを作成)]をクリックして新しいページを作成します。ページエ ディタがまだ開いていない場合は開きます。 2. <apex:page> タグを編集して Merchandise__c 標準コントローラを有効にするようにコードを変更します。 <apex:page standardController="Merchandise__c"> これにより、ページが組み込みのコントローラを使用して Merchandise__c カスタムオブジェクトに接続され ます。このコントローラは、Merchandise__c オブジェクトの参照、更新、新規作成など、多くの基本機能を 提供します。 3. 次に、recordSetVar 属性を設定して、標準リストコントローラ定義を追加します。 <apex:page standardController="Merchandise__c" recordSetVar="products"> これにより、コントローラが Merchandise__c レコードのリストを一度に処理できるように設定されます。た とえば、カタログの商品のリストを表示できます。これが必要な作業です。 4. [Save (保存)] をクリックします。または、キーボードを使用する場合は Ctrl + S キーを押します。 63 Visualforce と Apex の連携 Visualforce ページでの商品データの表示 ページが再読み込みされ、[Merchandise] タブが表示されていれば、そのタブが選択されます。それ以外の場 合、ページに外観上の変化はありません。ただし、コントローラを使用するようにページを設定し、変数 products を定義したので、変数がページの本文で使用できるようになり、Merchandise__c レコードのリス トを表します。 5. 2 つの <apex:page> タグで囲まれたコードを、商品リストを保持する次のページブロックで置き換えま す。 <apex:pageBlock title="Our Products"> <apex:pageBlockSection> (Products Go Here) </apex:pageBlockSection> </apex:pageBlock> メモ: これ以降、最新のコードを反映したページを確認する場合は適宜変更内容を保存してください。 6. それでは実際の商品リストを追加します。(Products Go Here) プレースホルダを選択し、それを <apex:pageBlockTable> コンポーネントで置き換えます。 7. 次に、属性を pageBlockTable タグに追加する必要があります。value 属性は、pageBlockTable コン ポーネントによる反復処理の対象となる品目のリストを示します。var 属性は、1 回の反復ごとにそのリ ストの各品目を pitem 変数に割り当てます。次の属性をタグに追加します。 <apex:pageBlockTable value="{!products}" var="pitem"> 8. 次に、各列を定義し、pitem 変数の適切な項目を検索して列のデータを取得するようにします。次のコー ドを pageBlockTable の開始タグと終了タグの間に追加します。 <apex:pageBlockTable value="{!products}" var="pitem"> <apex:column headerValue="Product"> <apex:outputText value="{!pitem.Name}"/> </apex:column> </apex:pageBlockTable> 9. [Save (保存)] をクリックすると、商品リストが表示されます。 headerValue 属性によって列のヘッダータイトルが設定され、その下に各商品レコードに対して 1 行ず つ、リストが表示されます。式 {!pitem.Name} は、現在の行の Name 項目を表示することを示します。 64 Visualforce と Apex の連携 Visualforce ページでの商品データの表示 10. 最初の列の終了タグの後に、さらに次の 2 つの列を追加します。 <apex:column headerValue="Condition"> <apex:outputField value="{!pitem.Condition__c}"/> </apex:column> <apex:column headerValue="Price"> <apex:outputField value="{!pitem.Price__c}"/> </apex:column> 11. 列が 3 つになると、テーブルの幅が狭いため、リストは圧縮されます。<apex:pageBlockSection> タグ を変更して幅を広くします。 <apex:pageBlockSection columns="1"> この変更により、セクションが 2 列から 1 列に変更され、1 つの列の幅が広くなります。 12. コードは次のようになります。 <apex:page standardController="Merchandise__c" recordSetVar="products"> <apex:pageBlock title="Our Products"> <apex:pageBlockSection columns="1"> <apex:pageBlockTable value="{!products}" var="pitem"> <apex:column headerValue="Product"> <apex:outputText value="{!pitem.Name}"/> </apex:column> <apex:column headerValue="Condition"> <apex:outputField value="{!pitem.Condition__c}"/> </apex:column> <apex:column headerValue="Price"> <apex:outputField value="{!pitem.Price__c}"/> </apex:column> </apex:pageBlockTable> </apex:pageBlockSection> </apex:pageBlock> </apex:page> これで商品カタログが完成しました。 もうひとこと... • pageBlockTable コンポーネントは、行を含むテーブルを作成し、各行はリストを反復処理して出力され ます。このページに使用した標準コントローラは Merchandise__c に設定され、recordSetVar は products に設定されました。その結果、コントローラはデータベースから取得された商品レコードを使 用して商品リスト変数に自動的にデータを入力します。このリストを pageBlockTable コンポーネントが 使用します。 • リストを反復処理するときに、現在のレコードを参照する方法が必要です。var="pitem" ステートメント は、現在の行のレコードを保持する pitem という変数を割り当てます。 65 Visualforce と Apex の連携 Visualforce ページでのカスタム Apex コントローラの使 用 Visualforce ページでのカスタム Apex コントローラの使用 これで、すべての商品レコードを表示する Visualforce ページが完成しました。前のチュートリアルではデフォ ルトのコントローラを使用しましたが、今度は自分でコントローラコードを記述します。コントローラは通 常、Visualforce ページに表示するデータを取得します。また、コマンドボタンがクリックされるなどのページ アクションに応答して実行されるコードが含まれます。 このレッスンでは、標準コントローラを使用するのではなく独自のカスタム Apex コントローラを使用するよ うにページを変更します。Apexを使用するコントローラを記述すると、標準コントローラで提供される基本的 な動作以外のことも実行できます。次のレッスンでは、このコントローラを拡張し、E コマース機能を追加し てリストをオンラインストアに変更します。 新しいコントローラクラスを作成する手順は、次のとおりです。 1. [設定] で、[開発] > [Apex クラス] をクリックします。 2. [新規] をクリックします。 3. クラスの定義として次のコードを追加し、[Quick Save (適用)] をクリックします。 public class StoreFrontController { List<Merchandise__c> products; public List<Merchandise__c> getProducts() { if(products == null) { products = [SELECT Id, Name, Condition__c, Price__c FROM Merchandise__c]; } return products; } } 4. https://<your-instance>.salesforce.com/apex/Catalog にある商品カタログページに戻り、ペー ジエディタがまだ開いていない場合は開きます。 5. <apex:page> 開始タグを変更して、ページを新しいコントローラクラスにリンクします。 <apex:page controller="StoreFrontController"> 属性名が standardController から controller に変更されたことに注目してください。recordSetVar 属性は標準コントローラでのみ使用されるため、削除します。 6. [保存] をクリックして変更を保存し、ページを再読み込みします。 唯一の変化は [Merchandise] タブが選択されなくなる点です。 7. 次のコードを追加して、アプリケーションタブスタイルを Merchandise に再度設定します。 <apex:page controller="StoreFrontController" tabStyle="Merchandise__c"> 8. ページエディタのツールバーの上に、[StoreFrontController]ボタンが表示されています。このボタンをクリッ クして、ページのコントローラコードを表示し、編集します。[Catalog (カタログ)]をクリックしてVisualforce ページのコードに戻ります。 66 Visualforce と Apex の連携 Apex コントローラでの内部クラスの使用 これは次のレッスンで使用します。 もうひとこと... • 前のレッスンと同様に、pageBlockTable の値属性は {!products} に設定され、テーブルコンポーネン トが products というリストを反復処理することを示します。カスタムコントローラを使用しているの で、Visualforce は、{!products} 式を評価するとき、Apex コントローラ内でメソッド getProducts() を 自動的に検出します。 • StoreFrontController クラスは、Visualforce カタログページで必要なデータを提供するために最小限必 要な処理しか行いません。このクラスには 1 つのメソッド getProducts() が含まれ、データベースをク エリして Merchandise__c レコードのリストを返します。 • 公開インスタンス変数 (products) と getter メソッド (getProducts()) を組み合わせて初期化を行い、アク セスを可能にするのは、Apex で記述された Visualforce コントローラの一般的なパターンです。 Apex コントローラでの内部クラスの使用 前のレッスンでは、Visualforceカタログページ用のカスタムコントローラを作成しました。ただし、コントロー ラはカスタムオブジェクトをデータベースから直接ビューに渡します。これは最適な方法とはいえません。こ のレッスンでは、MVC デザインパターンをより正確に使用するようにコントローラをリファクタリングし、さ らにページにいくつか機能を追加します。 1. [StoreFrontController] をクリックして、ページのコントローラコードを編集します。 2. クラスの定義を次のように修正し、[Quick Save (適用)] をクリックします。 public class StoreFrontController { List<DisplayMerchandise> products; public List<DisplayMerchandise> getProducts() { if(products == null) { products = new List<DisplayMerchandise>(); for(Merchandise__c item : [ SELECT Id, Name, Description__c, Price__c, Total_Inventory__c FROM Merchandise__c]) { products.add(new DisplayMerchandise(item)); } } return products; } // Inner class to hold online store details for item 67 Visualforce と Apex の連携 Apex コントローラでの内部クラスの使用 public class DisplayMerchandise { private Merchandise__c merchandise; public DisplayMerchandise(Merchandise__c item) { this.merchandise = item; } // Properties for use in the Visualforce view public String name { get { return merchandise.Name; } } public String description { get { return merchandise.Description__c; } } public Decimal price { get { return merchandise.Price__c; } } public Boolean inStock { get { return (0 < merchandise.Total_Inventory__c); } } public Integer qtyToBuy { get; set; } } } 3. [Catalog (カタログ)] をクリックしてページの Visualforce コードを編集します。 4. 列定義を、新しい内部クラスのプロパティ名を処理するように変更します。既存の列定義を次のコードで 置き換えます。 <apex:column headerValue="Product"> <apex:outputText value="{!pitem.Name}"/> </apex:column> <apex:column headerValue="Condition"> <apex:outputText value="{!pitem.Condition}"/> </apex:column> <apex:column headerValue="Price"> <apex:outputText value="{!pitem.Price}"/> </apex:column> outputField コンポーネントは自動的に sObject 項目を処理しますが、カスタムクラスはまったく処理し ません。outputText はどの値も処理します。 5. [保存] をクリックして変更を保存し、ページを再読み込みします。 価格列が currency として書式設定されなくなりました。 6. 価格の outputText タグを次のコードに変更します。 <apex:outputText value="{0,number,currency}"> <apex:param value="{!pitem.Price}"/> </apex:outputText> outputText コンポーネントを使用して、異なるデータ型を自動的に書式設定できます。 68 Visualforce と Apex の連携 Apex コントローラでの内部クラスの使用 7. コードが、次のようになっていることを確認して、[保存] をクリックします。 <apex:page controller="StoreFrontController" tabStyle="Merchandise__c"> <apex:pageBlock title="Our Products"> <apex:pageBlockSection columns="1"> <apex:pageBlockTable value="{!products}" var="pitem"> <apex:column headerValue="Product"> <apex:outputText value="{!pitem.Name}"/> </apex:column> <apex:column headerValue="Condition"> <apex:outputText value="{!pitem.Condition}"/> </apex:column> <apex:column headerValue="Price" style="text-align: right;"> <apex:outputText value="{0,number,currency}"> <apex:param value="{!pitem.Price}"/> </apex:outputText> </apex:column> </apex:pageBlockTable> </apex:pageBlockSection> </apex:pageBlock> </apex:page> カタログページは次のようになります。 もうひとこと... • DisplayMerchandise クラスは、すでにデータベースに存在する Merchandise__c 型をラップし、新しいプ ロパティとメソッドを追加します。コンストラクタを使用すると、既存の Merchandise__c レコードを渡すこ とで新しい DisplayMerchandise インスタンスを作成できます。これで、インスタンス変数 products は DisplayMerchandise インスタンスのリストとして定義されます。 • getProducts() メソッドは、Merchandise__c レコードを返すクエリ (SOQL クエリをコールする角括弧内の テキスト) を実行します。その後、クエリから返されたレコードを反復処理し、DisplayMerchandise 商 品のリストに追加して、そのリストを返します。 69 Visualforce と Apex の連携 Apex コントローラへの action メソッドの追加 Apex コントローラへの action メソッドの追加 このレッスンでは、action メソッドをコントローラに追加して新しい [Add to Cart (カートに追加)] ボタンのク リックを処理できるようにし、またショッピングカートの内容を出力する新しいメソッドも追加します。 Visualforceが透過的にデータをコントローラに戻して処理できるようにする方法を確認します。Visualforce側で、 ボタンと、買い物客が入力するフォーム項目をページに追加します。 1. [StoreFrontController] をクリックして、ページのコントローラコードを編集します。 2. 次のショッピングカートコードを StoreFrontController の定義の products インスタンス変数の直後 に追加し、[Quick Save (適用)] をクリックします。 List<DisplayMerchandise> shoppingCart = new List<DisplayMerchandise>(); // Action method to handle purchasing process public PageReference addToCart() { for(DisplayMerchandise p : products) { if(0 < p.qtyToBuy) { shoppingCart.add(p); } } return null; // stay on the same page } public String getCartContents() { if(0 == shoppingCart.size()) { return '(empty)'; } String msg = '<ul>\n'; for(DisplayMerchandise p : shoppingCart) { msg += '<li>'; msg += p.name + ' (' + p.qtyToBuy + ')'; msg += '</li>\n'; } msg += '</ul>'; return msg; } これで、商品カタログに購入用ユーザインターフェースを追加できる状態になりました。 3. [Catalog (カタログ)] をクリックしてページの Visualforce コードを編集します。 4. 商品カタログを form タグでラップします。ページ構造は次のコードのようになります。 <apex:page controller="StoreFrontController"> <apex:form> <!-- rest of page code --> </apex:form> </apex:page> <apex:form> コンポーネントを使用すると、ユーザが送信したデータをページからコントローラに返送で きます。 70 Visualforce と Apex の連携 Apex コントローラへの action メソッドの追加 5. 次のコードを使用して 4 つ目の列を商品リストテーブルに追加します。 <apex:column headerValue="Qty to Buy"> <apex:inputText value="{!pitem.qtyToBuy}" rendered="{! pitem.inStock}"/> <apex:outputText value="Out of Stock" rendered="{! NOT(pitem.inStock)}"/> </apex:column> この列は、購入数量入力用のフォーム項目、または各商品の DisplayMerchandise.inStock() メソッド の値に基づいた在庫切れ通知のためのフォーム項目になります。 6. [保存] をクリックしてページを再読み込みします。 購入客が各商品の購入数量を入力するための新しい列があります。 7. 次のコードを </apex:pageBlock> タグの直前に入力して、ショッピングカートボタンを追加します。 <apex:pageBlockSection> <apex:commandButton action="{!addToCart}" value="Add to Cart"/> </apex:pageBlockSection> この段階で [保存] をクリックしてフォームを試すと、すべてが機能しますが、ショッピングカートが表示 されないため効果は確認できません。 8. 次のコードをページの </apex:form> 終了タグのすぐ上に追加します。 <apex:pageBlock title="Your Cart" id="shopping_cart"> <apex:outputText value="{!cartContents}" escape="false"/> </apex:pageBlock> 9. [保存]をクリックして、フォームを試します。ショッピングカートに品目を追加できるようになりました。 この場合、単純なテキスト表示のみです。現実のシナリオでは、注文のメール送信、Web サービスの呼び 出し、データベースの更新などが考えられます。 10. 効果を追加するために、[Add to Cart (カートに追加)] commandButton のコードを変更します。 <apex:commandButton action="{!addToCart}" value="Add to Cart" reRender="shopping_cart"/> [保存] をクリックしてフォームを使用すると、ショッピングカートがページの再読み込みではなく Ajax 経 由で更新されます。 71 Visualforce と Apex の連携 まとめ もうひとこと... • このレッスンで確認したように、Visualforce は自動的にフォームでのデータ変更を元の商品変数に複製しま す。この機能はきわめて強力で、フォームやその他の複雑な入力ページをすぐに作成できます。 • [Add to Cart (カートに追加)] ボタンをクリックすると、画面全体は更新されずに、ショッピングカートパネ ルが更新されます。これを行う Ajax 効果には、通常複雑な JavaScript 操作が必要ですが、単純な reRender 属性で実現することができました。 • [Qty to Buy (購入数量)] 項目の値を変えて [Add to Cart (カートに追加)] を複数回クリックすると、ショッピン グカートで商品が重複するというバグがあります。これまでの Apex の知識でバグを発見して修復できるで しょうか。1 つの方法として考えられるのは、特定の List を Map に変更し、重複した ID を記録してチェック できるようにすることです。このドキュメントや紹介した参考資料から、必要な Map メソッドを探してみ てください。 まとめ このチュートリアルでは、Apex コントローラクラスを使用して Visualforce ページを記述し、Warehouse アプリ ケーションのカスタムユーザインターフェースを作成しました。Visualforce ページで MVC デザインパターンを どのように使用できるか、また Apex クラスがそのパターンにどのように適合するかを確認しました。また、 送信されたフォームデータの処理、アプリケーションおよびセッションデータの管理、内部クラスを使用した 便利なメソッドの追加が簡単に行えることを確認しました。 72 まとめと今後の方向性 以上でこのワークブックは終了です。ここでは学習した内容を振り返り、今後の方向性についてご説明しま す。 このワークブックは多くの事項を網羅し、Force.com開発の真のエキスパートになるための確固たる土台を築き ます。 • Visualforce ページや Apex クラスを [設定] から見つけ、複数のツールを使って編集するなど、これらの要素の 作成や編集に必要なあらゆある操作について学習しました。 • 多様な Visualforce コンポーネントをさまざまな方法で組み合わせました。 • 標準の Visualforce と JavaScript Remoting の両方を使用して、数通りのやり方で Visualforce ページを設計してみま した。 • さらに重要な点として、Visualforce ページの用途に応じて、ページの記述のアプローチを変更する理由を学 びました。 • クラスやメソッド、テストの作成など Apex の記述の基本を習得しました。 • 標準コントローラのような強力な Visualforce ランタイム機能を活用し、さらには標準コントローラの機能が アプリケーションに適していない場合に代わりに使用できる Apex コードを記述しました。 • そして、一番重要な点として、Force.com プラットフォーム上に独自のカスタムアプリケーションを構築す る醍醐味を味わうことができました。 強力な組み込み機能、柔軟性の高いツール、多様な開発オプションにより、Visualforce および Apex を活用する キャリアにつながる道が開かれました。多くのことを習得しましたが、学ぶべきことはまだまだたくさんあり ます。 • 中でも特に、https://developer.salesforce.com/ では開発者向けのあらゆるリソースを利用できます。今すぐこの サイトをブックマークしてください。 • Visualforce 習得の次の段階は、『Visualforce 開発者ガイド』です。同書は Visualforce のあらゆる事項を学ぶ決定 的なリソースで、初級、中級、上級向けの解説とサンプルコードが示されています。また、ページやアプ リケーションで使用可能な 150 近くものVisualforceコンポーネントの完全リファレンスも記載されています。 このドキュメントは、Visualforce を意欲的に学習したい方にぴったりです。 • もっと多くのコードを学びたいと考えている方のために、多数の学習方法が用意されています。本を読ん で学習するタイプの方には、Head First Javaをお勧めします。クラス形式の公式なトレーニングを希望する方 は、「Introduction to Object-Oriented Programming with Force.com (Force.com でのオブジェクト指向プログラミング 入門)」 (ADM231)の受講をご検討ください。このクラスは、特にApexを使用したソフトウェアの作成方法の 習得を希望する Salesforce システム管理者を対象としています。 • Apex に対する理解を深める次の段階は、『Apex ワークブック』です。本書で基本事項を学ぶことはできま したが、『Apex ワークブック』には言語自体について、および Visualforce の拡張のほか、言語を使用するさ まざまな手法について詳しく説明されています。 • Visualforce と同様、Apex にも、言語について詳述する 『Force.com Apex コード開発者ガイド』があり、アプリ ケーションコードでの高度な抽象化やサービスを可能にする何百もの組み込みクラスへのリファレンスが 記載されています。 73 まとめと今後の方向性 • Salesforce1 は、作成したアプリケーションをモバイルユーザに提供する場合に非常に便利です。『Salesforce1 アプリケーション開発者ガイド』は、この画期的なプラットフォームの包括的なリソースです。 • 自身のアプリケーションを Salesforce AppExchange で販売したいとお考えの方には、Force.com プラットフォー ムを利用したアプリケーションの開発および配布について、『ISVforce ワークブック』に簡単な概要、ある いは『ISVforce ガイド』に完全リファレンスが記載されています。 リソースはまだまだたくさんあります。弊社のエンジニアや開発者マーケティングチームのブログから開発者 フォーラム、そして最先端の機能を紹介する Web セミナーやビデオまで、Force.com プラットフォームでは、 強力なクラウドベースのアプリケーションの構築を学習する充実したエコシステムを提供しています。 74
© Copyright 2024 ExpyDoc