Laravel2 Advent Calendar 2019 – Qiita の15日目の記事になります。
(ちょうど空いていたのでぎりぎりですが参加させていただきました。)
Laravel 5.8以前ではフロントエンド フレームワークとしてbootstrapとVueがはじめから利用可能なように設定されていましたが、Laravel 6.xではデフォルトでは含まれなくなりました。
6.xでBootstrapを利用するには、フロントエンドのScafolding機能を提供するlaravel/ui
のコマンドを使用してJavascriptのビルドとCSSのプリプロセッサ設定を生成する必要があります。
この記事では、Laravel 6.xのプロジェクトにlaravel/ui
を利用してBootstrapをプロジェクトに導入する手順についてシンプルなアプリケーションを例に解説しています。
想定しているケースは、主にサーバー側のビューテンプレートで画面を実装する場合に、アプリケーション全体でBootstrap 4の基本的な機能を使ってマークアップと多少画面の動きをつけたい(DatePickerを利用したいなど)といった構成のアプリケーションです。
本格的にフロントエンド開発の環境を整えるには、Laravel Mixについても学ぶ必要がありますが、デフォルトのwebpack.mix.js
の設定でできる範囲にとどめて深堀りしません。
この記事のサンプルソースは Github リポジトリ https://github.com/hrendoh/laravel-ui-bootstrap-tasks に公開しています。記事の中で解説していないソースコードについてはこちらを参照ください。
laravel / uiとは
laravel/ui
は、JavascriptのビルドとCSSのプリプロセッサ設定のScaffoldingの生成コマンドを提供するパッケージですが、コマンドオプション--auth
でフォーム認証用のビューテンプレートも生成することができるようになっています。(6.xでは、artisan make:auth
コマンドがなくなっているので、認証機能をlaravel/ui
のコマンドオプション--auth
で生成します)
またlaravel/ui
のリポジトリの認証のScaffoldingを参考に基本的なCRUDのScaffoldingオプションの追加などの開発もできそうな気もします。
- 公式ドキュメント: JavaScript & CSS Scaffolding
- Github: laravel/ui
作成するアプリケーションについて
この記事では、Bootstrap 4で開発を進めるにあたって必ず要るであろう以下の3点を設定し、それぞれを利用するシンプルなToDoアプリを作成してみます。
- Bootstrap 4のスタイルの適用
- Bootstrap 4で除外されたDatePickerの組み込み、Tempus Dominusを利用
- アイコンの導入、Bootstarp 4が推奨しているライブラリのうちFontAwesomを導入
作成するのは、RailsなどのScaffoldが生成するようなCRUDアプリです。
index
create
前提条件
JavascriptとCSSのビルドはLaravel Mix つまり Webpackを利用しますので、node.jsのインストールが必用です。
プロジェクトの作成
適当に「laravel-ui-bootstrap-tasks」という名前のプロジェクトを作ってみます。
$ composer create-project --prefer-dist laravel/laravel laravel-ui-bootstrap-tasks
Bootstrap 4のインストール
まずは laravel/ui
をインストールします。
$ composer require laravel/ui --dev
ui
コマンドでbootstrap
用のJavascriptビルドおよびCSSプリプロセッサsassの設定を含むScaffoldingを追加します。
$ php artisan ui bootstrap
Bootstrap scaffolding installed successfully.
Please run "npm install && npm run dev" to compile your fresh scaffolding.
コマンドの実行で追加された設定を確認してみます。
package.json
には、以下のパッケージが追加されています。
"devDependencies": {
...
"bootstrap": "^4.0.0",
...
"jquery": "^3.2",
"popper.js": "^1.12",
...
}
jsは、bootstrap.js
にpopper.js
の初期化処理とbootstrap
のインポートが追加されました。
// resources/js/bootstrap.js
...
/**
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
* for JavaScript based Bootstrap features such as modals and tabs. This
* code may be modified to fit the specific needs of your application.
*/
try {
window.Popper = require('popper.js').default;
window.$ = window.jQuery = require('jquery');
require('bootstrap');
} catch (e) {}
...
sass側は_variables.scss
が追加され、app.scss
にGoogleのWebフォント Nunito、追加されたvariables
とbootstrap
の読み込みが追加されています。
// resources/sass/app.scss
// Fonts
@import url('https://fonts.googleapis.com/css?family=Nunito');
// Variables
@import 'variables';
// Bootstrap
@import '~bootstrap/scss/bootstrap';
追加されたnpm パッケージをインストールしてビルドします。
$ npm install
$ npm run dev
DONE Compiled successfully in 6036ms 6:50:18 PM
Asset Size Chunks Chunk Names
/css/app.css 196 KiB /js/app [emitted] /js/app
/js/app.js 1.06 MiB /js/app [emitted] /js/app
これでBootstrap 4を利用できる環境が整いました。
FontAwesomeとDatePickerは後ほど追加していきます。
テーブル(モデル)準備
早速以下のテーブルを用意していきます。
カラム名 | マイグレーションの型 | MySQLのデータ型 |
---|---|---|
id | bigIncrements | bigint(20) unsigned |
subject | string | varchar(255) |
description | text | text |
due_date | date | date |
completed | boolean | tinyint(1) |
モデル「Task」をマイグレーション オプションを付けて生成します。
$ php artisan make:model Task -m
Model created successfully.
Created Migration: 2019_12_11_151510_create_tasks_table
テーブル定義に従ってマイグレーションを作成します。
<?php
// database/migrations/2019_12_11_151510_create_tasks_table.php
...
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('subject');
$table->text('description')->nullable();
$table->date('due_date')->nullable();
$table->boolean('completed')->nullable();
$table->timestamps();
});
}
...
マイグレーションを適用します。
$ php artisan migrate
Migration table created successfully.
...
Migrating: 2019_12_11_151510_create_tasks_table
Migrated: 2019_12_11_151510_create_tasks_table (0.07 seconds)
ロジックを実装
リソースコントローラを生成して、対応するViewを追加します。(公式ドキュメント: Resource Controllers)
$ php artisan make:controller TaskController --resource --model=Task
Controller created successfully.
ルートはtasks
にリダイレクトし、Taskのリソース ルーティングを追加します。
<?php
// routes/web.php
Route::get('/', function () {
return redirect('tasks');
});
Route::resource('tasks', 'TaskController');
TaskControllerのリソース アクションに実装を追加します。
<?php
namespace App\Http\Controllers;
use App\Task;
use Illuminate\Http\Request;
class TaskController extends Controller
{
public function index()
{
$tasks = Task::paginate();
return view('tasks.index', compact('tasks'));
}
public function create()
{
return view('tasks.create');
}
public function store(Request $request)
{
$inputs = $request->all();
Task::create($inputs);
return redirect()->route('tasks.index')->with('message', 'Task created successfully.');
}
public function show(Task $task)
{
return view('tasks.show', compact('task'));
}
public function edit(Task $task)
{
return view('tasks.edit', compact('task'));
}
public function update(Request $request, Task $task)
{
$inputs = $request->all();
if (!isset($inputs['completed'])) $inputs['completed'] = false;
$task->update($inputs);
return redirect()->route('tasks.index')->with('message', 'Task updated successfully.');
}
public function destroy(Task $task)
{
$task->delete();
return redirect()->route('tasks.index')->with('message', 'Task deleted successfully.');
}
}
resources/views
にレイアウト用のテンプレートを追加
// resources/views/layout.blade.php
resources/views
にtasks
ディレクトリを追加して、ビュー’index’、’created’、’show’、’edit’を追加します。
$ mkdir resources/views/tasks
// resources/views/tasks/index.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">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
<a class="navbar-brand" href="#">{{ config('app.name', 'Laravel') }}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="{{route('tasks.index')}}">Tasks</a></li>
</ul>
</div>
</nav>
<div class="container">
@if(session('message'))
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{@session('message')}}
</div>
@endif
@yield('header')
@yield('content')
</div>
@yield('scripts')
</body>
</html>
create.blade.php
, show.blade.php
, edit.blade.php
のテンプレートについてはGithubリポジトリのソースを確認して追加してください。
ここまでで/tasks/index
を表示してみると以下の用になります。
テンプレートにはアイコンのタグが含まれていますが、まだ表示されていません。
Font Awesomeの追加
次にFont Awesomeを導入してアイコンも表示されるようにしていきます。
Font Awesomeを選択している理由としては、Bootstrapのドキュメント Icons · Bootstrap で推奨しているライブラリの一番上に載っていたからというだけです。
npmで、@fortawesome/fontawesome-free
とfont-awesome-scss
を追加します。
$ npm install @fortawesome/fontawesome-free --save-dev
$ npm install font-awesome-scss --save-dev
resources/sass/app.scss
にFontAwesomeのインポートを追加します。
// resources/sass/app.scss
// Font Awesome
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/regular';
この記事で作るアプリでは、「free」の「solid」と「regular」に含まれるアイコンのみを使用しています。
その他のアイコン(「blands」など)を使う場合は、必要に応じてインポートは追加してください。
ビルドし直します。
$ npm run dev
再度、/tasks/index
を表示してみると以下のようにアイコンも表示されるようになります。
DatePickerの追加
Bootstrap 4ではDatePickerが含まれなくなったので、Tempus Dominus bootstrap 4を利用します。
また、Tempus Dominusを動かすにはMoment.jsが必要なのでMoment.jsもインストールします。
$ npm install moment --save-dev
$ npm install tempusdominus-bootstrap-4 --save-dev
resources/sass/app.scss
にTempus Dominusのscssのインポートを追加します。
// resources/sass/app.scss
// Tempus Dominus
@import '~tempusdominus-bootstrap-4/src/sass/tempusdominus-bootstrap-4-build';
resources/js/bootstrap.js
にmoment.jsとtempusdominus-bootstrap-4の読み込みを追加します。
moment.jsはグローバル変数にセットする必要があります。
// resources/js/bootstrap.js
/**
* Import moment js
*/
import moment from 'moment';
window.moment = moment;
require('tempusdominus-bootstrap-4');
$('.datetimepicker').datetimepicker({
icons: {
// Font Awesome 5には「fa-clock-o」がなくなっているので指定する
time: 'far fa-clock'
},
format: 'YYYY-MM-DD'
});
resources/views/tasks/create.blade.php
のDue Dateのマークアップは以下のように記述しています。
<div class="form-group">
<label for="due_date-field">Due Date</label>
<div class="input-group date datetimepicker" id="due_date" data-target-input="nearest">
<input type="text" name="due_date" id="due_date-field" class="form-control datetimepicker-input" data-target="#due_date" />
<div class="input-group-append" data-target="#due_date" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
ポイントは、datepicker
クラスを指定しているdiv要素のidの値「due_date」を、input-group-append
クラスのdiv要素のdata-target
属性の値にセットするあたりです。