Google App Makerで社内サービスを作る #1 （モデル編）

## Google App Maker で何か使えるもの作りたい

と思い、勉強を兼ねて社内の書籍管理アプリを作ってみることにしました。
同様のアプリ（技術本などの管理に利用）がすでに社内で運用されていたので、これをベースにしようと思います。
記事ではアプリ構築の手順と、作業を進める中で得られた App Maker の知見を書いていきます。

App Maker を初見で使ったとき、画面はなんとなく作れたけど、画面間のつながりとかデータ周りの処理とか、どう作っていいのか分からず結構苦労しました。
同じことで苦労されている方がいたら参考になれば、と思います。

### 作る機能

現行アプリが持っている機能は一通りおさえて、追加機能を付けたいと思っています。

#### 現行アプリの機能

* 書籍の登録
    * 書籍には、書籍名、所有者、所在、URLを設定できる
* 登録された書籍の検索
    * 書籍名と所在で検索できる
* 書籍の貸し出し状況管理
    * 貸出中なら、誰が、いつ、借りたのか確認できる

#### 追加したい機能

* 貸出履歴を残したい
    * 図書館にある（あった？）貸出カードのようなもの

## 必要なもの

* Google App Maker が利用できる Google アカウント

個人の Google アカウントでは App Maker は利用できないようです。
https://developers.google.com/appmaker/getting-started/faq?hl=ja#am_access で確認できます。

## さっそくやってみる

この記事ではまずモデルの作成から手をつけます。

https://appmaker.google.com を開きます。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/182d5400-2c71-0efc-7c5a-6330ace5ac77.png)

## モデルを作成する

モデルを作成する、とは、
データベースのテーブルを定義すること、と考えれば良いと思います。

画面左側の `DATA` → `+` を押し、次に `Google Cloud SQL` を選択 → `NEXT` します。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/e4a71617-7075-b988-7ddd-a12799fbb724.png)

次にモデルの名前を決めます。
まずは書籍を表すモデルを作るので `Model name` に `Book` と入れ、`CREATE` します。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/401dafd9-70a9-b281-3e90-656462594c01.png)

作成できました。
まだ `Id` しか無いので、必要なものを追加していきます。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/0dbe40be-9fba-3f64-dd58-1a48e223aeea.png)

書籍名を入れるフィールドを追加します。
`ADD FIELD` → `String` を選択します。
`Name` はカラム名（物理名）で、`Display Name` は UI に表示するときの表示名になります。
それぞれ `Name` 、`書籍名` と入れました。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/dfd93b27-0fa6-d606-cb93-cb5f16025333.png)

この要領で以下のフィールドも追加しました。

|型|Name|Display Name|備考|
|:-:|:-:|:-:|:-:|
|String|Author|著者|   |
|Date|BorrowedAt|貸出日時|   |
|String|BorrowedBy|借りている人|借りられていない時は空|
|String|Url|URL|公式サイトなどのURLを入れる予定|
|Date|CreatedAt|作成日時|   |
|String|CreatedBy|作成者|   |
|Date|UpdatedAt|更新日時|   |
|String|UpdatedBy|更新者|   |

こんな感じになりました。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/d18fdf57-b97e-8087-2e25-deac463275fc.png)

続けて以下のモデルも作成します。
Id や Created??, Updated?? は以下では省略していますが、全てのモデルに入れています。

**BookOwner（書籍の所有者）**

|型|Name|Display Name|備考|
|:-:|:-:|:-:|:-:|
|String|Name|書籍所有者|部署名とか個人名とかが入る|

**BorrowHistory（貸出履歴）**

|型|Name|Display Name|備考|
|:-:|:-:|:-:|:-:|
|Date|BorrowedAt|貸出日時|   |
|String|BorrowedBy|借りた人|   |
|Date|ReturnedAt|返却日時|   |

**Location（（借りられていない時の）書籍の所在地）**

|型|Name|Display Name|備考|
|:-:|:-:|:-:|:-:|
|String|Name|所在地|2Fの本棚とか支社とか|

なお各フィールドを開くと `Advanced` 欄があります。
NOT NULL や DEFAULT 制約を付けたり、カラムの型やサイズを変更したり、といった細かな設定ができます。

## リレーションを設定する

作ったモデル間のリレーションは下記を想定しています。

* BookOwner (1) --- Book (多)
* Location (1) --- Book (多)
* Book (1) --- BorrowHistory (多)

`Book` モデルの `RELATIONS` タブを選択し、`ADD RELATION` を押します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/7d7f9295-8c05-7539-35ef-0a70b6b3d698.png)

まず Book と BookOwner のリレーション設定をします。
右側で `BookOwner` と `ONE` を選択し、左側の Count は `MANY` を選択して、`CREATE` します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/70f4e4ca-bf09-9ce0-0f72-614cd95bea14.png)

これで Book と BookOwner のリレーション設定ができました。

続けて、残りのリレーションも設定します。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/33d65674-5684-9f0f-45e8-0f7e5c81090b.png)

これでリレーション設定は終わりです。

## イベント発生時の処理を設定する

特定のイベントが起きた時に動くスクリプトを設定します。
レコード毎に作成日時や更新者を保持するようにしたので、**レコードの作成、更新時に作成日時や更新日時、作成者や更新者を書き換える** 処理を自動的にするように設定します。

`Book` モデルの `EVENTS` タブを選択し、`onBeforeCreate` （レコードが作成される前に動く）を押します。

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/d4adb1f1-7325-dc85-4db0-42c60a4e4b3f.png)

ServerScript を記述する欄が現れます。
作成日時、作成者を設定するスクリプトを書きます。

```javascript:onBeforeCreate
var now = new Date();
var email = Session.getActiveUser().getEmail();
record.CreatedAt = now;
record.CreatedBy = email;
record.UpdatedAt = now;
record.UpdatedBy = email;
```

ここで `record` （新規作成されるレコード）は onBeforeCreate の引数として渡されます。
また Server Script 欄の編集内容は自動保存されます（保存の操作は不要）。画面上部に `All changes saved` と出ていれば保存されています。

続いて `onBeforeSave` （レコードが更新される前に動く） も設定します。

```javascript:onBeforeSave
record.UpdatedAt = new Date();
record.UpdatedBy = Session.getActiveUser().getEmail();
```

設定したイベントの横には :heavy_check_mark: が付きます。

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/ceef893d-c1ce-8540-fa5a-7f993ff4779e.png)

### イベントについて注意！！

ここで設定したイベント発生時のスクリプトは、レコードを **クライアント側のコードから操作した時に動きます。サーバー側コードから操作した時は動きません。**
これを知らずにサーバー側のコードでも設定されるはずだと思いこんで作業していて、１時間ほどハマりました。
公式リファレンスにきっちり書いてあるのに見落としてました。思い込みはいけないですね。

## ディレクトリモデルを作成する

ディレクトリモデルを使うと、利用するユーザーの情報を取得できます。
このシステムでは各ユーザーのメールアドレスを利用するので、ディレクトリモデルを作っておきます。

画面左側の `DATA` → `+` を選び、`Directory` を選択 → `NEXT` します。
Model name は、`User` として、`CREATE` します。
すると、最初からたくさんのフィールドが設定されたモデルが作成されます。
ユーザーのこれらフィールドの情報が取得できるということのようです。（設定されていれば）

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525650/f9437ee4-ceae-b672-086c-4a3e02850183.png)

続いて、このモデルの各フィールドに値を設定する処理を記述します。
`DATASOURCES` タブにある、`User` データソースに手を加えます。

続いて、`DATASOURCES` タブにある `User` データソースに、このモデルの各フィールドに値を設定する処理を記述します。

* Name を `CurrentUser` にする（好きな名前で）
* Query に下記のコードを記述する
    * 本データソースでこのコードが動くことで、ディレクトリモデルのフィールドを経由して、各値が取得できるようになります

```js:ServerScript
// メールアドレスを元にユーザー情報を取得するコード
var email = Session.getActiveUser().getEmail();
var query = app.models.User.newQuery();
query.filters.PrimaryEmail._equals = email;
query.limit = 1;
return query.run();
```

* Query page size を `1` にします
    * 取得件数を 1 件にしているため合わせておきます

これで CurrentUser データソースを利用すると、システムを利用中のユーザーの情報が取得できます。

## MySQL Workbench などでテーブルを見たい

特に設定していなければ、デフォルトの CloudSQL データベースにテーブルができています。
画面右上の歯車を押し、`DATABASE` タブを選んでください。
Database Key の値でスキーマが作成されています。

## ここまででモデルの作成は終わりです

モデルの作成が終わったので、次回はこれらを使って画面を作ります。

## 参考サイト

https://developers-dot-devsite-v2-prod.appspot.com/appmaker/models/overview