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

  • 68
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

管理画面用のフレームワークである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

参考

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