LWCからデータベースにアクセスする方法

# LWCからデータベースにアクセスする方法

## 概要

1. Lightning データサービスを使用する方法
2. Wire Service でApexメソッドを使用する方法
3. 命令的にApexメソッドを使用する方法

## 1. Lightning データサービスを使用する方法

### 特徴

- Apexを使用しない
- レコードの操作は1件ごと
- 取得するレコードはIDで指定する

### ① レコードの取得

まず、JSファイルにgetRecordワイヤーアダプタをインポートします。また、wireデコレータもインポートします。

```javascript:js
import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
```

取得する項目を定義します。

```javascript:取得項目の定義①
const FIELDS = ['Contact.Name', 'Contact.Phone'];
```

または

```javascript:取得項目の定義②
import CONTACT_NAME_FIELD from '@salesforce/schema/Contact.Name';
import CONTACT_PHONE_FIELD from '@salesforce/schema/Contact.Phone';
const FIELDS = [CONTACT_NAME_FIELD, CONTACT_PHONE_FIELD];
```

プロパティにwireデコレータを使用して、データベースから取得したレコードの情報が格納されるようにします(メソッドに接続させることも可能です)。
取得する項目はページレイアウトで設定することも可能です([参考](https://developer.salesforce.com/docs/component-library/documentation/en/48.0/lwc/lwc.reference_wire_adapters_record))。
項目の値にアクセスする場合、`contact.data.fields.Name.value`のようになります。
また、`getFieldValue(contact.data, CONTACT_NAME_FIELD)`で取得することも可能です。

```javascript:js
import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
import CONTACT_NAME_FIELD from '@salesforce/schema/Contact.Name';
import CONTACT_PHONE_FIELD from '@salesforce/schema/Contact.Phone';

const FIELDS = [CONTACT_NAME_FIELD, CONTACT_PHONE_FIELD];

export default class WireGetRecordDynamicContact extends LightningElement {
    @api recordId;

    @wire(getRecord, { recordId: '$recordId', fields: FIELDS })
    contact;

    get name() {
        return this.contact.data.fields.Name.value;
    }

    get phone() {
        return this.contact.data.fields.Phone.value;
    }
}
```

データが取得できていなくても画面はレンダリングされます。
`lwc:if={contact.data}`を使用することで取得するまで表示させないようにできます。

```html:html
<template>
    <lightning-card icon-name="standard:contact">
        <template lwc:if={contact.data}>
            <p>★取得データ</p>
            <p>{name}</p>
            <p><lightning-formatted-phone value={phone}></lightning-formatted-phone></p>
        </template>
    </lightning-card>
</template>
```

### ② レコードの更新

updateRecordをインポートします。

```javascript:js
import { updateRecord } from 'lightning/uiRecordApi';
```

updateRecordはプロミスを返却します。
また、一件ずつしか処理できません。複数件を処理する場合は繰り返し実行する必要があります。
複数件updateする例は開発者ガイドに記載されています([参考](https://developer.salesforce.com/docs/component-library/documentation/en/48.0/lwc/data_table_inline_edit))。

```javascript:js
    handleSave(event) {
        const fields = {};
        fields['Id'] = event.detail.draftValues[0].Id;
        fields['FirstName'] = event.detail.draftValues[0].FirstName;
        fields['LastName'] = event.detail.draftValues[0].LastName;

        const recordInput = {fields};

        updateRecord(recordInput).then(() => {
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Success',
                    message: 'Contact updated',
                    variant: 'success'
                })
            );
        }).catch(error => {
            // エラー処理
        });
    }
```

## 2. Wire Service でApexメソッドを使用する方法

ワイヤーサービスを利用して、Apexメソッドから返されたデータをプロパティ、またはメソッドに割り当てます。
Apexメソッドから返されるデータは`data`と`error`のプロパティをもつオブジェクトです。Apexの戻り値はdataプロパティに格納されています。
（Spring'23からWiredプロパティがデバッグできるようになりました（[リリースノート](https://help.salesforce.com/s/articleView?id=release-notes.rn_lwc_wire_debug.htm&type=5&release=242)）。）

### 特徴

- Apexを使用する
- データの参照のみ可能。DML不可。
- 明示的にメソッドを呼び出す処理をJSに記述しなくてもデータが取得される。

### 例

#### Apexクラスの作成

LWCで呼び出すApexメソッドを作成します。
<strong>Wireサービス</strong>で使用するApexには下記が必要です。

- `static`
- `public`または`global`
- `@AuraEnabled(cacheable=true)`

サポートされる<strong>引数、戻り値</strong>は下記になります。
引数をApexメソッドに渡すには、キー(Apexメソッドの仮引数とおなじもの)と値のペアのオブジェクトが必要です。

- プリミティブ型: Boolean, Date, DateTime, Decimal, Double, Integer, Long, String.
- SObject型: 標準オブジェクト、カスタムオブジェクト
- Apexクラス（引数に使用する場合、publicなコンストラクタが必要)
- コレクション

```apex:apexコントローラ
public with sharing class LwcSampleCtrl {
    @AuraEnabled(cacheable=true)
    public static List<OrderItem> getTableData(Id orderId) {
        return [
            SELECT
                Id,
                ProductName__c, // 商品名
                TotalPrice,     // 合計金額
                SortOrder__c    // 表示順
            FROM
                OrderItem
            WHERE
                OrderId = :orderId
        ];
    }
}
```

#### javascriptを作成

作成したApexメソッドとwireデコレータをJSファイルにインポートします。
記述は`import apexMethod from '@salesforce/apex/Namespace.Classname.apexMethod';`のようになり、Namespaceが`c`の場合は不要です。

```javascript:js_apexクラスのインポート
import { LightningElement, api, wire, track } from 'lwc';
import getTableData from '@salesforce/apex/LwcSampleCtrl.getTableData';
```

次に、Apexメソッドをプロパティに接続します。
プロパティにはオブジェクトが代入されます。Apexの戻り値は代入されたオブジェクトの`data`プロパティに設定されます。読込中は空のオブジェクトが代入されます。
Apexクラスに引数を渡す必要がある場合、第2引数にオブジェクトで指定します。<font color="Red">このとき、引数が一つでも undefined だった場合、Apexメソッドはコールされません。</font>[^wired-apex1]
なお、wireで取得した値を更新したい場合は`refreshApex()`を使用します。

```javascript:js_プロパティに接続
import { LightningElement, api, wire, track } from 'lwc';
import getTableData from '@salesforce/apex/LwcSampleCtrl.getTableData';
import { refreshApex } from "@salesforce/apex";

export default class ConfirmSuccessFee extends LightningElement {
    @api recordId;

    @wire(getTableData, { orderId: '$recordId' })
    tableData;

    refresh() {
        refreshApex(this.tableData);
    }
}
```

#### htmlの作成

Apexメソッドから渡された戻り値はdataプロパティの中に格納されます。

```html:html
<template lwc:if={tableData.data}>
    <lightning-datatable
        key-field="Id"
        data={tableData.data}
        columns={columns}>
    </lightning-datatable>
</template>
```

読込中は画面に表示させたくない場合は、`lwc:if=`を使用します。

```html:html
<template lwc:if={value.data}></template>
<template lwc:if={canDisp}></template>
<!-- 
 戻り値がBooleanの場合、javascriptで下記を定義しておく。
 get canDisp() { return this.value.data !== undefined; }
-->
```

#### 補足

Apexメソッドをメソッドに接続することも可能です。
refreshApexを使用する場合、wireメソッドのresultオブジェクト自体をrefreshApexに渡す必要があります。`.data`だけを渡してもリフレッシュされません。

```javascript:js_メソッドに接続
import { LightningElement, api, wire } from 'lwc';
import getTableData from '@salesforce/apex/LwcSampleCtrl.getTableData';
import { refreshApex } from "@salesforce/apex";

export default class ConfirmSuccessFee extends LightningElement {
    @api recordId;
    data;
    wiredTableData;

    @wire(getTableData, { orderId: '$recordId'})
    getTableData(value) {
        this.wiredTableData = value;
        const { error, data } = value;
        if (data) {
            this.data = data;
        }
    }

    refresh() {
        refreshApex(this.wiredTableData);
    }
}
```


## 3. 命令的にApexメソッドを使用する方法

この方法ではワイヤーサービスは使用しません。Apexインポートしたメソッドはpromiseを返却します。

### 特徴

- Apexを使用する
- データの参照だけでなく、DML操作も可能。

### 例

#### Apexクラスの作成

LWCで呼び出すApexメソッドを作成します。

- `static`
- `public`または`global`
- `@AuraEnabled`

サポートされる引数、戻り値は下記になります。

- プリミティブ型: Boolean, Date, DateTime, Decimal, Double, Integer, Long, String.
- SObject型: 標準オブジェクト、カスタムオブジェクト
- Apexクラス
    - LWCで参照するプロパティには`AuraEnabled`と、`public`または`global`が必要
    - 引数に使用する場合、publicなコンストラクタが必要
- コレクション

```java:apex
public with sharing class LwcSampleCtrl {
    @AuraEnabled
    public static Response register(Request req) {
        // 処理
    }

    public class Request {
        @AuraEnabled
        public String name { get; set; }
        @AuraEnabled
        public String mail { get; set; }
    }

    private class Response {
        @AuraEnabled
        public Object value { get; private set; }
    }
}
```

#### javascriptの作成

作成したApexメソッドをJSファイルにインポートします。

```javascript:js
import register from '@salesforce/apex/LwcSampleCtrl.register';
```

インポートされた関数はプロミスを返します。

```java:js
async register() {
    this.showSpinner = true;
    try {
        const result = await register({ request: this.inputData });
        // 処理
    } catch (error) {
        console.error(error);
    }
    this.showSpinner = false;
}
```

## Appendix

### 現在のユーザの情報を取得する

現在のユーザのIdはインポートで取得可能です。
他の項目を取得する場合、このIDをを使用して上記のいずれかの方法でデータを取得します。

```javascript:js
import userId from '@salesforce/user/Id';
```

[開発者ガイド | 現在のユーザに関する情報の取得](https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.get_current_user)

## 参考

- Lightning データサービスを使用する方法
    - [Lightning データサービス | Developers](https://developer.salesforce.com/docs/platform/ja-jp/lwc/guide/data-ui-api.html)
    - [getRecord | Developers](https://developer.salesforce.com/docs/platform/ja-jp/lwc/guide/reference-wire-adapters-record.html)
    - [インライン編集を使用したテーブルでのデータの表示 | Developers](https://developer.salesforce.com/docs/platform/ja-jp/lwc/guide/data-table-inline-edit.html)
    - [Apex メソッド結果のクライアント側キャッシュ | Developers](https://developer.salesforce.com/docs/platform/ja-jp/lwc/guide/apex-result-caching.html)
- Wire Service でApexメソッドを使用する方法
    - [Apex メソッドのコール | Developers](https://developer.salesforce.com/docs/platform/ja-jp/lwc/guide/apex.html)
- 命令的にApexメソッドを使用する方法
    - [命令としての Apex メソッドのコール | Developers](https://developer.salesforce.com/docs/platform/ja-jp/lwc/guide/apex-call-imperative.html)

<!-- 脚注 -->
[^wired-apex1]: <https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.apex_wire_method>