【Salesforce】LWC で作る取引先責任者検索コンポーネント

## はじめに

Salesforceの取引先責任者（Contact）を検索できるLightning Web Component（LWC）を作成する方法を解説します。
このコンポーネントは、初心者の方でも理解しやすいように基本的な機能に焦点を当てています。

### 完成イメージ
- 名前/メール/電話番号での検索
- 検索結果の一覧表示（10件ずつ）
- ページネーション機能
- 選択した取引先責任者の詳細表示

![スクリーンショット 2025-02-23 22.16.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492812/a81c3a29-784b-4b0b-b497-c33684c0dd95.png)

## 1. ファイル構成

以下の4つのファイルを作成します：

```
force-app/main/default/
├── classes/
│   ├── ContactSearchController.cls          # Apexコントローラー
│   └── ContactSearchController.cls-meta.xml # メタデータ
└── lwc/contactSearch/
    ├── contactSearch.html                   # HTMLテンプレート
    ├── contactSearch.js                     # JavaScriptコントローラー
    └── contactSearch.js-meta.xml           # コンポーネントメタデータ
```

## 2. Apexコントローラーの作成

まず、検索ロジックを実装するApexクラスを作成します。

```apex:ContactSearchController.cls
/**
 * 取引先責任者検索用のApexコントローラー
 */
public with sharing class ContactSearchController {
    @AuraEnabled(cacheable=true)
    public static List<Contact> searchContacts(String searchKey, Integer pageNumber, Integer pageSize) {

        // ページネーション用のオフセット計算
        Integer offset = (pageNumber - 1) * pageSize;
        
        // 検索キーワードの処理
        String searchQuery = String.isEmpty(searchKey) ? '%' : '%' + String.escapeSingleQuotes(searchKey) + '%';
        
        // 取引先責任者の検索
        return [
            SELECT Id, Name, Email, Phone, Account.Name
            FROM Contact
            WHERE Name LIKE :searchQuery
            OR Email LIKE :searchQuery
            OR Phone LIKE :searchQuery
            ORDER BY Name
            LIMIT :pageSize
            OFFSET :offset
        ];
    }
    
    // 総件数取得用メソッド
    @AuraEnabled(cacheable=true)
    public static Integer getTotalContacts(String searchKey) {

        String searchQuery = '%' + String.escapeSingleQuotes(searchKey) + '%';
        
        return [
            SELECT COUNT()
            FROM Contact
            WHERE Name LIKE :searchQuery
            OR Email LIKE :searchQuery
            OR Phone LIKE :searchQuery
        ];
    }
}
```

### ポイント解説
- `with sharing`: 共有ルールを考慮します
- `@AuraEnabled(cacheable=true)`: LWCから呼び出し可能にします
- `Schema.sObjectType.Contact.isAccessible()`: セキュリティチェック
- `String.escapeSingleQuotes()`: SQLインジェクション対策
- `LIKE :searchQuery`: 部分一致検索
- `LIMIT`と`OFFSET`: ページネーション用

## 3. LWCの作成

### JavaScriptコントローラーのポイント解説

```javascript
// 1. インポート
import { LightningElement, track } from 'lwc';
import searchContacts from '@salesforce/apex/ContactSearchController.searchContacts';
import getTotalContacts from '@salesforce/apex/ContactSearchController.getTotalContacts';

export default class ContactSearch extends LightningElement {
    // 2. リアクティブプロパティ
    @track searchTerm = '';      // 検索キーワード
    @track contacts = [];        // 検索結果
    @track pageNumber = 1;       // 現在のページ
    @track pageSize = 10;        // 1ページの表示件数
    @track totalContacts = 0;    // 総件数
    @track loading = false;      // ローディング状態

    // 3. データテーブルの列定義
    columns = [
        { label: '名前', fieldName: 'Name', type: 'text' },
        { label: 'メール', fieldName: 'Email', type: 'email' },
        { label: '電話', fieldName: 'Phone', type: 'phone' },
        { label: '取引先', fieldName: 'AccountName', type: 'text' }
    ];

    // 4. ライフサイクルフック
    connectedCallback() {
        this.loadContacts();  // コンポーネント表示時に初期データ読み込み
    }

    // 5. 非同期データ取得
    async loadContacts() {
        try {
            this.loading = true;
            let results = await searchContacts({...});  // Apexメソッド呼び出し
            this.contacts = results.map(contact => ({
                ...contact,
                AccountName: contact.Account?.Name  // 取引先名の展開
            }));
        } finally {
            this.loading = false;
        }
    }

    // 6. イベントハンドラー
    handleSearch(event) {
        this.searchTerm = event.target.value;  // 入力値の取得
        this.pageNumber = 1;                   // ページをリセット
        this.loadContacts();                   // 検索実行
    }
}
```

### 重要なポイント：
1. **インポート**
   - `@track`デコレータ：プロパティの変更を監視
   - Apexメソッドのインポート：`@salesforce/apex/`を使用

2. **リアクティブプロパティ**
   - `@track`で修飾したプロパティは値が変更されると自動的にUIが更新
   - 初期値の設定が重要

3. **非同期処理**
   - `async/await`を使用してApexメソッドを呼び出し
   - `try/catch/finally`でエラーハンドリング

4. **データ加工**
   - `map`を使用してデータ構造を変換
   - オプショナルチェーン（`?.`）で安全にプロパティにアクセス

### HTMLテンプレートのポイント解説

```html
<template>
    <!-- 1. メインコンテナ -->
    <lightning-card title="取引先責任者検索">
        <div class="slds-p-around_medium">
            <!-- 2. 検索入力 -->
            <lightning-input
                type="search"
                onchange={handleSearch}
                value={searchTerm}>
            </lightning-input>
            
            <!-- 3. 条件付きレンダリング -->
            <template if:true={loading}>
                <lightning-spinner></lightning-spinner>
            </template>
            
            <!-- 4. データテーブル -->
            <template if:true={contacts.length}>
                <lightning-datatable
                    key-field="Id"
                    data={contacts}
                    columns={columns}>
                </lightning-datatable>
                
                <!-- 5. ページネーション -->
                <lightning-button-group>
                    <lightning-button 
                        disabled={isFirstPage}
                        onclick={handlePrevious}>
                    </lightning-button>
                </lightning-button-group>
            </template>
        </div>
    </lightning-card>
</template>
```

### 重要なポイント：
1. **Base Lightning コンポーネント**
   - `lightning-card`: カード形式のコンテナ
   - `lightning-input`: 入力フィールド
   - `lightning-datatable`: データテーブル
   - `lightning-button`: ボタン

2. **SLDS クラス**
   - `slds-p-around_medium`: パディング
   - `slds-text-align_center`: テキスト位置
   - Salesforceの標準デザインに準拠

3. **条件付きレンダリング**
   - `if:true={条件}`: 条件に応じて表示/非表示
   - `template`タグでラップ

4. **データバインディング**
   - `{プロパティ名}`: JavaScriptの値を表示
   - `onchange={ハンドラー名}`: イベント処理

5. **アクセシビリティ**
   - `alternative-text`: スクリーンリーダー対応
   - `label`: 入力フィールドのラベル

これらの機能を組み合わせることで、使いやすく保守性の高いコンポーネントを作成できます。


## 4. コンポーネントの使い方

3. Lightning アプリケーションビルダーで以下のページに追加できます：
   - ホームページ
   - アプリケーションページ
   - レコードページ

## 5. 主な機能の解説

### 検索機能
- 名前、メール、電話番号での部分一致検索
- 入力するたびに自動的に検索実行
- 検索中はローディングスピナーを表示

### ページネーション
- 1ページ10件表示
- 前へ/次へボタンでページ移動
- 現在のページ/全ページ数を表示
- 最初/最後のページでは対応するボタンを無効化

### セキュリティ
- 共有ルールの適用（with sharing）
- オブジェクトアクセス権限チェック
- SQLインジェクション対策

## まとめ

このコンポーネントは、LWCの基本的な機能を使用しながら、実用的な検索機能を提供します。
初心者の方は、このコードを基に少しずつカスタマイズを加えていくことで、LWCの理解を深めることができます。

## 参考
- [Lightning Web Components 開発者ガイド](https://developer.salesforce.com/docs/component-library/documentation/lwc)
- [Lightning Design System](https://www.lightningdesignsystem.com/)