ミューテーター & キャスト
アクセサとミューテーターは、モデルの属性を取得・設定するときにその値を変換します。日付や JSON、カスタムクラスへのキャストも説明します。
アクセサとミューテーターを使うと、モデルインスタンスの Sutando 属性値を取得または設定するときに、その値を変換できます。
アクセサ & ミューテーター
アクセサの定義
アクセサを定義するには、アクセス可能な属性を表す camelCase 名の attribute{Attribute} メソッドをモデルに作成します。このメソッド名は、実際の基になるモデル属性 / データベースフィールドの表現に対応します。
この例では、first_name 属性のアクセサを定義します。first_name 属性の値を取得しようとすると、Sutando は自動的にこのアクセサを呼び出します。
import { Model, Attribute } from 'sutando';
class User extends Model {
attributeFirstName() {
return Attribute.make({
get: value => value.toUpperCase()
})
}
}
すべてのアクセサメソッドは、属性へのアクセス方法と属性の変更方法を定義した Attribute インスタンスを返します。この例では、属性にアクセスする方法だけを定義しています。そのために、Attribute クラスのコンストラクタに get パラメータを渡しています。
ご覧のとおり、カラムの元の値がアクセサに渡されるため、値を操作して返すことができます。アクセサの値にアクセスするには、モデルインスタンスの first_name 属性にアクセスするだけです。
const user = await User.query().find(1);
const firstName = user.first_name;
アクセサが複数のモデル属性を単一の「値オブジェクト」に変換する必要がある場合があります。そのためには、get クロージャで attributes の第 2 引数を受け取ります。これは自動的にクロージャに渡され、モデルの現在のすべての属性を含むオブジェクトが入ります。
attributeFullName() {
return Attribute.make({
get: (value, attributes) => `${attributes.first_name} ${attributes.last_name}`
})
}
ミューテーターの定義
ミューテーターを定義するには、属性を定義するときに set 引数を指定します。first_name 属性のミューテーターを定義しましょう。このミューテーターは、モデルの first_name 属性の値を設定しようとすると自動的に呼び出されます。
import { Model, Attribute } from 'sutando';
class User extends Model {
attributeFirstName() {
return Attribute.make({
get: value => value.toUpperCase(),
set: value => value.toLocalLowerCase()
})
}
}
ミューテーターは属性に設定されようとしている値を受け取り、値を操作して、その操作後の値を Sutando モデルの内部 attributes プロパティに設定できます。ミューテーターを使用するには、Sutando モデルの first_name 属性を設定するだけです。
const user = User.query().find(1);
user.first_name = 'Sally';
この例では、set コールバックが値 Sally で呼び出されます。ミューテーターはその名前に対して toLocalLowerCase 関数を適用し、その結果の値をモデルの内部 attributes に設定します。
複数の属性をミューテートする
ミューテーターが基になるモデルの複数の属性を設定する必要がある場合があります。そのためには、set クロージャからオブジェクトを返します。オブジェクトの各キーは、モデルに関連付けられた基になる属性 / データベースカラムに対応している必要があります。
attributeFullName() {
return Attribute.make({
get: (value, attributes) => `${attributes.first_name} ${attributes.last_name}`,
set: (value) => ({
first_name: value.split(' ')[0],
last_name: value.split(' ')[1],
}),
});
}
属性キャスト
属性キャストは、モデルに追加のメソッドを定義することなく、アクセサやミューテーターと同様の機能を提供します。代わりに、モデルの casts プロパティが、属性を一般的なデータ型に変換する便利な方法を提供します。
casts プロパティは、キーがキャストする属性の名前、値がカラムをキャストしたい型であるオブジェクトにします。サポートされているキャスト型は次のとおりです。
integerintfloatdoublestringbooleanboolcollectiondatedatetimejsonobject
属性キャストを説明するために、データベースに整数(0 または 1)として格納されている is_admin 属性をブール値にキャストしてみましょう。
import { Model } from 'sutando';
class User extends Model {
// The attributes that should be cast.
casts = {
is_admin: 'boolean',
};
}
キャストを定義すると、基になる値がデータベースに整数として格納されていても、アクセスするたびに is_admin 属性は常にブール値にキャストされます。
const user = await User.query().find(1);
if (user.is_admin) {
// ...
}
JSON キャスト
json キャストは、シリアライズされた JSON として格納されたカラムを扱うときに特に便利です。たとえば、データベースにシリアライズされた JSON を含む JSON または TEXT フィールド型がある場合、その属性に json キャストを追加すると、モデルでその属性にアクセスしたときに自動的にデシリアライズされます。
import { Model } from 'sutando';
class User extends Model {
// The attributes that should be cast.
casts = {
options: 'json',
};
}
キャストを定義すると、options 属性にアクセスでき、JSON からオブジェクトに自動的にデシリアライズされます。options 属性の値を設定すると、指定したオブジェクトは自動的に JSON にシリアライズされて格納されます。
import { Model } from 'sutando';
const user = await User.query().find(1);
const options = user.options;
options.key = value;
user.options = options;
await user.save();
日付キャスト
デフォルトでは、Sutando は created_at と updated_at カラムを Date のインスタンスにキャストします。モデルの casts プロパティに追加の日付キャストを定義することで、さらに日付属性をキャストできます。通常、日付は datetime キャスト型を使用してキャストする必要があります。
date または datetime キャストを定義するときは、日付のフォーマットも指定できます。このフォーマットはモデルがオブジェクトまたは JSON にシリアライズされるときに使用されます。
casts = {
created_at: 'datetime:YYYY-MM-DD',
};
モデルに serializeDate メソッドを定義することで、すべてのモデル日付のデフォルトのシリアライズフォーマットをカスタマイズできます。このメソッドは、データベースに格納するときの日付のフォーマットには影響しません。
import dayjs from 'dayjs';
class User extends Model {
serializeDate(date) {
return dayjs(date).format('YYYY-MM-DD');
}
}
データベース内でモデルの日付を実際に格納するときに使用するフォーマットを指定するには、モデルに dateFormat プロパティを定義する必要があります。
class User extends Model {
dateFormat = 'X'
}
利用可能なすべてのフォーマットの一覧
| フォーマット | 出力 | 説明 |
|---|---|---|
YY |
18 | 2 桁の年 |
YYYY |
2018 | 4 桁の年 |
M |
1-12 | 月(1 から始まる) |
MM |
01-12 | 月(2 桁) |
MMM |
Jan-Dec | 月名の略称 |
MMMM |
January-December | 月名の完全な名称 |
D |
1-31 | 月の日 |
DD |
01-31 | 月の日(2 桁) |
d |
0-6 | 曜日(日曜日を 0 とする) |
dd |
Su-Sa | 曜日の最小名 |
ddd |
Sun-Sat | 曜日の短縮名 |
dddd |
Sunday-Saturday | 曜日の名前 |
H |
0-23 | 時 |
HH |
00-23 | 時(2 桁) |
h |
1-12 | 時(12 時間制) |
hh |
01-12 | 時(12 時間制、2 桁) |
m |
0-59 | 分 |
mm |
00-59 | 分(2 桁) |
s |
0-59 | 秒 |
ss |
00-59 | 秒(2 桁) |
SSS |
000-999 | ミリ秒(3 桁) |
Z |
+05:00 | UTC からのオフセット、±HH:mm |
ZZ |
+0500 | UTC からのオフセット、±HHmm |
A |
AM PM | |
a |
am pm | |
Q |
1-4 | 四半期 |
Do |
1st 2nd … 31st | 序数付きの月の日 |
k |
1-24 | 時(1 から始まる) |
kk |
01-24 | 時(2 桁、1 から始まる) |
X |
1360013296 | 秒単位の Unix タイムスタンプ |
x |
1360013296123 | ミリ秒単位の Unix タイムスタンプ |
日付キャスト、シリアライズ、& タイムゾーン
デフォルトでは、date と datetime キャストは、アプリケーションのタイムゾーン設定オプションで指定されたタイムゾーンに関係なく、日付を UTC の ISO-8601 日付文字列(2012-12-12T12:25:36.000000Z)にシリアライズします。
datetime:YYYYY-MM-DD HH:mm:ss のように date または datetime キャストにカスタムフォーマットを適用した場合、日付のシリアライズ中に UTC タイムゾーンが使用されます。
カスタムキャスト
Sutando にはさまざまな便利な組み込みキャスト型がありますが、独自のキャスト型を定義する必要がある場合もあります。すべてのカスタムキャストクラスは CastsAttributes を継承します。このインターフェースを実装するクラスは get と set メソッドを定義する必要があります。get メソッドはデータベースからの生の値をキャスト値に変換する役割を担い、set メソッドはキャスト値をデータベースに格納できる生の値に変換します。例として、組み込みの json キャスト型をカスタムキャスト型として再実装します。
// casts/json.js
import { Model, CastsAttributes } from 'sutando';
class Json extends CastsAttributes {
// Cast the given value.
static get(model, key, value, attributes) {
try {
return JSON.parse(value);
} catch (e) {
return null;
}
}
// Prepare the given value for storage.
static set(model, key, value, attributes) {
return JSON.stringify(value);
}
}
カスタムキャスト型を定義したら、そのクラスを使用してモデル属性にアタッチできます。
import Json from './casts/json';
class User extends Model {
// The attributes that should be cast.
casts = {
options: Json,
};
}