Laravel 6.x でモデルを分割して肥大化を防ぐ方法
はじめに
Laravel のモデルは1ファイルで構成され、またリレーションを関数で実装することもあって、カラム数が多いテーブルでは行数が多くなりがちです。
そこで今日は Laravel 6.x で、なるべく Laravel の使用感を保ちつつ、モデルの一部を別ファイルに分割するアイディアをご紹介します。
確認に使用したのは Laravel 6.20.7 です。
- 目次
1. 下準備
動作確認用のデータベースとして articles と users を用意しました。
マイグレーションとシードは以下の通りです。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTables extends Migration
{
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->text('body');
});
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
});
}
public function down()
{
Schema::dropIfExists('articles');
Schema::dropIfExists('users');
}
}
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run()
{
DB::table('articles')->insert([
['user_id' => 1, 'body' => 'King の本文'],
['user_id' => 2, 'body' => 'Queen の本文']
]);
DB::table('users')->insert([
['id' => 1, 'name' => 'King'],
['id' => 2, 'name' => 'Queen']
]);
}
}
両ファイルとも動作確認用なのでまとめて書いていますが、実際の開発作業では1テーブルごとに分割するのがいいと考えています。
下記コマンドなどでデータベースを更新してください。
(fresh を使うと既存テーブルが削除されます。)
$ cd /path/to/my-project
$ php artisan migrate:fresh --seed
2. モデルを分割して実装
モデル分割には PHP の trait を使いました。
ファイル構成と内容は以下の通りです。
/path/to/my-project/
├ app/
│ ├ Database/
│ │ ├ Models/
│ │ │ ├ Article.php
│ │ │ └ User.php
│ │ │
│ │ ├ Relations/
│ │ │ └ ArticleRelations.php
│ │ │
│ │ ├ Scopes/
│ │ │ └ ArticleScopes.php
~~~~~
<?php
namespace App\Database\Relations;
trait ArticleRelations
{
public function user()
{
return $this->belongsTo('App\Database\Models\User', 'user_id');
}
}
<?php
namespace App\Database\Scopes;
trait ArticleScopes
{
public function scopeOwnedBy($query, $user_id)
{
return $query->where('user_id', $user_id);
}
}
<?php
namespace App\Database\Models;
use App\Database\Relations\ArticleRelations;
use App\Database\Scopes\ArticleScopes;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use ArticleRelations;
use ArticleScopes;
}
そしてリレーション用に User モデルを用意します。
<?php
namespace App\Database\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
}
これで完了です。
3. 動作確認
動作確認に使用したコントローラを置いておきます。
適宜変更してご利用ください。
<?php
namespace App\Http\Controllers;
use App\Database\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function index()
{
$article = Article::ownedBy(2)->first();
dd($article->user);
}
}
// 追加
Route::get('/article', 'ArticleController@index');
4. おわりに
Laravel は自由度が高いフレームワークなのでカスタムしやすいのですが、自由ゆえに実装方法を悩むことも多いです。
今回は特にフォルダ名(ファイル構成)で悩みました。Laravel 本体の構成と同じように「Eloquent」フォルダに入れることも考えたのですが、今回は階層が深くなるのを避けるため Database の中に入れています。
ところで、この記事ではリレーションとスコープの分割方法をご紹介しましたが、アクセサなども同様に別ファイル化が可能です。
またスコープにつきましては、もしグローバルスコープを使用する場合には、同じフォルダに混在させず、違いを分かりやすくするための工夫が必要だと考えています。
- Global Scopes (Eloquent: Getting Started - Laravel - The PHP Framework For Web Artisans)
- https://laravel.com/docs/6.x/eloquent#global-scopes