A Laravel(DB関連 Eloquent) - user000422/0 GitHub Wiki
概要
Active RecordライクなORMです。 トランザクションを忘れるな。 スコープを積極的に使うこと。複雑なクエリをModelに隠蔽できる。 レコードの更新は「save」ではなく「create(update)」を使用する。簡潔だから。 Laravelでは、Daoクラスよりもリポジトリクラスとしてリソース(DBデータに限らない)操作部分をまとめる例が多いです。
マイグレーション
データベースのバージョン管理機能。
テーブル名は複数形であること。(Laravel公式)
カラム名はスネークケースかつ基本は単数形であること。(Laravel公式)
マイグレーションスクリプトファイルの場所 database/migrations
。
手順 : スクリプトファイル作成
-> スクリプトファイル編集
-> マイグレーション
# artisan マイグレーションスクリプトファイルの作成(テーブル単位)
# ファイル名規則 : create_{作成したいテーブル名}_table
php artisan make:migration create_samples_table
# マイグレーション + MySQL に必要なパッケージのインストール
dnf install php-pdo
dnf install php-mysql
# artisan マイグレーション実行
php artisan migrate
php artisan migrate:fresh # 【危険なため末尾を加工】テーブルのレコードを全て削除しマイグレーション
php artisan migrate:refreshhh # 【危険なため末尾を加工】テーブルのレコードを全て削除しマイグレーション
# artisan マイグレーション実行 単一のみマイグレーション
php artisan migrate:refresh --path=/database/migrations/2020_01_01_000000_create_users_table.php
# artisan make:model モデルを作成
php artisan make:model SampleTable
# artisan make:seeder シーダーを作成
php artisan make:seeder UserSeeder
# artisan db:seed シーダーを実行
php artisan db:seed # 一括実行(DatabaseSeederに記載したシーダー)
php artisan db:seed --class=UserSeeder # 個別指定
// マイグレーションスクリプトファイル database/migrations/~
// up : テーブル作成
public function up(): void
{
Schema::create('samples', function (Blueprint $table) {
// Nullはデフォルトで許容しない
$table->increments('id'); // 自動採番
$table->bigIncrements('id');
$table->integer('age'); // integer
$table->string('password'); // varchar
$table->integer('sample')->nullable(); // NULL許容
$table->integer('is_active')->default(1); // デフォルト値
$table->timestamps(); // カラムが2つ作成「created_at」「updated_at」
}
}
// down : テーブル削除
public function down(): void
{
// dropColumn('カラム名')だけでよい
$table->dropColumn('age');
}
Eloquent エロクワント
■Model モデルクラスの命名規則(Laravel公式) : テーブル名は複数形。モデルクラス名は単数系。 他の名前を明示的に指定しない限り、クラス名を複数形の「スネークケース」にしたものが、テーブル名として使用される。 統一するために下記のように各モデルで定義してもいいと思う。
class User extends Model
{
use HasFactory;
// 【推奨】テーブル名の設定
protected $table = 'users';
// 【推奨】主キーの設定(ここで定義(記載)しない場合は「id」)
protected $primaryKey = 'user_id';
protected $keyType = 'string'; // 主キーの型を設定(文字列の場合は必須)(要確認)
public $incrementing = false; // 主キーの自動採番設定(false:自動採番無効)デフォルトはtrue
// 【推奨】fillable : 指定カラムのupdate(insert)を許可
protected $fillable = [
'name',
'age',
];
// guarded : 指定カラムの更新(挿入)を禁止 自動採番のPrimaryKey等を記述しておく
protected $guarded = ['id'];
// created_at, updated_at の設定 使用しない場合はfalse
// protected $timestamps = false; // デフォルトはtrue
// DB操作用のメソッド定義も見かけた
public function getUser()
{
return $users;
}
}
■Controller(DB操作の例) もちろんController以外で書くこと。
use App\Models\Sample;
use Illuminate\Support\Facades\DB;
public function index()
{
// find : Modelオブジェクトを取得(PrimaryKey)
$user = User::find(10);
// Modelオブジェクトからカラムを取得
$result = $user->name;
// 基本型(分離)
$query = User::query();
$query->where('id', $id);
$query->select(
'id',
'name',
);
$user = $query->get();
// where[Column name] : レコードを取得 条件(カラム)指定
$result = Sample::whereName('taro')->get();
// where : レコードを取得 条件指定(自由)
$result = Sample::where('name', 'Sato')->get();
// count : 集計 レコード数を取得
$result = Sample::where('color', 'red')->count();
// create : レコードを追加
// Modelに定義( protected $fillable = ['name']; )が必須
// 戻り値 : 作成したObject
$result = User::create([
// カラム => 値
'name' => 'Sato Taro',
'password' => Hash::make($request->password),
]);
// update : レコードを更新
$result = Users::where('name', 'hanako')->update(['name' => 'sakura']);
// update: 実践式
// 自動で「updated_at」が更新される
$user = User::find($user_id); // まずは主キーで一意にする
$count = $user->update(['name' => 'sakura']);
// delete: 実践式
$query = User::query();
$query->where('name', $name);
$count = $query->delete();
// SQLの結果をJSONで返却したい場合
return $users->toJson();
// 【実践】SELECTの結果が0件か確認したい
// なぜこんなことをするかというと emptyが空配列をtrueとしてくれない変なバグで致命的な障害が起きたから
$user = User::query()->get();
if ($user->isEmpty()) { return null; };
}
■クエリビルダ(DB操作の例)
use Illuminate\Support\Facades\DB;
// 基本型
$users = DB::table('users')
->select('users.user_name')
->where('users.age', '>', '19')
->whereBetween('users.created_at', ['2024/01/01', '2024/12/31']) // BETWEEEN 第二引数:範囲
->whereIn('users.status', [1, 2]) // IN 第二引数:条件
->orderBy('users.user_id', 'desc')
->get();
// チェーンは分割してもよい ※公式ドキュメントより変数名「$query」
$query = DB::table('users');
$query->where('users.age', '>', '19');
$users = $query->get();
// value 値を1つのみ取得
$query = DB::table('users');
$query->where('users.id', $user_id);
$query->select('users.name');
$name = $query->value('name');
// JOIN: 実践
$result = DB::table('users')
->leftjoin('departments', 'users.department_id', '=', 'departments.id')
->where('users.id', $user_id)
->get();
// where 複数
// 配列でwhereを組む
$conditions = [
['users.id', $user_id]
];
$result = DB::table('users')
->where($conditions)
->get();
// Modelオブジェクトの操作
// 値の取得
$name = $users->name;
// ページネーション
$query = DB::table('users');
$query->where('users.age', '>', '18');
$users = $query->paginate(10); // 10レコードごとにページ切り替え
// ページネーションでページ切り替えするとテキストボックスの値が保持されない どうしたらいい
$users = $query->paginate(10)->withQueryString(); // クエリを引き継ぐ関数を追加
ローカルスコープ
複雑なSQLを簡易な記述で呼び出す。あらかじめModelに定義する。 Modelに定義しControllerから呼び出す。 関数名規則は「scope」+「用途名(頭文字は大文字)」。
// Model Class
// ローカルスコープ( 命名: scope + 用途名を大文字始まり)
public function scopeActive($query)
{
// Laravel9以降は「return」を省略推奨
$query->where('is_active', '1');
}
// Controller
public function index($query)
{
// ローカルスコープを呼び出す テーブル名::スコープ名
$result = User::active()->get();
// ローカルスコープのチェーンも便利
$result = User:::active()->ageOver20()->get();
}
has One
1対1。主テーブルに紐づく従テーブルのレコードを取得。 「LEFT JOIN」のようなイメージ。
// Model 主テーブル
public function sub()
{
// 引数: 従テーブルのモデル
return $this->hasOne(Sub::class);
}
// Controller
public function index()
{
// 主テーブル::条件->主テーブルモデルで定義したhasOne用メソッド
$result = Main::find(1)->sub;
}
Seeder
テーブルにレコードを作成する。検証等で便利。
use Illuminate\Support\Facades\DB; //追加
use Illuminate\Support\Facades\Hash; //追加
class createUserSeeder extends Seeder
{
public function run(): void
{
DB::table('users')->insert([
'user_name' => Str::random(10),
'email' => Str::random(10).'@gmail.com',
'password' => Hash::make('password'),
'password_expires_at' => '2020-01-01 00:00:00.000000',
]);
}
}
トランザクション
手動とクロージャによるトランザクションがあるがクロージャが推奨されている。 例外が発生した場合自動的にロールバックされる。
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
});
// トランザクション内部のスコープから外部の変数を扱うことはできない
// そのためuseで変数を渡す必要がある
DB::transaction(function () use ($name, $email, $password) {
DB::update('update users set votes = 1');
});
エラーハンドリング
■ページネーションの矢印が大きすぎる https://qiita.com/EasyCoder/items/192590b5605b4b267c30