弁護士ドットコム Advent Calendar 2022
お急ぎの方
リポジトリを用意したので、document/
配下の coding-standards.md
を参考にしてください。
概要
プロジェクトによって色々なコーディングルールがあると思うけど、
PHP初めて触る人に PSR
に準拠してね!ってだけじゃちょっと難しい時もある。
ただ1から説明するのはめんどくさい...そういう時のためにmarkdownファイルで独自のPHPコーディングルールのテンプレを作ったので配布します。
ちなみにルールはその時に独自で決めただけで、PSRに準拠している訳ではありませんのでご注意を
コーディングを統一する時のおすすめライブラリ
-
PHP-CS-Fixer
- 余分な空白改行を削除したり、PSRに準拠した書き方に統一してくれたりする
- もちろん独自にカスタマイズも可能
- CIに設定することで細かなミスに実装者が気付けるのでレビューで細かなミスを指摘する必要がなくなる
- ちなみにライブラリとは別にエディタのプラグインでも設定すれば保存時に自動で整形してくれるよ!
- 余分な空白改行を削除したり、PSRに準拠した書き方に統一してくれたりする
-
PHPStan(LaravelならLarastan)
- 静的解析ツールでタイプヒントの誤りや変数の未定義などを解析し、対象を検知してくれる
- こちらもCIに設定する事で細かいレビューが減る
- バグの起きにくいコードが実装できる
テンプレ
Githubにリポジトリを用意したので、好きな様にご利用してください。
- ドキュメント関連
- コーディングルール
- document/coding-standars.md
- ツールの説明
- document/tools.md
- コーディングルール
- Github Actions の設定ファイル
- Laravelテスト
- .github/workflows/laravel-testing.yml
- larastan
- .github/workflows/larastan.yml
- PHP-CS-Fixer
- .github/workflows/php-cs-fixer.yml
- Laravelテスト
Docker × Laravel 環境はこちらで用意しました! Special Thanks!!
https://qiita.com/ucan-lab/items/5fc1281cd8076c8ac9f4
コーディングルール
PSR-12
に準拠
PHP コーディングルール
そもそものお約束
- 無駄な改行は入れない🙅♂️
- 無駄な空白も入れない🙅♂️
- 不必要なコメントアウトは必ず消す🙅♂️
-
==
などによる比較はダメ🙅♂️型まで判定する完全一致を使用しましょう===
declareモード
対象ファイルの型チェックが厳密になります 必ず使ってください!!
<?php
declare(strict_types=1);
use
- 改行を開けずにアルファベット順
use App\Models\User;
use Illuminate\Http\Request;
変数宣言
- イコールにはスペースを入れる(というか記号の横にはほぼ全部スペース)
$sample = 'sample';
配列
- 配列は
array()
ではなく[]
を使用する - ダブルアローにスペースを入れる
$samples = [
'tokyo' => 1,
'osaka' => 2,
];
メソッドチェーン
- メソッドチェーンにはスペースを入れない
- 複数メソッドチェーンを使用する時は、一つインデントを下げて折り返す
$this->userModel
->where('id', $id)
->get();
if文
- 記述方法は以下の通りスペースを空ける
- else は極力使わない様に心掛けて実装する(もちろん必要な場合は使ってOK)
- elseの中身が大きくなればなるほどしんどくなる
- ネストした if文 は極力使用しない(最大でも2回まで)
- これまた条件が増えた時に有名な波動拳の画像みたいになる
- 早期リターンなどを駆使して無駄な分岐を省く
-
!
は早期リターンや簡単な条件式以外では使用しない
if ($sample) {
}
以下、早期リターンの例
🙅♂️ ダメな例
if ($id) {
$this->find($id);
} else {
return null;
}
🙆♂️ ナイスな例
if (!id) {
return null;
}
$this->find($id);
型宣言
- 引数と戻り値には必ず型を書く
- 引数と戻り値のプリミティブ型(string int bool...etc)は小文字
- null許容する場合は型の前に
?
を用いる
public function sample(?int $id, string $name): ?string
{
if (!$id) {
return;
}
return `{$id} のユーザーは {$name} です`;
}
例外処理
- 例外が起きそうな箇所は必ず例外処理を入れる
if ($isFollow) {
throw new Exception('すでにフォロー済みです。');
}
PHPDoc
ただのコメントじゃないよ(静的解析で使用します)
それぞれに改行を入れよう
/**
* sampleの実装
*
* @param int|null $id
* @param string $name
*
* @return string|null
*
* @throws Exception
*/
public function sample(?int $id, string $name): ?string
{
// 省略
if ($isFollow) {
throw new Exception('すでにフォロー済みです。');
}
// 省略
}
Laravel コーディングルール
構成
-
Controller
- Controller は
単数形
+Controller
例: SampleController
- バリデーション処理は書かないこと
- Controller は
-
Model
- 基本的には以下の責務以外は書かない
- プロパティ(定数含む)
- リレーション
- DB操作(Eloquent)
- ビジネスロジックは Serviceクラス に責務を寄せます
- 基本的には以下の責務以外は書かない
-
View
- Controller名 に対して単数形でディレクトリを切ること
例: views/sample/index.blade.php
- URL を使用する際は必ず
route()
を使うこと - View からメソッドを呼び出さないこと
- なるべく if文 などの使用を避けて Controller などでうまいことできないか考える(もちろん必要な場合は使ってもOK)
- コンポーネント化できそうなところは ViewComponent を使用する
- Controller名 に対して単数形でディレクトリを切ること
-
Request
- バリデーション処理を書くこと
- それ以外でも Controller の前処理を書いてもいい
-
Service
- ビジネスロジックはここに書く(複雑な処理やリレーション間の保存を制御など)
-
ViewComposer
- 全体で共通として使用したい変数をここで定義する
- 使用する際は必ず
app/Http/ViewComposers
配下にクラスを定義してからViewComposerProvider
で使うようにすること - layoutsなど
Controller
が存在しないファイルに対して以外極力使わないようにする - View で使用する際はコメント必須(どこから呼び出せれているか分からなくなるので)
-
ViewComponent
- コンポーネント化(部品化)できそうな箇所はこちらに切り出す
-
Conroller
からView(blade)
に渡した値を加工したい時に使用する
-
web(ルーティング)
- なるべく
group()
でまとめる - メソッドチェーンで書く
-
name()
を必ず使用する
- なるべく
メソッド・コンストラクタインジェクション
- new は基本的に使用せずにメソッドインジェクションを使用する
- クラス内で2つ以上同じメソッドインジェクションをしている場合はコンストラクトに移行すること
以下、メソッドインジェクションを2つ以上使用している時の例
🙅♂️ ダメな例
public function index(User $userModel) {
return $userModel;
}
public function show(User $userModel) {
return $userModel;
}
🙆♂️ ナイスな例
private $userModel;
public function __construct(User $userModel) {
$this->userModel = $userModel;
}
public function index() {
return $this->userModel;
}
public function show() {
return $this->userModel;
}
トランザクション
- 保存や削除が保証されて欲しい箇所や複数を跨ぐ保存処理などはトランザクションを貼る
return DB::transaction(function () use ($userId) {
return $this->create([
'user_id' => $userId,
]);
});
View へ変数を渡す
-
compact
は使わない様にする- 変数宣言が必須になるのと連想配列の様にキー名を指定できないことで弊害が起きる
- 連想配列を使おう
🙅♂️ ダメな例
$items = $item->get();
$categories = $category->get();
return view('sample', compact('items', 'categories'));
🙆♂️ ナイスな例
return view('sample', [
'items' => $item->get(),
'categories' => $category->get()
]);
マイグレーションの書き方
- 親テーブル > 小テーブル > 中間テーブル の順で作成すること
-
$table->id
や$table->timestamps()
など以外は comment()を書く - 外部キーは
id
の次に書く- 外部キーは
$table->foreignId('user_id')->constrained()
の様に書く
- 外部キーは
- created_at や deleted_at などの timestamp は下の方に書く
例
public function up()
{
Schema::create('user_information', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('muttering')->nullable()->comment('一言コメント');
$table->string('icon')->nullable()->comment('プロフィールアイコン');
$table->timestamps();
});
}
定数の扱い
- Model(テーブル)と紐づくデータは
app/Models/
配下のModelに書く - グローバルで扱いたいデータは
app/Const/
に書く
例
Modelに書くパターン
class User extends Model
{
private const CLASSIFICATION = [
1 => '一般',
2 => '学生'
];
}
app/Const/GlobalConst
namespace App\Consts;
class GlobalConst
{
public const PREEFECTURE = [
'北海道',
'青森',
// 省略
]
}