【Laravel】TailwindCSSで動的にクラスを割り当てる方法

# はじめに
Laravel環境でTailwind CSSを用いる際に、動的にクラスを割り当てる方法について書きます。
Tailwind CSSはユーティリティファーストという特徴を持つオープンソースのCSSフレームワークで、Laravelにもインストールすると使用できるようになります。
LaravelでTailwind CSSを使用すると、DBから取得した値や変数の値に応じてHTML要素に割り当てるクラス名を変化させることができます。
これによってフロントエンドのデザインを柔軟に変えることができます。
今回は、この動的にクラス名を割り当てる処理をLaravelでどう実装するのかについて考えてみました。

# 環境
Laravel v10.28.0
TailwindCSS　v3.1.0

# 動的にクラスを割り当てる方法
Laravelで動的にクラスを割り当てる方法はいくつか考えられますが、以下の２つの方法を考えました。

* **bladeファイルに直接Switch文を書く方法**
* **共通関数として呼び出す方法**

## bladeファイルに直接switch文を書く方法
ファイルの中に直接switch文で条件分岐させることで動的にクラスを割り当てます。
**<span>@</span>phpディレクティブ**を用いて、bladeファイル内に動的にクラスを生成する関数addClassを作成します。

サンプルコードは、果物の名前に応じて背景色を変更させるものです。
果物の名前はサンプルでは直接記述していますが、実際にはDBから取り出した値とかを想定しています。
```html:xxxx.blade.php
@php
  function addClass($fruit){
    switch($fruit){
      case 'リンゴ':
          return 'text-red-500';
      case 'バナナ':
          return 'text-yellow-500';
      case 'オレンジ':
          return 'text-orange-500';
    }
  }
@endphp

<p>私の好きなフルーツは<span class="{{ addClass('リンゴ') }}">リンゴ</span>です。</p>
```
実際にブラウザではこのように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3527297/8361d2bb-00e3-3e39-1cc7-326f4fd36fed.png)



bladeファイルに直接switch文を書き込む方法は、同じファイル内に記述するので実装しやすいと思います。

しかし、同様の動的にクラスを割り当てる処理を別のビューファイルでも実装したい場合、同じswitch文の処理を繰り返して記述することになってしまいます。
同じ処理を複数箇所で記述すると、コードが冗長で保守性の観点からもあまり良くないです。
また、ビューファイル自体に複雑なロジックを書き込みすぎると、MVCアーキテクチャの考え方にも反してしまいそうです。
そのような場合は、次に説明する方法がおすすめです。

## 共通関数として呼び出す方法
クラスを動的に割り当てる処理を共通関数として定義して、それをビューファイルから呼び出して使用する方法です。

まずは共通関数を作成します。
共通関数を作成する場所は任意ですが、今回は「app\Libs\Common.php」に作成します。
```php:app\Libs\Common.php
<?php

namespace App\Libs;

// 共通関数用のクラス
class Common
{
  // クラスを動的に生成する関数   
  public function addClass($status)
  { 
      switch($fruit){
        case 'リンゴ':
            return 'text-red-500';
        case 'バナナ':
            return 'text-yellow-500';
        case 'オレンジ':
            return 'text-orange-500';
      }  
  }
}
```
名前空間「App\Libs」を定義して、Commonクラスの中にクラスを動的に割り当てる関数addClassを定義します。（addClassの中の処理は１つ目の方法と同じです。）


次に、ビューファイルを作成します。
```html:xxxx.blade.php
@inject('common', 'App\Libs\Common')

<p>私の好きなフルーツは<span class="{{ addClass('リンゴ') }}">リンゴ</span>です。</p>
```
こちらのブラウザでの出力結果は１つ目の方法と同じになります。
1行目の「<span>@</span>inject('common', 'App\Libs\Common')」では、共通関数クラスCommonから生成したインスタンスをcommonという変数に代入しています。
これによって、「$common->addClass(値)」を使用すると、ビューファイル内からCommonクラスで定義した共通関数を呼び出すことができます。

このように、**<span>@</span>injectディレクティブ**を用いることで、どのビューファイルからでもクラスを動的に割り当てる処理を共通関数として呼び出すことができるので便利です。


# CSSが生成されないときに確認すべきこと
処理を実装した後にブラウザで確認すると、
「動的にクラス名を割り当てる処理まではできてるのに、なぜかCSSが生成されない...」
という場面に直面するかもしれません。
そのような場面で自分自身が実際に直面した解決方法です。
同じ悩みを抱えている人にとって助けになるかもしれません。

## クラス名は完全な形で指定する
クラス名の一部を置き換えた形で記述すると、Tailwind CSSがクラス名を認識できず、CSSが生成されない仕組みになっています。
そのため、Tailwind CSSに認識されるように、クラス名は完全な文字列で記述する必要があります。

以下の例では、$colorという変数の値をクラス名に割り当てる例です。
上のように、「text-{{ $color }}-500」としてクラス名を記述すると、Tailwind CSSは、「text-{{ $color }}-500」をクラス名として捉えてしまいます。そのようなクラス名は見つからないので結果として上手くCSSが生成されません。
CSSを生成するためには、下のように完全な文字列でクラス名を割り当てる必要があります。

```html
<!-- 上手くいかない例 (クラス名の一部分だけ動的に変更) -->
@php
    $color = 'red';
@endphp
<p class="text-{{ $color }}-500">Hello</p>

<!-- 上手くいく例 (クラス名の全文字列を動的に変更) -->
@php
    $color = 'text-red-500';
@endphp
<p class="{{ $color }}">Hello</p>
```

## ファイルパスの指定
Tailwind CSSは「tailwind.config.js」で指定したファイルを解析して、その中にあるクラス名をコンパイルします。
そのため、ここで指定するファイルパスが間違っていると、CSSが生成されません。

特に注意したいのが、**２つ目の共通関数を利用した方法**です。
この場合、bladeファイルとは別で生成したファイルの中に共通関数を定義しており、そこにTailwind CSSのクラス名を記述しています。
そのため、生成したファイル「**app\Libs\Common.php**」をファイルパスとして指定しておかないとTailwind CSSがクラス名を読み取りません。
自分が中々解決できずに、ハマってしまった要因がこれでした。

最終的にbladeファイルの中にクラス名が出力されるので、bladeファイルのパスだけを指定していれば問題ないと思っていましたが、クラス名自体が記述されているファイルを指定する必要があるので注意が必要です。

```js:tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./resources/**/*.blade.php",
    "./resources/**/*.{js,ts,jsx,tsx}",
    "./app/Libs/Common.php",
  ],
  theme: {
    extend: {
    },
  },
  plugins: [],
}
```

# まとめ
複数のbladeファイルで同じ動的にクラスを割り当てる処理を使用する際は、共通関数を定義して呼び出す方法が一番スマートに書けるので良いと思いました。
tailwind.config.jsで読み込むファイルを指定する際は注意しておきたいです。
クラス名が出力されるbladeファイルを指定しておけばいいと思っていたら間違っていました。
TailwindCSSのクラスからCSSを生成するまでの流れはしっかりと理解しておきたいですね。


---
参考サイト
[Dynamic generation of classes will not work with JIT](https://github.com/tailwindlabs/tailwindcss/discussions/6763)
[【Next.js】tailwind cssのクラスを変数を使って動的に作成したらつまづいた](https://suzumura-tumiage.com/next-js/642/)
[Dynamic Classes in Tailwind CSS](https://www.codeconcisely.com/posts/tailwind-css-dynamic-class/)