どーも!
たかぽんです!
今回は先日LaravelのDBからある一定期間のデータを取得しようと思ったのですが、Eloquentを触りながら...
あれ?これどうやればええん・・・?
ってなったので、それについて書いていきます...!
やりたかったこと
例えば、articleテーブルに以下のようなデータがあったとします。
id | article_name | published_at_year | published_at_month |
1 | article1 | 2022 | 1 |
2 | article2 | 2022 | 10 |
3 | article3 | 2021 | 12 |
そもそもyearとmonthを分ける意味って?みたいなお話しもあるかもしれませんが、やんごとなき理由でそうしているとします...w
この際、published_at_year, published_at_monthでいい感じに期間を指定して取得できるか?というのを考えたとき、これが結構難しかったんですよ...
例えば2022/01/01 ~ 2022/12/31までのものを取得したい...!といった、年区切りの場合はなんとかなる(published_at_year = 2022)かもしれませんが、2022/04/01 ~ 2023/03/31)で判定したい場合はどうでしょうか...?
laravelだとこれに使えそうなbetweenメソッドというものがありますが...
$query->whereBetween('age', [$start, $end]);
上記のbetweenを使うと、たとえば...
$query->whereBetween('published_at_year', ['2022', '2023'])
->whereBetween('published_at_month', ['4', '3']);
こんなイメージを持つかもしれません。
ただ、これだとうまくいきません...
年と月別々で判断してしまっているので、2022年の4月 ~ 2023年の3月ではなく、2022もしくは2023年の 4 ~ 3月になってしまっています。(厳密な動作を見ていないのですが、betweenに4,3で指定するとおそらく4, 3月のみ?)
どちらにせよ、2022年の4月...というふうに併せた形で条件をする必要があります。
さて、ではこれを解決するにはどうするか...ですが...whereRawを使用して、日付をそのまま数値に変換してbetweenの比較を行います。
$startDate => '20220401';
$endDate => '20230331';
$query->whereRaw('(published_at_year * 10000 + published_at_month * 100) BETWEEN ? and ?', [$startDate, $endDate]);
上記の様な形で判定に使用する日付を2022年4月1日なら、"2022040100"といった形に変換します。
そして、whereRawの中でBetweenの対象を年が入ったpublished_at_yearの10000倍、月が入ったpublished_at_monthの100倍を足した数にします。
こうするとなにがおこるかというと...
たとえばフィルターをかける年度期間を2022/04/01 ~ 2023/03/31とします。
すると、
$startDateは'20220401', $endDateは'20230331'になります。
これで先ほどのid=1のカラムを判定しようとすると...
published_at_year=2022, published_at_month=1となるので...
published_at_year * 10000 + published_at_month * 100 = 20220100
となります。
つまり...発行されるクエリイメージとしては以下の様になります。
20220100 BETWEEN 20220401 and 20230331;
これだと、20220100は20220401より小さく、範囲外になるので、対象外になります。
id=2のものだと、
20221000 BETWEEN 20220401 and 20230331;
となり、今度は対象になります。
日付の大小は数字として日付を順番に並べたものの大小を比較すればそのまま出せるため、使える方法になりますね...!
今回は月・日までで指定したかったので、上記の様な形ですが、カラムの方に時間まであるならさらに桁数を追加すれば対応も可能です。
まとめ
今回はDBに年、月といった情報がバラバラに入ったテーブルから、一定期間のデータを抽出する方法を見ていきました..!
最初はサクッといけるだろ!と思っていましたが、思っていたより面倒でした...w
分けて管理することでこういうところで面倒くさくなるんですね...
そう考えると、分ける必要ってあるのだろうか...?
Datetime型で入れておけば後で分解はいつでもでき、カラム数が一つ分で済むので、速度的にも管理的にもそちらの方が良い気が...
必然性がないなら基本的にはDatetimeで管理した方がよいかもしれませんね...!
それでわ!