Laravel+Inertia+Vueを利用したプロジェクトでのコーディングルール

# コーディングルール

- Laravel+Inertia+Vueを利用したプロジェクトを立ち上げた時のコーディングルール備忘録
- 基本的なところだけを定めているので、チームの方針によって少し編集するだけで使えると思う
- 型定義のチェック関連に関しては、別途定義した方がわかりやすかったため、ここでは記載しない

# 共通
- フロントエンドのコードにおけるタブは2スペースで作成する（原則Tabを使用する）
- バックエンドのコードにおけるタブは4スペースで作成する（原則Tabを使用する）
- 文字列などは基本的に下手打ちせずに、定数を利用する
    - Laravelのlangディレクトリにファイルを作成する
    - 画面側の文字列も基本的にLaravelのlangを利用する

## langファイルへの文字切り出しについて
- langファイルの第一階層はテーブル名、第二階層名はカラム名に準じる
    - 最大第二階層までの階層分けとする
- DBに無い項目については、下記のような名称付けルールとする
    - ページのタイトル：title
    - ページの説明：titleDescription

## バックエンドのデータの流れと役割
1. Model：テーブルと1対1になる。テーブルの情報だけを管理（リレーションやアクセサなど）
2. Repository：テーブルに対するCRUD処理を管理
3. Service：データの加工を管理
4. Controller：viewに返却する用としてのデータ加工&返却値を管理

## サーバーサイド→フロントエンドへのデータの受け渡しについて
- 本プロジェクトにおいてはInertiaを使用して構築するため、LaravelではコントローラーからVueに値を渡し、Vue.jsはその値をPorpsとして受け取る
- この際、フロントエンドにおけるコーディング規約である変数をローワーキャメルケースで取り扱う仕様を踏襲するため、下記のようにする
    - Laravelはフロントエンドに値を渡す際、対象の変数名に関してのみローワーキャメルケースで記載する
        - ただし、lang系ファイルのように、オブジェクト（ディクショナリー系）を渡す際は、下階層まで全てローワーキャメルケースに変換するのは冗長であるため、最上階層の変数名のみローワーキャメルケースとすることで事足りることとする

### 例
``` php
return Inertia::render(
    '{ページ名}',
     [
         'title' => $title,
         ’emailPlaceholder' => $email _placeholder, //  PHPの変数名はスネークケース、Vue.jsに渡す変数名はローワーキャメルケース
         'lang' => $lang, // langには、下階層に`lang.error_message`や`lang.success_message`などが紐づくが、最上階層のみローワーキャメルケースとするため、問題ない
      ]
);
```

# サーバーサイド
## メソッドや変数の命名規則
| 対象 | 命名規則 | 書き方の例 |
|---|---| ---|
| メソッド名 | (ローワー)キャメルケース | userProfile |
| 変数名 | スネークケース | user_profile |
| 定数名 | スネークケース | API_URL |
|クラス(クラスファイル)名 | パスカルケース(アッパーキャメル) | UserProfile |
|インターフェース(インターフェースファイル)名| パスカルケース(アッパーキャメル) | UserProfile |
|viewファイル名| スネークケース | user_profile |

### リポジトリパターンにおけるインターフェースとリポジトリの命名規則について
| 対象 | 命名規則 | 書き方の例 |
|---|---| ---|
| インターフェース名 | 〇〇RepositoryInterface | UserRepositoryInterface |
| リポジトリ名 | 〇〇Repository | UserRepository |

## 各ファイルの役割
- Controllerファイルに肥大化を避けるため下記の通り定義する

| ファイル種類|役割 |
| --- |--- |
| Requestクラス | リクエストパラメータ関連の処理を記載する。バリデーションもここで行う |
| MiddleWare | Controllerに行く前に行いたい処理をする。IP制限や認証制限などを行う |
| Repositoryクラス | インターフェースに従って、データベースからユーザーデータの取得や保存などを行う |
| Serviceクラス | ユーザーに関連するビジネスロジックを実装する。リポジトリからデータを取得し、必要な処理を行う | 

## 基本ディレクトリ構成
```
src
└ app
  ├ Http
  | ├ Controllers
  | | ├ Api  // API用
  | | ├ Auth // 認証関連用
  | | ├ Web  // Web用
  | | | ├ WithToken // 認証トークンが必要な処理群
  | | | | ├ Admin  // Admin権限用の処理
  | | | | | ├ AdminUserController.php
  | | | | | └ ・・・
  | | | | ├ UserController.php // 一般権限用の処理
  | | | | └  ・・・
  | | | └ WithoutToken // 認証トークン不要な処理群
  | | | | ├  GetLogidomainInfoController.php
  | | | | └  ・・・
  | ├ MiddleWare
  | ├ Requests
  | | | ├ WithToken // 認証トークンが必要な処理群
  | | | | ├ Admin // Admin権限用の処理
  | | | | | ├ AdminUserRequest.php
  | | | | | └ ・・・
  | | | | ├ GetLogidomainInfoRequest.php
  | | | | └  ・・・
  | | | └ WithoutToken // 認証トークン不要な処理群
  | | | | ├ GetPublicLogidomainInfoRequest.php
  | | | | └  ・・・
  | | ├ User
  | | | └ UserRequest.php
  | | └ ・・・
  ├ Repositories
  | ├ User
  | | ├ UserRepository.php 
  | | └ UserRepositoryInterface.php // interface
  | └ ・・・
  ├ Models
  | ├ User.php
  | ├ Product.php
  | └ ...
  ├ Services
  | ├ User
  | | ├ UserServices.php
  | ├ Product
  | | └ ProductServices.php
  | └ ...
  |
  ├ lang // バリデーションやバックエンド/フロントエンド共通文言管理
  
```

## リレーションの命名規則
- テーブルリレーションに関してもメソッド同様に`ローワーキャメルケース`を用いて命名する

``` php
public function userProfile()
{
    $user = User::find(1);
    // ローワーキャメルケース
    $address = $user->userProfile->address;
}
```

## routeの記述法
### web.php / api.php
- 基本的に関心事でまとめるようにする
- 例として、UserとAdminで分ける場合は、下記のようになるイメージ
``` php
Route::prefix('user')->name('user.')->group(['middleware' => ['auth.api']], function () {
 // user関連の各ルーティングをまとめて記述
});
Route::prefix('admin')->name('admin.')->group(['middleware' => ['admin.auth.api']], function () {
 // admin関連の各ルーティングをまとめて記述
});


```

### Controller系
- ファイルは`namespace`をフルパスで記述数形式ではなく、`use`を利用してインポートするようにする
- ディレクトリ違いで同名のControllerが存在する場合は、`as`を利用して別名で定義する
    - ※基本的に名称がかぶることはない想定なので、厳密な定義は割愛
- 下記のようになるイメージ
``` php
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('/home', [HomeController::class, 'index'])->name('home');
```

## Controllerの種類
### コントローラの生成ルール
- 基本的に下記とする
    - CRUD機能は`リソースコントローラ`を利用する
    - ~~その他、CRUD処理以外の機能に関しては`シングルアクションコントローラ`を利用する~~

### リソースコントローラの生成
- デフォルトの各アクションの引数が`$id`になっているものではなく、直接Modelを注入する形式で生成する

``` php
php artisan make:controller UserProfileController --model=UserProfile
```

# フロントエンド
## メソッドや変数の命名規則
| 対象 | 記法名 | 例 |
|---|---| ---|
| コンポーネント名 | アッパーキャメルケース | UserForm |
| 変数名 | ローワーキャメルケース | sampleFunction |
| 定数名 | スネークケース | API_URL |
| メソッド名 | ローワーキャメルケース | addNumber |
| プロパティ名 | ローワーキャメルケース | userName |
| クラス名 | アッパーキャメルケース | MyCar |
|CSSのclass名 ※具体的な名称は[クラスの命名ルール](https://truebizon.backlog.com/alias/wiki/3100376#loom-header-18)を参照| ケバブケース| add- number |
|インターフェース(インターフェースファイル)・型定義名| パスカルケース(アッパーキャメル) | UserProfile |

## 各ファイルの役割
- ロジックの切り分けを下記の通り定義する

| ファイル種類|役割 |
| --- |--- |
| composables | 共通のロジックを抽象化し、コンポーネント固有のロジックや状態を再利用可能な形で提供 |
| components | 機能とUIを切り出した単一コンポーネント。基本的に2回以上登場する同要素はcomponentにする |
| pages | バックエンドから渡されたデータをもとにページを構成する |
| layouts | ページ構成に関して、共通の画面構成を担うベースコンポーネントを担う | 
| plugins | アプリケーション全体に対して影響を与えるロジックを管理する | 
| const | フロントエンド全体を通じて管理する定数の管理を行う | 

## 基本ディレクトリ構成
```
resources
├ js
| ├ components
| | ├ Ui
| | ├ User // User関連で使用しているコンポーネント
| | └ ・・・
| ├ composables
| | ├ Api // Api関連
| | | └ useApi.js // APIをコールする関する
| | ├ User // User情報関連
| | └ ・・・
| ├ const
| | ├ droneCompanyOptions.js // SD/DCが選択できる製造機体情報のオブジェクト
| | └ ・・・
| ├ pages
| | ├ User  // userディレクトリ配下のページ群
| | | ├ Show.vue // UserControllerのshowメソッドの内容を表示する
| | | ├ Edit.vue // UserControllerのeditメソッドの内容を表示する
| | └ ・・・
| ├ layouts
| | ├ TheSidebar.vue
| | ├ TheHeader.vue
| | └ ・・・
| ├ plugins
| | ├ GoogleMapsApi.js
| | ├ Firebase.js
| | └ ・・・
| |
| └ ・・・
├ sass
└ ・・・
```

### クラスの命名ルール
- 基本はBEM記法に則る
- ただし、ディレクトリ構成に則ってプレフィックスをつける
- 各ファイルで共通するスタイルの場合はassets/sass/base.scssに定義する

``` js
// 例えば、コンポーネント内でscoped属性をつけて定義されたスタイルのクラス名は下記のようになる
pages -> p-xxx
layout -> l-xxx
components -> c-xxx
```
- 意図１：base.scssで定義されたCSSか、コンポーネント内で定義されたCSSか一眼で見分けることができるようにするため
- 意図２：Vuetifyの利用を想定し、デフォルトでグローバル宣言するクラスとの重複を避けるため

### Vueの定義ルール
- コンポーネントはパスカル（アッパーキャメル）ケースで作成し、呼び出しもパスカル（アッパーキャメルケース）で行う
- vueのscriptタグ内は各プロパティごとにアルファベット順で並べる
    - 特にmethodsの中でもasync(非同期処理)は重要な処理のケースが多いので、methods内でも区別して上にまとめる
- vuexは基本的に使用しない
- styleは原則scopedで記載する
- scriptの記載原則setup構文で記載する
- templete, script, styleの順は下記のようにtemplateから記述する

``` js
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
```

- scriptタグ内では、下記のように、どこからどこまでがどの処理なのかわかるようにコメントを入れる
    - ※尚、記載の順番も下記の通りの順番で記載する

``` js
<script>
  /* ===============================
   * types
   * =============================== */
  type Props = {
    title: string;
  }
   
  /* ===============================
   * data ※ABC順に記載すること
   * =============================== */
  const accountId = ref("")
  const accountIdMaxLength = ref(14)
  const password = ref("")
  
  /* ===============================
   * computed
   * =============================== */
  
  /* ===============================
   * lifecycle
   * =============================== */
  onMounted(() => {
  })
  
  /* ===============================
   * methods ※ asyncは重要な処理のケースが多いので上にまとめる
   * =============================== */
   const async login = () => {
    //
  }

</script>
```

## コンポーネントの命名規則
- 原則、単一ファイルコンポーネントの名称は、すべてパスカルケース (PascalCase) とする
### 基底コンポーネントの名前
- 接頭辞として`Base`を付与する

``` js
// 例
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
```

### 単一インスタンスのコンポーネント名
- 常に1つのアクティブなインスタンスしかもたないコンポーネントは、1つしか存在しえないことを示すために `The` という接頭辞で始める
- 単一インスタンスという意味は「ページごとに 1 回しか使われない」という意味
    - アプリに対して固有となるため、`props`が設定されることはない
        - 設定される場合、それは単一インスタンスのコンポーネントではなく再利用可能なコンポーネントとなるため、本章の対象としない

``` js
components/
|- TheHeading.vue
|- TheSidebar.vue
```

## props名の型式
- props 名は、定義の時は常にキャメルケース（camelCase）を使用する
    - ※テンプレート内では、ケバブケース（kebab-case）を使用する

``` js
<WelcomeMessage greeting-text="hi"/>
```

## ディレクティブの短縮記法について
- 原則、ディレクティブは短縮記法で記載する

``` js
// v-bind
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>

// v-on
<input
  @input="onInput"
  @focus="onFocus"
>

// v-slot
<template #header>
  <h1>Here might be a page title</h1>
</template>

```

## TypeScriptの型定義に関して
 - 原則`type`を使用して定義する
 - そのファイル内でしか使用しない方は、ファイル内に記載し、複数ファイルで使い回す関数などの型については、型定義ファイルを作成する
 - 原則`any`は使用しない


## フォーム作成時の登録画面・編集画面・完了画面の生成について
- 原則、フロントエンド側は1つのviewのみを作成することとする（登録・編集・完了が同じ画面）
    - disabledやreadonlyなどで制御する
- バックエンド側も登録/編集/完了でコントローラーを分けない