[Laravel入門]TODOアプリを作成しよう(環境とモック画面作成)

Laravel

Laravel8とTailwindCSSを使って、TODOアプリを作成してみます。
Laravelに入門したい初心者さんや、どんな感じか知りたい人向けです。
対象読者としては、こちらのPHP入門の記事が大体理解できた方を対象にしています。

今回作成するものは、簡単な機能しかないWebアプリです。
機能としては下記になります。

・一覧表示をする
・終わったものにチェックをつける
・TODOを登録する
・TODOを削除する

このチュートリアルで行わないことは下記です。
Laravelの細かい内容については、別の記事にリンクを貼っていますので確認してみてください。

・Dockerの詳しい使い方
・Docker環境をdocker-composeファイルから作成する方法
・TailwindCSSのクラスについての解説
・ターミナルのコマンドの説明

開発環境はDocker(Docker Compose)を使用しています。(nginx + php + mysql + phpmyadminな感じです。)
既にPHPが動く環境があれば、そちらでも問題ありません。

完成しているものは、下記のリポジトリに置いています。
わからなくなったなどに参考にしてください。

今回はLaravelの環境作成から、見かけだけの画面表示までをやってみましょう。

プロジェクトの準備をする

Docker環境を作成するためのdocker-compose.ymlファイルを用意しています。
まずは、ローカルの好きな場所に、下記リポジトリをクローンするかダウンロードします。

$ git clone https://github.com/YasuakiHirano/laravel8-todo-list

既にTODOアプリとして、完成したものが一緒に入ってくるので削除します。

$ cd laravel8-todo-list
$ rm -rf todolist_app

環境を立ち上げて、Laravelをインストールする

Docker Desktopをインストールしておいてください。
Docker Desktopのインストールについては、下記の記事に書いています。

Dockerの用意が出来たら環境を立ち上げてみましょう。
下記のコマンドをダウンロードしたディレクトリ配下で実行すると、環境が立ち上がります。

$ cd laravel8-todo-list
$ docker-compose -f .docker_todo_list/docker-compose.yml up -d

実行が完了すると、下記のようにdoneが4つそれぞれ表示されます。

$ docker-compose -f .docker_todo_list/docker-compose.yml up -d
Creating todo_list_laravel_mysql ... done
Creating todo_list_laravel_phpmyadmin ... done
Creating todo_list_laravel_php        ... done
Creating todo_list_laravel_nginx      ... done

http://localhost:8086/にアクセスすると、phpMyAdminの画面が表示されます。
これは、MysqlのデータベースをWeb上から使うためのアプリケーションです。

(日本語になっていない場合は、トップのLangageから日本語に設定可能です)

http://localhost:8085/にアクセスすると、ローカルで作成したアプリにアクセスできます。
現状は何もないので、このように表示されます。

Laravelをインストールして、プロジェクトを作成します。
まず、下記のコマンドでDockerのPHPコンテナに入ります。

$ docker-compose -f .docker_todo_list/docker-compose.yml exec php /bin/bash

PHPコンテナに入ると、先頭が「#」から始まるようになります。
下記のコマンドを実行して、Laravelのプロジェクトを作成します。

# composer create-project laravel/laravel todolist_app

インストールが完了して、ディレクトリが作成されるまで待ちます。

完了したらコンテナから出ましょう。
exitコマンドを打つと入っているコンテナから抜けれます。

# exit

todolist_appというディレクトリが作成されて、Laravel関連のファイルがその中に入っています。
ここまで来たら、Laravelの準備が完了したので画面を確認してみましょう。

http://localhost:8085/にアクセスします。
このようにウェルカム画面が表示されました。

これで、Laravelの準備ができました。
次はフロントを作成するためにTailwind CSSの設定をしましょう。

Tailwind CSSをインストールする

公式のドキュメントはこちらです。ここに書いてある設定を行います。

まず、コンテナに入ります。

$ docker-compose -f .docker_todo_list/docker-compose.yml exec php /bin/bash

プロジェクトのディレクトリに移動して、npmコマンドを使用して必要なモジュールをインストールします。

# cd todolist_app/
# npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

モジュールのインストールが終わったら、次に下記のコマンドを実行します。
コマンドを実行するとtailwind.config.jsファイルを作成してくれます。(tailwindの設定ファイルです。)

# npx tailwindcss init

コマンドを実行後に、作成したtailwind.config.jsを開いて、下記のように編集します。
purgeの項目として、3行追加しています。

module.exports = {
  purge: [
     './resources/**/*.blade.php',
     './resources/**/*.js',
     './resources/**/*.vue',
  ],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

終わったら、次にwebpack.mix.jsを修正して、下記のようにします。
webpack.mix.jsファイルはプロジェクトのディレクトリ直下にあります。

mix.js("resources/js/app.js", "public/js")
    .postCss("resources/css/app.css", "public/css", [
        require("tailwindcss"),
    ]);

resources/css/app.cssの先頭に下記の項目を追加します。

@tailwind base;
@tailwind components;
@tailwind utilities;

CSSへの追加が終わったら、下記コマンドを実行します。
npm installでフロント関連の必要なパッケージをインストールします。 (時間がかかるので待ちましょう)

npm run devで設定した内容を含めて、cssファイルを作成します。

# npm install
# npm run dev

TailwindCSSで使うクラスを含んだファイルがcss/app.cssに作成されます。
これを読み込んで、フロントの画面を作成します。

それでは、Laravelのウェルカム画面をTODOリストに変えてみましょう。
とりあえず、モックを作成していきます。(静的な画面)

ルーティングを作成する

Laravelのルーティングについては、下記を参照してください。

ルートを作成して、「/」にアクセスされた場合にモックのTODOリストを表示します。
routes/web.phpを下記のように編集します。

Route::get('/', function () {
    return view('todolist');
});

welcomeの画面を指定していたところをtodolistに変更しました。
まだ画面のファイルがないので、アクセスするとエラーになります。

このようにRoute::getを使用することで、HTTPのGET方式のアクセスに対応できます。
第1引数にURLで、第2引数に処理を書きます。

詳しくは、先ほどのルーティングについてのリンクを参照してください。

Tailwind CSSを使用したTODOリストを作成する

resources/viewstodolist.blade.phpというファイルを下記のように作成してください。
既にwelcome.blade.phpというファイルがあるかと思いますので、同階層に置きます。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
        <title>todo</title>
    </head>
    <body>
        <ul class="flex p-3 mb-6 bg-blue-600">
          <li class="mr-6 text-white text-2xl flex">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" viewBox="0 0 20 20" fill="currentColor">
                <path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z" />
                <path fill-rule="evenodd" d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z" clip-rule="evenodd" />
            </svg>
            Todo App
          </li>
        </ul>
        <div class="w-3/5 flex mb-10 m-auto">
            <input type="text" name="hoge" placeholder="TODOを入力する" class="flex-auto w-32 py-2 px-2 rounded-md border-blue-500 hover:border-blue-700 ring-2 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500" />
            <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer h-10 w-10 ml-5 py-2 shadow-md rounded-md font-semibold text-white text-base bg-blue-500 hover:bg-blue-700 ring-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
            </svg>
        </div>
        <ul class="w-4/5 m-auto">
            <li class="block py-3 border-b-2 border-gray-200 flex justify-between">
                <div class="flex">
                    <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer ml-3 h-8 w-8 mr-3 py-1 shadow-md rounded-md font-semibold text-white text-base bg-gray-500 hover:bg-gray-700 ring-gray-200 ring-2" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
                    </svg>
                    <span class="text-2xl">
                        todo1
                    </span>
                </div>
                <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer mr-3 h-8 w-8 py-1 shadow-md rounded-md font-semibold text-white text-base bg-red-500 hover:bg-red-700 ring-2 ring-red-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
                </svg>
            </li>
            <li class="block py-3 border-b-2 border-gray-200 flex justify-between">
                <div class="flex">
                    <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer ml-3 h-8 w-8 mr-3 py-1 shadow-md rounded-md font-semibold text-white text-base bg-gray-500 hover:bg-gray-700 ring-gray-200 ring-2" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
                    </svg>
                    <span class="text-2xl">
                        todo2
                    </span>
                </div>
                <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer mr-3 h-8 w-8 py-1 shadow-md rounded-md font-semibold text-white text-base bg-red-500 hover:bg-red-700 ring-2 ring-red-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
                </svg>
            </li>
            <li class="block py-3 border-b-2 border-gray-200 bg-gray-300 flex justify-between">
                <div class="flex">
                    <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer ml-3 h-8 w-8 mr-3 py-1 shadow-md rounded-md font-semibold text-white text-base bg-green-500 hover:bg-green-700 ring-green-200 ring-2" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
                    </svg>
                    <span class="text-2xl">
                        todo3
                    </span>
                </div>
                <svg xmlns="http://www.w3.org/2000/svg" class="cursor-pointer mr-3 h-8 w-8 py-1 shadow-md rounded-md font-semibold text-white text-base bg-red-500 hover:bg-red-700 ring-2 ring-red-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
                </svg>
            </li>
        </ul>
    </body>
</html>

解説

todolist.blade.phpというbladeファイルを作成しました。
bladeはLaravelのビューを担当するファイルです。

headタグから見ていきます。
下記のように書くことで、tailwindcssを含んだcssファイルにリンクしています。

<link href="{{ asset('css/app.css') }}" rel="stylesheet">

これで、tailwindcssのクラスが使用できます。

bodyタグの中にモックを構成するタグを書いています。
iconをsvgで作っているので、少し見づらいかもしれません。

タグにクラスをたくさん付けています。
このようにクラスを複数指定して、作りたいものに近づけるのがTailwindCSSの書き方です。

今回は1ページだけなので、たくさんのクラスをわーっと書いています。。
複数ページ作る場合や同じボタンなどを作っていく場合は、atomic designを参考にしてコンポーネント化していくといいと思います。

動作確認

http://localhost:8085/にアクセスすると、下記のようにモック画面が表示されます。

とりあえず、静的なファイルの作成までが完了しました。
ボタンを押しても動きを付けていないので、何も起きません。

次回からTODOの追加などの動的なアクションを付けていきます。

次回の記事

ご紹介

【外部サイトのご紹介】カモメのススメ様
もくもく会アプリを作るという、あまり他ではみないチュートリアルがあります。
詳しく書かれているようなので、よかったらチェックしてみてください。
Docker×Laravel8もくもく会のアプリ作成1(アプリ概要)

コメント

タイトルとURLをコピーしました