はじめに
業務でLaravel×MongoDBを使う場面でMongoDBはLaravel標準ではデフォルトでサポートしていないので、MongoDBをEloquentチックに使える**laravel-mongodb** というライブラリを使っております。
その中でEloquentとはちょっと違う挙動で戸惑った時のメモです。
前提
環境は
- Laravel 8.0.17
- laravel-mongodb 3.8.4
ライブラリはインストール済みです
composer require jenssegers/mongodb
DB
今回mongoDBにあるテーブルはtalks
というテーブル
カラム名 | 型 | 説明 |
---|---|---|
_id | string | 一意のID |
body | string | 本文 |
read_at | datetime | 既読時間 |
created_at | datetime | 作成時間 |
ソースコード
モデル
App/Models/Talks.php
<?php
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model;
class Talk extends Model
{
/**
* コレクション名.
*
* @var string
*/
protected $collection = 'talks';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'body',
'read_at',
];
/**
* 保存時、取得時にMongoDateとCarbonの形式の変換を行うカラムの設定。
*
* @var array
*/
protected $dates = [
'read_at'
];
}
use Jenssegers\Mongodb\Eloquent\Model;
でライブラリを読み込んで、extendsしています
コントローラ
↓App\Controllers\TalkController.php
public function detail($id)
{
...
$talk = Talk::findOrFail($id);
$talk->whereNull('read_at')->update(['read_at' => now()]);
...
}
挙動としてはトークの詳細画面に遷移した際に、「未読状態」のトークのread_atに日時(Carbon)を入れて「既読状態」にする、といった感じ
動作確認
想定挙動
上記のコードでTalkControllerのdetailアクションを実行してみた
想定挙動はCarbonのnow()を保存しに行くので、以下の様にISODate(<現在日時>)になると思っている。
{
...
read_at: ISODate('2022-07-16T00:56:15.378Z'),
...
created_at: ISODate('2022-07-16T00:56:15.378Z')
}
実際の挙動
ただ、実際は以下のようになった
{
...
read_at: {},
...
created_at: ISODate('2022-07-16T00:56:15.378Z')
}
空のオブジェクトが入ってきた。。。
調査結果
原因
mongoDBへのupdate()
処理時だけこの挙動になるとのissueを見つけた
※どうやらsave()はnow()でも想定通りの挙動になるらしい
※そして、isssueに挙がっている挙動ではdate
として日時が保存されたオブジェクトが保存されるらしい。ちょっと自分の挙動と違ったが、一旦スルーする。
対応策
挙動は違えど、「Carbonを使っている」という共通点があったので上記issueに記載してあった「UTCDateTimeクラスを使いましょう」という助言に従い、以下の形で修正した
↓App\Controllers\TalkController.php
public function detail($id)
{
$talk->whereNull('read_at')->update(['read_at' => new UTCDateTime(new DateTime)]);
}
現在時刻が欲しいのでDateTimeクラスのインスタンスを引数にしてみた
再度detailアクションを実行してみると以下の様に想定通りの値に更新されていることを確認
{
...
read_at: ISODate('2022-07-16T00:56:15.378Z'),
...
created_at: ISODate('2022-07-16T00:56:15.378Z')
}
やったね。
所見
save()メソッドはCarbon使えるのに、updateは使えないのか…というのは意外な落とし穴でした。
mongoDBの使用パターン上、複数レコードの特定カラム(今回だとtalksのread_at)を一気に更新するって処理って結構多いはずなので、ここはCarbonでも対応できるようになってほしいなあ、と思った僕のワガママです笑
mongoDBは少し触りだしたばかりなので、引き続き気づきがあれば投稿します。
最後までお読みいただきありがとうございました。