BLOG

ブログ

2022/09/26 技術系

【Laravel / DB設計】イケてないやり方(アンチパターン)に気をつけよう

この記事を書いた人 M.Y
画像1

今回は私M.YがLaravelでのDB設計時にやらかしてしまったお話を書きたいと思います。

アンチパターンの例

前回のブログ でも少し触れたのですが、前職ではフレームワークを使用していない素のPHPで実装を行なっていました。
そのため必然的にDBからデータを取得する際も素のSQLを直接文字列で書くことになります。
その経験が生んでしまったDB設計がこちら💁(絶対に真似してはいけません⚠️)

テーブルの概要

このテーブル(= classification_table) は、同じ構造で且つマスタを持つ必要がないもの(データが固定なもの、など)をなんでもポンポン入れてしまった、ごった煮状態テーブル💧になっていました。

IDnamegroup
1大区分categories
2中区分categories
3小区分categories
4紹介contacts
5メールcontacts
6広告contacts
7北海道areas
8東北areas
9関東areas
10中部areas
11近畿areas
12中国areas
13四国areas
14九州areas
classification_table

区分(ここではgroup)の値で取得したものを配列(あるいはオブジェクト)でもち、その値の中から必要なデータ(ここではname)を他のテーブルに紐づける、と言ったイメージ。

実際、classification_table をどう利用するかというと、以下の user_table のようにもたせていました。
(さらに user_table 以外のテーブルでも classification_table が参照されるような設計になってしまっていましたが、今回は省略します)

IDnamecategory_idcontact_idarea_id
1hoge149
2fuga3514
3piyo269
user_table

user_table.category_id を設定する際は classification_table.group = categories のものから設定する、という(私の中では)よくありそうな設計でした。(user_table.contact_id, user_table. area_id でも同様)

DB設計に関する疑問

ある日、PJメンバーからとある疑問をいただきました。

『このclassificationテーブルのデータって、データベースに格納して持つ必要あるんでしょうか👀

テーブルで持つ以外だったらどうしましょうか🤔と相談させていただき、
”各モデルに定数で持たせれば良いのでは?💡”
と、例えば以下のようにしてはいかがでしょうか。とアドバイスをいただきました🙇‍♀️

// 定義の例
public const CATEGORY_LABEL = [
    self::DAI => '大区分',
    self::CHU => '中区分',
    self::SHOU => '小区分'
];

確かに、マスタデータとしてシステム側でのCRUDを準備するほどではないデータだからと、わざわざなんでもかんでもDBに持たせる必要はそもそもありませんでした。

Laravelではアクセサ前回のブログでも紹介したscopeを利用し、以下のように定義・取得することができます💡

// 取得するときの例
public function getCategoryLabelAttribute(): string
{
    return self::CATEGORY_LABEL[$this->category];
}

public function scopeCategoryDAI($query)
{
    return $query->where(function ($builder) {
        $builder->orWhere('categories', '=', self::DAI)
    });
}

なぜこんな設計をしてしまったのか

さらに、なぜテーブルにわざわざ持たせてたんだろうか?🤔というところにまで話は及びます。

その理由はこのブログの前半に書いた『DBからデータを取得する際も素のSQLを直接文字列で書く』という、自分の中での勝手な常識となってしまっていた、まさにヨロシクナイ習慣 (= アンチパターンでした😨
(Laravelで言うところの)Modelを意識していなかったため、直接SQLそのものをガリガリ書いても違和感を感じていなかったのです。

フレームワークがちゃんと準備してくれています

そもそも、現在携わっている開発で使用しているLaravelでは、素のSQLを書くことは基本的にありません。(前回のブログ参照
Laravelにはとても便利な ORM(Eloquent) が組み込まれています✨
Eloquentはデータベースとモデルを対応づける機能です。
ちゃんとEloquentモデルオブジェクトで返してくれてデータ型も担保されている上に、その後のデータの取り扱いを便利にしてくれるクエリビルダも使うことができます👍

しかし、今回のようなごった煮テーブルとなってしまった classification_table でEloquentを使うことの旨味はおそらくほぼなさそうです🤷‍♀️
しかもなんのデータが入ってるのかもぱっと見では分かりづらく、メンテナンス性も悪くなりそうです🙅‍♀️
(なんなら格納する値の型が異なった場合、 classification_table では格納することができずまた似たような構造のごった煮テーブルを作るようなことになりかねない😫)

今回の出来事で得た教訓

  1. 何が入っているかぱっと見ではわからないようなテーブルは作るべからず
  2. 何でもかんでもデータをDBに格納すればいいというものではない
  3. フレームワークの仕様に準拠したDB設計を意識すること

そんなこんなで久しぶりに アンチパターン本 と向き合おうと思いました・・・・😵‍💫

参考サイト:アンチパターン(3) 同じ構造のテーブルを一つにまとめる

今回のブログを執筆するにあたりとても参考にさせていただきました

参考サイト:Laravel 8.x Eloquentの準備

公式ドキュメントはちゃんと読まなきゃいけないなといつも反省

参考書:SQLアンチパターン

必読本の一つです!



株式会社ウイングドアは福岡のシステム開発会社です。
現在、私達と一緒に"楽しく仕事が出来る仲間"として、新卒・中途採用を絶賛募集しています!
ウイングドアの仲間達となら楽しく仕事できるかも?と興味をもった方、
お気軽にお問い合わせ下さい!

アーカイブ