Edited at

管理画面を作る:AdminLTE Laravel組込み編

More than 3 years have passed since last update.

管理画面用のフレームワークであるAdminLTEの構造を見てみたので、今度は、PHPのフレームワークであるLaravel5.1に組み込んで見たいと思います。

なお、Laravelの基本的な知識はあるものとして話を進めます。


準備


laravelのインストール

laravelのインストールはComposerで行うことにします。そもそも、Composerが入っていない人は準備して下さい。

composer create-project laravel/laravel test


AdminLTEの導入

AdminLTEのCSSやJSを設置する場所は、laravelのpublicフォルダになるかと思います。

AdminLTEをダウンロードしてきて直接展開してもいいですし、bowerでインストールしてもいいです。

ここでは、bowerを利用します。Bowerが入っていない人は準備して下さい。

cd test/public

bower install admin-lte

そうすると、public/bower_components/admin-lte以下にファイルが展開されます。

主なフォルダとしては、


  • dist AdminLTEの本体系CSS,JS

  • bootstrap ベースとなっているBootstrap3.x系ファイル

  • plugins jQueryなどなど

それ以外は、サンプルだったりするので、消してもいいかなと思います。

これで、とりあえずファイルの準備は完了です。


Laravelと統合

ここでいうLaravelとの統合とは、Laravelのテンプレートエンジンであるbladeとパーシャル機能を使った部品化です。


テンプレート化への前準備

bladeでテンプレート化する前に、そもそもLaravelでAdminLTEで作成した画面が正しく表示されるようにします。

基本編で用意した最低構成のファイルをコピーして、resources/views/以下にadminlte.blade.phpとして保存します。

また、このページがhttp://localhost:8000/admin 等で呼び出しできるようにRouteを設定します。

Route::get('admin',function(){

return view('adminlte');
});

アクセスしてみると、表示はされるものの、CSS等が適用されておらず、表示が崩れています。

これは、admin.blade.phpから、adminLTE等のCSS,JSが正しく参照されていないからです。

<head>および<body>の最後のCSSやJSの参照パスをpublic/bower-components/admin-let/以下を参照するように修正します。

以下が、正しく適用したものです。

CDN参照のものはそのままで、ローカルリソースに対して、{{asset("bower_components/admin-lte/~")}}をつけてやる感じです。なお、asset()はpublicフォルダとして解釈されます。

<!doctype html>

<html>
<head>
<meta charset="utf-8">
<title>adminLTE test</title>
<!-- for responsive -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- bootstrap -->
<link href="{{asset("bower_components/admin-lte/bootstrap/css/bootstrap.min.css")}}" rel="stylesheet" type="text/css" />
<!-- font awesome -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- ionicons -->
<link href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<!-- adminLTE style -->
<link href="{{asset("bower_components/admin-lte/dist/css/AdminLTE.min.css")}}" rel="stylesheet" type="text/css" />
<link href="{{asset("bower_components/admin-lte/dist/css/skins/skin-blue.min.css")}}" rel="stylesheet" type="text/css" />
</head>
<body class="skin-blue">
<div class="wrapper">

<!-- トップメニュー -->
<header class="main-header">

<!-- ロゴ -->
<a href="" class="logo">管理画面</a>

<!-- トップメニュー -->
<nav class="navbar navbar-static-top" role="navigation">
<ul class="nav navbar-nav">
<li><a href="">顧客管理</a></li>
</ul>
</nav>

</header><!-- end header -->

<!-- サイドバー -->
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu">

<!-- メニューヘッダ -->
<li class="header">機能一覧</li>

<!-- メニュー項目 -->
<li><a href="">新規登録</a></li>

</ul>
</section>
</aside><!-- end sidebar -->

<!-- content -->
<div class="content-wrapper">

<!-- コンテンツヘッダ -->
<section class="content-header">
<h1>ページタイトル</h1>
</section>

<!-- メインコンテンツ -->
<section class="content">

<!-- コンテンツ1 -->
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">ボックスタイトル</h3>
</div>
<div class="box-body">
<p>ボックスボディー</p>
</div>
</div>

</section>

</div><!-- end content -->

<!-- フッター -->
<footer class="main-footer">
<div class="pull-right hidden-xs">Version1.0</div>
<strong>Copyright &copy; 2015</strong>, All rights reserved.
</footer><!-- end footer -->

</div><!-- end wrapper -->
<!-- JS -->

<!-- jquery -->
<script src="{{asset("bower_components/admin-lte/plugins/jQuery/jQuery-2.1.4.min.js")}}" type="text/javascript"></script>
<!-- bootstrap -->
<script src="{{asset("bower_components/admin-lte/bootstrap/js/bootstrap.min.js")}}" type="text/javascript"></script>
<!-- adminLTE -->
<script src="{{asset("bower_components/admin-lte/dist/js/app.min.js")}}" type="text/javascript"></script>
</body>
</html>

http://localhost:8000/admin にアクセスすると、

adminlte

と、表示されればOKです。


テンプレート化する

ベースとなるファイルで正しく表示ができるようになったので、これを分解して、テンプレート化します。

テンプレートの構造は、


  • layout.blade.php

を主ファイルとし、共通部分を


  • header.blade.php

  • sidebar.blade.php

  • footer.blade.php

に分散させます。


layout.blade.phpを作る

layout.blade.phpをviews以下に作成し、先ほど動作を確認したadminlte.blade.phpのコードをコピペします。

adminlte.blade.phpをlayoutにリネームしても大丈夫です。


header部分を置き換える

layout.blade.phpの<header>...</header>部分をカットし、header.blade.phpとしてペースト・保存します。

<!-- トップメニュー -->

<header class="main-header">

<!-- ロゴ -->
<a href="" class="logo">管理画面</a>

<!-- トップメニュー -->
<nav class="navbar navbar-static-top" role="navigation">
<ul class="nav navbar-nav">
<li><a href="">顧客管理</a></li>
</ul>
</nav>

</header><!-- end header -->

さらに、layout.blade.phpの<header>...</header>があった部分を、

@include('header')

のように分離したheader部分を読み込むように置き換えます。

同様に、sidebar, footerなども置き換えます。


sidebarを置き換える

サイドバーの位置にあるコードをカットし、sidebar.blade.phpとして保存します。

<!-- サイドバー -->

<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu">

<!-- メニューヘッダ -->
<li class="header">機能一覧</li>

<!-- メニュー項目 -->
<li><a href="">新規登録</a></li>

</ul>
</section>
</aside><!-- end sidebar -->

サイトバーがあった位置、<aside class="main-sidebar">...</aside class="main-sidebar">を

@include('sidebar')

に置き換えます。


footerを置き換える

フッターの位置にあるコードをカットし、footer.blade.phpとして保存します。

<!-- フッター -->

<footer class="main-footer">
<div class="pull-right hidden-xs">Version1.0</div>
<strong>Copyright &copy; 2015</strong>, All rights reserved.
</footer><!-- end footer -->

フッターがあった<footer>...</footer>を、

@include('footer')

に置き換えます。


contentを置き換える

content部分は動的に変わるところなので、include()ではなく、yield('content')に置き換えます。

header等は、関連記述全体を置き換えましたが、contentでは<div class="content-wrapper"></div>部は残しています。

これで、テンプレート化は終了です。

layout.blade.php全体を見てみます。共通部分が外部ファイルへ切りだされ、動的部分は@yield('content')となりました。

<!doctype html>

<head>
<meta charset="utf-8">
<title>adminLTE test</title>
<!-- for responsive -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- bootstrap -->
<link href="{{asset("bower_components/admin-lte/bootstrap/css/bootstrap.min.css")}}" rel="stylesheet" type="text/css" />
<!-- font awesome -->
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<!-- ionicons -->
<link href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<!-- adminLTE style -->
<link href="{{asset("bower_components/admin-lte/dist/css/AdminLTE.min.css")}}" rel="stylesheet" type="text/css" />
<link href="{{asset("bower_components/admin-lte/dist/css/skins/skin-blue.min.css")}}" rel="stylesheet" type="text/css" />
</head>
<body class="skin-blue">
<div class="wrapper">

<!-- ヘッダー -->
@include('header')

<!-- サイドバー -->
@include('sidebar')

<!-- content -->
<div class="content-wrapper">

<!-- コンテンツ -->
@yield('content')

</div><!-- end content -->

<!-- フッター -->
@include('footer')

</div><!-- end wrapper -->
<!-- JS -->

<!-- jquery -->
<script src="{{asset("bower_components/admin-lte/plugins/jQuery/jQuery-2.1.4.min.js")}}" type="text/javascript"></script>
<!-- bootstrap -->
<script src="{{asset("bower_components/admin-lte/bootstrap/js/bootstrap.min.js")}}" type="text/javascript"></script>
<!-- adminLTE -->
<script src="{{asset("bower_components/admin-lte/dist/js/app.min.js")}}" type="text/javascript"></script>
</body>
</html>

だいぶ見通しがいい感じになりました。


表示してみる

では、作成したテンプレートを利用してコンテンツを表示してみます。

とりあえず、さきほど@yield('content')に置き換えた部分を、下記のようにsample.blade.phpとして保存します。

@extends('layout')

@section('content')

<!-- コンテンツヘッダ -->
<section class="content-header">
<h1>ページタイトル</h1>
</section>

<!-- メインコンテンツ -->
<section class="content">

<!-- コンテンツ1 -->
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">ボックスタイトル</h3>
</div>
<div class="box-body">
<p>ボックスボディー</p>
</div>
</div>

</section>

@endsection

layout.blade.phpをレイアウトとして読み込むように@extends('layout')とし、@yield('content')に置き換わる部分を@section('content')...@sectionendで囲いました。

そして、ルートを適切に書き換えます。sample.blade.phpを見に行くようにします。

Route::get('admin',function(){

return view('sample');
});

/admimにアクセスしてみます。

adminlte

見た目はまったく変わっていませんが、テンプレート化された状態で表示されています。


ファイルをまとめる

Laravelのプロジェクトを作成するたび、こんな操作をしてられません。とりあえず、views直下に作成したファイル群をファイルにまとめます。後は、まとめたファイルをgithub等で管理し、都度cloneするなり、各種自動化ツールで、取得するようにすればいいでしょう(もちろん手動でもいいですが)。

ここでは、views以下にadminlteというフォルダを作って、そこに集約します。

修正が必要なファイルは2つ。


  • layout.blade.php

  • sample.blade.php

です。


テンプレートの修正

まず、layout.blade.php側でinclude()の記述を修正します。


@include('header') => @include('adminlte.header')


頭に、adminlte.を付与します。headerだけでなく、sidebar,footerについても行います。


テンプレート利用側の修正

sample.blade.phpの頭でextends()を修正します。


@extends('layout') => @extends('adminlte.layout')


とします。

完了したら、/adminにアクセスして、表示に問題がないか確認して下さい。


応用

いくつかの機能がLaravelで正しく動くか試してみます。


ページネーション

LaravelはBootstrap3互換のページネーションタグを出力しますが、それがAdminLTEで動くかの動作確認をしてみたいと思います。

結論から言えばとりあえずは動きました。ただ、AdminLTEが用意しているページネーションのスタイルに完全に合わせるためには、Laravel側でページネーションをカスタマイズする必要があります。


ちなみに、表示している顧客データはダミー情報生成ツールFakerで生成した架空のものですが、偶然実名と一致することもあるでしょうから、モザイクかけときます。


adminlte

標準でmigrateできるUserを利用し、Seederを使ってデータを生成しました。

で、コントローラーで、

public function index()

{
//
$query = User::query();
$users = $query->paginate(10);
return view('users')->with('users',$users);

などとし、Viewで、テンプレートを使い、

@extends('adminlte.layout')

@section('content')

<!-- コンテンツヘッダ -->
<section class="content-header">
<h1>顧客管理</h1>
</section>

<!-- メインコンテンツ -->
<section class="content">

<!-- コンテンツ1 -->
<div class="box">
<div class="box-header with-border">
{{-- <h3 class="box-title">顧客一覧</h3> --}}

<form class="form-inline" style="margin:15px;">
<div class="form-group">
<label>検索:</label>
<input type="text" name="keyword" class="form-control" style="width:200px;">
</div>
<div class="form-group">
<input type="submit" value="検索" class="btn btn-primary">
</div>
</form>

</div>
<div class="box-body">
<table class="table table-bordered">
<tr>
<th>ID</th>
<th>氏名</th>
<th>email</th>
</tr>
@foreach($users as $user)
<tr>
<td>{{$user->id}}</td>
<td>{{$user->name}}</td>
<td>{{$user->email}}</td>
</tr>
@endforeach
</table>
</div>
<div class="box-footer clearfix">
{!! $users->render() !!}
</div>
</div>
</section>

@endsection

としています。ポイントはboxのheaer,footer等をちゃんと利用することでしょうか。

残念ながら一部直接styleを追加して補正しています。。。まあ、仕方ないでしょう。

補正してるところは、


  • 検索用のFormに15pxのマージン追加。

  • 検索用のinput typeの長さを200pxに。

です。


Chart.js

AdminLTEでは、いくつかのChartプラグインが最初から含まれているようですが、ここでは簡単に試せるchart.jsを見てみます。

結論から言えば、少し癖があるものの動きまし。とはいえ、Laravel依存なのはchart.jsへのPathくらいですが。

adminlte

但し、幾つかやること・注意点があります。


chart.jsを読み込む

読み込みを共通に行っているlaytou.blade.phpにChart.jsを読み込むための記述をします。

また、javascriptを記述する専用の@include('scripts')を各種JSの読み込み後に入れました(下記は抜粋部)。

<!-- JS -->

<!-- jquery -->
<script src="{{asset("bower_components/admin-lte/plugins/jQuery/jQuery-2.1.4.min.js")}}" type="text/javascript"></script>
<!-- bootstrap -->
<script src="{{asset("bower_components/admin-lte/bootstrap/js/bootstrap.min.js")}}" type="text/javascript"></script>
<!-- adminLTE -->
<script src="{{asset("bower_components/admin-lte/dist/js/app.min.js")}}" type="text/javascript"></script>
<!-- chart.js -->
<script src="{{asset("bower_components/admin-lte/plugins/chartjs/Chart.min.js")}}" type="text/javascript"></script>

@include('scripts')


canvasの配置

Chart.jsでは、canvasに描画されるので、HTMLにChart描画用のcanvasを配置します。

ここではid="line"と配置しています。

@extends('adminlte.layout')

@section('content')

<!-- コンテンツヘッダ -->
<section class="content-header">
<h1>チャート表示</h1>
</section>

<!-- メインコンテンツ -->
<section class="content">

<!-- コンテンツ1 -->
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">LineChart</h3>
</div>
<div class="box-body">
<div class="chart">
<canvas id="line" height="200" width="800"></canvas>
</div>
</div>
</div>

</section>

@endsection

Canvasを<div class="chart">...</div>で囲ってやることで、Windowsサイズに合わせてリサイズされるようになるようです(まあ、ただ伸び縮みしてるだけですが)。ただ、その伸び縮みロジックがいまいち???な感じです。縦横を均等ではなく、一定の比率を取ってやるほうがちゃんと機能するようです。詳細は機械があれば調べますが。。。


JSを書く

描画用のJavascriptと記述します。ここでは、scripts.blade.phpに切り出したので、そこに書きます。

<script>

$(function(){

//動的要素
var axis = ["A","B","C","D"];
var data = [10,20,30,20];

//描画設定
var lineChartData = {
labels:axis,
datasets:[
{
fillColor:"rgba(220,220,220,0.5)",
strokeColor:"rgba(220,220,220.1)",
pointColor:"rgba(220,220,220,1)",
pointStrokeColor:"#FFF",
data:data
}
]
};

//特定のルート時のみ適用
var url = window.location;
if(url.pathname == "/chart"){

var ctx = $("#line")[0].getContext("2d");
new Chart(ctx).Line(lineChartData);

}

});
</script>

基本はchart.jsのページ等を参考に書くだけです。あと、今回は、layout.blade.phpに@include('scripts')としたので、関係無いページでもJSが実行されます。存在しないIDを参照するとエラーになるので、特定のルート(ここでは/chart)の時だけ、描画処理を行うように記述しています。

動的要素のところをAPIから取得すれば、汎用性が上がります。LaravelでAPIつくるのはここに書いてます。


Form

いよいよLaraveとは無関係ですが、この流れでFormのチェックもしておきます。Form関連はBootstrapとほぼ同じようです。

adminlte

コードは下記の通り。

<form>タグの位置をbox-headerやbox-footerの位置を考慮しながら置くと、いい感じになるようです。

@extends('adminlte.layout')

@section('content')

<!-- header -->
<section class="content-header">
<h1>Formのテスト</h1>
</section>

<!-- body -->
<section class="content">

<!-- box col-12 -->
<div class="box box-primary">
<div class="box-header with-border">
Simple From
</div>
<form>
<div class="box-body">
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control">
</div>
<div class="form-group has-error">
<label>Password</label>
<input type="text" class="form-control">
<span class="help-block">パスワードがおかしいです。</span>
</div>
<div class="form-group">
<label>Select</label>
<select class="form-control">
<option>option1</option>
<option>option2</option>
<option>option3</option>
</select>
</div>
<div class="form-group">
<label>File Upload</label>
<input type="file">
</div>
<div class="checkbox">
<label>
<input type="checkbox"><b>remember</b>
</label>
</div>
<p><b>Radio</b></p>
<div class="radio-inline">
<label>
<input type="radio" name="gender" value="man"><b></b>
</label>
</div>
<div class="radio-inline">
<label>
<input type="radio" name="gender" value="woman"><b></b>
</label>
</div>
<div class="form-group">
<label>TextArea</label>
<textarea class="form-control" rows="3"></textarea>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</section>

@endsection


参考

基本的な流れはここを参考にしました。

参考になりました。