#はじめに
現在Laravel × Vueを用いてサービス開発をしており、チームとしては実装担当4名でプロジェクトを進めています。開発を進めていく中で、初めのうちに決めておけばよかった!ということがあったので当記事で共有しておきます。
コーディングルールを決めておけば、プログラムの統一性が増し可読性の向上、バグの減少、命名にかける時間の削減に繋がりますので決めておくことをおすすめします。
#決めておいたほうがいいこと
- メソッドや変数の命名規則
- 各ファイルの役割
- ディレクトリ構成
- リレーションの命名規則
- routeの記述の仕方
- コントローラの使い方
- リソースコントローラでモデル結合ルート(ルートモデルバインディング)を使うかどうか
メソッドや変数の命名規則
命名規則において以下の形式がよく用いられます。
命名規則 | 書き方の例 |
---|---|
キャメルケース | userProfile |
スネークケース | user_profile |
パスカルケース(アッパーキャメル) | UserProfile |
ケバブケース | user-profile |
これはLaravelに限らずどの言語にも言えることですが、クラス名やメソッド名、変数名にそれぞれどの命名規則で記述するか、まず決めておいたほうがいいでしょう。
ちなみに今のプロジェクトではクラス名はパスカルケース、メソッドはキャメルケース、変数名はスネークケースで定義すると決めています(ただし、一部例外あり)。
中にはローカル変数に_(アンダースコア)をつけるというルールをつける場合もあります。
各ファイルの役割
LaravelはMVCのフレームワークのためContollerとModelそして、viewファイルが存在します。
基本的にこの3種類のファイルだけでもアプリケーションは作れますが、これだけだとContollerが肥大して可読性の低いfatControllerが出来上がってしまいます。より責務を分けるためにLaravelではいくつかのファイルが用意されています。
以下にいくつか紹介します。
ファイル種類 | 役割 |
---|---|
Requestクラス | リクエストパラメータ関連の処理をまとめる。バリデーションもここで行う |
MiddleWare | Controllerに行く前に行いたい処理をする。IP制限や認証制限などを行える |
Resourceクラス | APIなどでレスポンスパラメータを記述できる |
また、ビジネスロジックを記述するServiceクラスやUseCaseクラスを定義するパターンもよくあります。
いずれもControllerに書くこともできますがファイルを分けることによって、可読性が増し、また使い回ししやすく開発スピードの向上にもなります。
その際問題となるのがどのファイルにどこまで記述するのかです。
各ファイルをどのようにして使っていくのか、大枠でいいので軽くルール決めしておいたほうがいいでしょう。
あとは開発を進めながらチームでルールを追加修正していく必要が出てきます。
ディレクトリ構成
プロジェクト全体で使うファイル群を格納するディレクトリを決めます。
どのような構成にするかはプロジェクトによって異なってきますが、我々のプロジェクトでは以下の構成で作成しています。
src
└ app
├ Http
├ Controllers
├ Api
├ Auth
└ Web
├ MiddleWare
├ Requests
└ Resources
├ Model
├ User
├ User.php
└ UserProfile.php
├ Product
├ UserProduct.php
└ UserProductItem.php
└ ...
├ Services
├ UserServices.php
└ ProductServices.php
└ Util
└ Price.php
ControllerはApiとWebがあるのでそれぞれ分けています。
Modelは分けるか迷いましたが、Modelの数が増えてきたときにわかりにくくなりそうだったので今回は関心事で分けてみました。
ビジネスロジックは基本的にServiceに切り分けServicesディレクトリにまとめています。
また、プロジェクト全体で汎用的に使えるクラスをまとめるためにUtilディレクトリを用意しています。定数管理のクラスをメインにおいてます。
リレーションの命名規則
私達はここで揉めました。皆さんはリレーションをModelに定義するとき命名規則はどうしていますか?
// スネークケース
public function user_profile()
{
return $this->hasOne(UserProfile::class);
}
// パスカルケース
public function UserProfile()
{
return $this->hasOne(UserProfile::class);
}
// キャメルケース
public function userProfile()
{
return $this->hasOne(UserProfile::class);
}
// リレーション使用時
public function userProfile()
{
$user = User::find(1);
// スネークケース
$address = $user->user_profile->address;
// パスカルケース
$address = $user->UserProfile->address;
// キャメルケース
$address = $user->userProfile->address;
}
スネークケースはカラム名と同じ命名規則でアローでつなげても自然です。ですがカラムとリレーションが混合してリレーションであるとすぐに判断できないかもしれません。
パスカルケースの場合は明確にリレーションだと理解できそうです。
もし他のメソッドがキャメルケースで統一している場合はリレーションの定義もキャメルにしておけば命名規則を合わせることができます。
リレーションはLaravelの主要な機能なので、決めておいたほうがいいでしょう。
routeの記述の仕方
ルーティングを定義するweb.phpとapi.phpにも決めておいたほうがいいことがいろいろあります。
まずはグループ化です。各ルーティングをどのようにまとめるのか(もしくはまとめないか)は決めておいたほうがいいでしょう。基本的に関心事でまとめて置くと探しやすくなります。
以下はUserとAdminで分けた例です。
Route::prefix('user')->name('user.')->group(['middleware' => ['auth.api']], function () {
// user関連の各ルーティングをまとめて記述
});
Route::prefix('admin')->name('admin.')->group(['middleware' => ['admin.auth.api']], function () {
// admin関連の各ルーティングをまとめて記述
});
次にControllerの記載方法です。Laravel8からはuseを使って記述できるようになり大変便利になりました。
その際、useを使うか、namespaceをフルで書くかです。
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('/home', [HomeController::class, 'index'])->name('home');
個人的にはuseしたほうが見やすいのでuseしています。
もしUserとAdminで同じController名になってもuseでasを使えば別名で定義することができます。
コントローラの種類
コントローラの種類には「独自メソッド定義コントローラ」「リソースコントローラ」「シングルアクションコントローラ」があります。
make:controllerする際に何もオプションを付けなければ単なる「独自メソッド定義コントローラ」になります。
CRUD処理を記述するのであれば「リソースコントローラ」を使います。
さらに、アクションを一つだけ含むコントローラを定義したい場合「シングルアクションコントローラ」を使います。
どれを使用するかはそのコントローラで何をするかにもよりますが、CRUD処理のコントローラは「リソースコントローラ」を使おう!、あまりアクション数が増えると探すのが大変だから基本的には「シングルアクションコントローラ」にしよう!など、プロジェクトでルールを決めておくと統一性が増すでしょう。
リソースコントローラでモデル結合ルート(ルートモデルバインディング)を使うかどうか
普通にmake:controller UserProfileController -r
すると以下のようなファイルが生成されます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserProfileController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
注目すべきはshowやeditアクションの引数が$idになっている点です。
ここにはルーティングで指定したパラメータが入ってきます。
基本的には$idはModelのid値が入ってきますが、make時にModelを指定すれば直接Modelを注入した形でも生成できます。
php artisan make:controller UserProfileController --model=UserProfile
すると以下のようなファイルが生成されます。
<?php
namespace App\Http\Controllers;
use App\Models\UserProfile;
use Illuminate\Http\Request;
class UserProfileController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\UserProfile $userProfile
* @return \Illuminate\Http\Response
*/
public function show(UserProfile $userProfile)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\UserProfile $userProfile
* @return \Illuminate\Http\Response
*/
public function edit(UserProfile $userProfile)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\UserProfile $userProfile
* @return \Illuminate\Http\Response
*/
public function update(Request $request, UserProfile $userProfile)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\UserProfile $userProfile
* @return \Illuminate\Http\Response
*/
public function destroy(UserProfile $userProfile)
{
//
}
}
$idではなくモデルのインスタンスがはいっているのがわかるでしょう。
こちらもチームで$idにするかモデル結合ルートを使うか統一しておいたほうがいいでしょう。
私は$idにする理由はないと思っているので基本的にモデル結合ルートにしています。
#まとめ
チームで開発する際はなるべくコードを統一できるように工夫していく必要があります。
今回はLaravelプロジェクトにおいて議論した内容を記載してみました。
追加があれば随時追記していこうと思います。