Laravel でダークモード対応にする(CDNだから簡単!)

こんにちは❗フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、私も長らく開発のお仕事をさせていただき、ある種「好きなことで生きていく」というYouTuberのキャッチコピーのような状態にあります。

ただ、もちろんですが、「長く携わっている」イコール「年齢も重ねてる」という事実もあるわけで、最近「あー、これあると助かるわ😊」というものがあることに気がつきました。

それが・・・・・・

ダークモード

です。

通常ウェブサイトは、背景色が白など明るい色で、テキストが黒のコントラストが多いのですが、ダークモードはこれを反転して「背景が暗くてテキストが白い」という配色になっています。

ちなみに、なぜこんなものがあるかというと…

目が疲れにくいから

なんですね。

そうです!

目は消耗品」といわれるようにドンドン若い頃のように使えなくなるので、少しでも長持ちさせるための対処なんですね。

そこで❗

今回は、Laravel + Bootstrap でダークモードの切り替えができる実装をしてみようと思います。

ぜひ、この機能で(私を含む)世のオジサンたちに救いの手を差し伸べてあげてください(笑)

「最近は、YouTube 〜 GitHub
までALLダークモードです
これも一種の闇落ちですかね😂」

開発環境: Laravel 8.x、Bootstrap 5

前提として

今回実装するのは、各ユーザーごとに「デフォルト」と「ダークモード」を選択&保存できるようにします。

そのため、Laravelにログイン機能がインストールされていることが前提です。

もしまだの方は先に以下を参考にしてインストールし、テストユーザーを用意しておいてください。

📝 参考ページ: Laravel Breezeで「シンプルな」ログイン機能をインストール

users テーブルに配色テーマのカラムを追加する

まずは、usersテーブルに、「どの配色テーマを使うか」を管理するthemeというカラムを追加します。

以下のコマンドを実行してください。

php artisan make:migration add_theme_to_users

すると、マイグレーション・ファイルが作成されるので中身を以下のように変更します。

database/migrations/****_**_**_******_add_theme_to_users.php

// 省略

class AddThemeToUsers extends Migration
{
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('theme')
                ->default('normal')
                ->after('password')
                ->comment('配色テーマ'); // `normal` or `dark`
        });
    }

    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('theme');
        });
    }
}

変更を保存したら、実際にデータベースへ反映させましょう。
以下のコマンドを実行してください。

php artisan migrate

すると、実際のテーブルはこうなりました。

定数をつくっておく

では、今回テーマとして選ぶことができる以下2つを「どこからでも呼び出せるように」モデルに定数をつくっておきましょう。

// 省略

class User extends Authenticatable
{
    // 省略

    const THEME_NORMAL = 'normal';
    const THEME_DARK = 'dark';
    const THEMES = [
        self::THEME_NORMAL => 'ノーマル',
        self::THEME_DARK => 'ダークモード'
    ];

※ なお、これまで定数については「うーん、複雑になって全体が理解しにくくなるよね…」ということで直書きしてきましたが、やはり一気に値を変更したいことも出てくると思うので、今回は採用しました。(今後、記事の内容自体が複雑な場合は直書きするかもしれません。m(_ _)m)

View Composer をつくる

View Composerとは簡単に言うと「1ヶ所にコードを書くだけで、いろんなビューに変数を送れるよ👍✨」というものです。

今回はこれを使って「ノーマルのCSS」か「ダークモードのCSS」が自動的に切り替わるようにします。

なお、View Composer の詳しい説明は以下のページをご覧ください。

📝 参考ページ: ViewComposerをつかう

app/Providers/ViewServiceProvider.php

// 省略

class ViewServiceProvider extends ServiceProvider
{
    // 省略

    public function boot()
    {
        // どのページにも反映されます
        View::composer('*', function($view){

            $theme_css_url = 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css'; // ノーマルのCSS

            if(auth()->check()) {

                $user = auth()->user();
                $theme = $user->theme;

                if($theme === User::THEME_DARK) { // ダークモードの場合

                    $theme_css_url = 'https://cdn.jsdelivr.net/npm/bootstrap-dark-5@1.1.2/dist/css/bootstrap-night.min.css'; // Bootstrap-Night の CDN

                }

            }

            $view->with('theme_css_url', $theme_css_url);

        });
    }
}

ちなみに、今回ダークモードは Bootstrap-NightCDNで呼び出しますので、とてもシンプルに実装できると思います。

では、View Composerはファイルをつくっただけでは有効にはなりませんので、Laravel側に登録しておきましょう。

config/app.php

'providers' => [

    // 省略

    App\Providers\ViewServiceProvider::class,

],

これで、どのビューからも$theme_css_urlにアクセスすることができるようになりました。

また、$theme_css_urlの中身はユーザーのthemeで切り替わります。

コントローラーをつくる

続いて、先ほどつくったthemeの中身をユーザー自身が変更できるようにしていきます。

まずは、コントローラーです。
以下のコマンドを実行してください。

php artisan make:controller ThemeController

するとファイルが作成されるので、中身を次のようにします。

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class ThemeController extends Controller
{
    public function edit(Request $request)
    {
        $user = $request->user();
        $themes = User::THEMES; // 👈 配色テーマを定数から取得

        return view('theme.edit')->with([
            'user' => $user,
            'themes' => $themes
        ]);
    }

    public function update(Request $request)
    {
        $user = $request->user();
        $theme_keys = array_keys(User::THEMES); // 👈 配色テーマの定数から「キー」を取得

        $request->validate([
            'theme' => ['required', Rule::in($theme_keys)]
        ]);

        $user->theme = $request->theme;
        $result = $user->save();

        return ['result' => $result];
    }
}

この中で重要なのは、「配色テーマ」を先ほどセットした定数から取得している部分です。

こうしておくことで、もし今後「ノーマル」や「ダークモード」だけでなく、新しく「カラフル」のような新テーマが追加になっても既存のコードを変更する必要はなく、Userモデルに「カラフル」を追加するだけでOKです。

// 省略

class User extends Authenticatable
{
    // 省略

    const THEME_NORMAL = 'normal';
    const THEME_DARK = 'dark';
    const THEME_COLORFUL = 'colorful';
    const THEMES = [
        self::THEME_NORMAL => 'ノーマル',
        self::THEME_DARK => 'ダークモード',
        self::THEME_COLORFUL => 'カラフル', // 👈 こんなカンジですね
    ];

ビューをつくる

では、先ほどのコントローラーにセットしたtheme.editビューをつくっていきましょう。(コマンドはないので、ファイルはご自身で追加してください)

resources/views/theme/edit.blade.php

<html>
<head>
    <link href="{{ $theme_css_url }}" rel="stylesheet">
</head>
<body>
<div id="app" class="p-4">
    <h1>配色テーマの変更</h1>
    <div v-for="(name,key) in themes">
        <label>
            <input type="radio" :value="key" v-model="theme"> <span v-text="name"></span>
        </label>
    </div>
    <button type="button" class="btn btn-primary mt-3" @click="onSubmit">保存する</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue@3.1.1/dist/vue.global.prod.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script>

    Vue.createApp({
        data() {
            return {
                theme: '{{ $user->theme }}',
                themes: @json($themes)
            }
        },
        methods: {
            onSubmit() {

                if(confirm('送信します。よろしいですか?')) {

                    const url = '{{ route('user.theme.update') }}';
                    const params = {
                        theme: this.theme,
                        _method: 'PUT'
                    };

                    axios.post(url, params)
                        .then(response => {

                            if(response.data.result === true) {

                                location.reload(); // ページを再読込み

                            }

                        });

                }

            }
        }
    }).mount('#app');

</script>
</body>
</html>

この中で重要なのは、$theme_css_urlの部分です。

そうです!

ここには、先ほどのView Composerでセットした「ユーザーごとに切り替わるCSSのURL」が入っています。

そのため、このページで設定を変更し再読込みすることで「ノーマル」or「ダークモード」が自動で切り替わるようになります。

ルートをつくる

では、最後にここまでで作成したコントローラーをルートに登録して、ブラウザからアクセスできるようにしましょう。

routes/web.php

use App\Http\Controllers\ThemeController;

// 省略

Route::prefix('user/theme')->middleware('auth')->group(function(){

    Route::get('edit', [ThemeController::class, 'edit'])->name('user.theme.edit');
    Route::put('/', [ThemeController::class, 'update'])->name('user.theme.update');

});

なお、middleware('auth')で「ログインしてない人はアクセスできないようになっている」ことに注意してください。

これで作業は完了です!
お疲れ様でした😊👍✨

テストしてみる

では、実際にテストしてみましょう。

まず先にログインをしてから「http://******/user/theme/edit」へアクセスします。

すると、配色テーマの選択フォームが表示されます。

では、「ダークモード」を選択して保存ボタンをクリックしてみます。

すると・・・・・・??

はい❗自動でダークモードになりました。(もちろんノーマルに戻すことも確認しました)

成功です😊👍✨

企業様へのご提案

今回の機能をつかえば、長時間ウェブシステムを使うことになる従業員さんやクライアント様方に「目に優しい」=「疲れない」=「より使ってもらえる」システムを構築することができます。

もしこういった「見た目は小さいけれども、長い目でみると大きな差になる」サブ的な機能をご希望でしたら、ぜひお問い合わせよりご連絡ください。

どうぞよろしくお願いいたします。m(_ _)m

おわりに

ということで、今回はLaravelを使って配色テーマの切り替えを実装してみました。

正直なところ、使っているのはLaravelの基本的な技術の組み合わせだけですので、それほど難しくはないかと思います。

また、今回はモデル内に定数をつけてみました。

個人的には定数はコード量が増えますし、可読性が悪くなるので好きではないんですが、今後のメンテンナンスをしやすくするにはあったほうが楽なのは事実ですね。

ちなみに、コード中のコメントはブログ記事だからつけてますけど、私の場合は実際にはほぼ書かない(見れば分かるコードを重視しています)ですし、どうしても必要な時でも、単語が1〜3つぐらいでシンプルにで書くようにしています。

※ 確か昔「コメントは書かないほうがメリットが大きい」という海外の記事を見たことがあります。実際はどうなんでしょうね。この辺りも人によって意見が分かれるところかもしれません。

今後の課題として私も少しずつ改善していきます。

ではでは〜❗

「画像もダークモードにしてみました。
目に優しくなりますように❗」

開発のご依頼お待ちしております 😊✨
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします!
このエントリーをはてなブックマークに追加       follow us in feedly  

開発効率を上げるための機材・まとめ