前回FOSUserBundleのインストールとセットアップを行いました。 セットアップが完了していれば画面が表示されるはずです。

動作確認

ログインページを表示してみます。 http://localhost:8000/login

image

画面が寂しいですが、一応表示されますね。

ログインしてみたいのでユーザーを作ってみましょう。

ユーザーを作成

登録画面から登録してもよいですが、確認メール送信の為にsmtpの設定やらが必要なのでコンソールから作ります。

php app/console list fos

FOSUserBundleのコマンドが増えている事が確認できます。

fos:user:create でユーザーが作れるので作ってみます。

$ fos:user:create
Please choose a username:quartet
Please choose an email:quqrtet@example.com
Please choose a password:
Created user quartet

ユーザーが作成できたらログイン等を確認してみましょう。
ログインできていたら、Symfony Profiler Toolbar に認証情報が表示されます。

screenshot

レイアウトを変更する

インストールしたバンドルのレイアウトを修正したい!

直接インストールしたバンドルを修正するなんてのは論外です。
なのでそんな時にどうしたらよいかの説明をします。

基本的な流れはこれだけです。

  1. サードパーティーバンドルが使用している ビューのファイル名 を確認する。
  2. 同じビューのファイル名のテンプレートを サブバンドル 内に作る。
  3. 作ったテンプレートを編集

サブバンドル って何?となりますが、バンドルには親子関係を設定できます。
要は、 QuartetBlogBundleの親がFOSUserBundleである事を定義 すればよいのです。

クラスの継承関係みたいなもんですね。

バンドルの親子関係を定義

早速定義してみます。

<?php
// QuartetBlogBundle

namespace Quartet\Bundle\BlogBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class QuartetBlogBundle extends Bundle
{
    public function getParent()
    {
        return 'FOSUserBundle';
    }
}

バンドルの親子関係の定義はこれだけです。

次にレイアウトをオーバーライドしてみます。

レイアウトのオーバーライド

プログラムでもオーバーライドする時は親メソッド名が分かっていないとできません。
感覚的にはそんな感じです。

今回はユーザー情報ページを変更してみようと思います。
http://localhost:8000/profile/

まずは、 このページがどのテンプレートファイルを使っているか を知る必要があります。

探してみる

下のコマンドで、どのアクションを実行しているかが分かるので、そこからvendorディレクトリ内のテンプレートファイルを探します。

php app/console debug:router {ルーティング名}

こんな事をしなくても、バンドルのディレクトリ構成は一緒なのでResources/viewsの中を見れば大体察しがつく事が殆どですが笑

下記がユーザー情報ページのテンプレートです。

// Resources/views/Profile/show.html.twig

{% extends "FOSUserBundle::layout.html.twig" %}

{% block fos_user_content %}
{% include "FOSUserBundle:Profile:show_content.html.twig" %}
{% endblock fos_user_content %}

テンプレート FOSUserBundle::layout.html.twig を継承している事が分かります。

補足: twig テンプレート内のファイルパス

twig内のファイルパスの命名規則は バンドル名:ディレクトリ名:テンプレートファイル名 となっています。
twig内のファイルパスは、各バンドルのResources/views/からの絶対ファイルパスになります。
Resources/views/ディレクトリ名/テンプレートファイル名 といった感じです。

FOSUserBundle::layout.html.twig

の様なパスはディレクトリに入っていない事を意味します。

下記がそのテンプレートファイルの中身です。

// Resouces/views/layout.html.twig
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
    </head>
    <body>
        <div>
            {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
                {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} |
                <a href="{{ path('fos_user_security_logout') }}">
                    {{ 'layout.logout'|trans({}, 'FOSUserBundle') }}
                </a>
            {% else %}
                <a href="{{ path('fos_user_security_login') }}">{{ 'layout.login'|trans({}, 'FOSUserBundle') }}</a>
            {% endif %}
        </div>

        {% if app.request.hasPreviousSession %}
            {% for type, messages in app.session.flashbag.all() %}
                {% for message in messages %}
                    <div class="flash-{{ type }}">
                        {{ message }}
                    </div>
                {% endfor %}
            {% endfor %}
        {% endif %}

        <div>
            {% block fos_user_content %}
            {% endblock fos_user_content %}
        </div>
    </body>
</html>

このテンプレートファイルを、子バンドルであるQuartetBlogBundleの同じテンプレートファイルパスにコピーします。
場所はQuartetBlogBundle::layout.html.twigです。

コピーしたあとのテンプレートを編集すると、レイアウトが変更される事が確認できると思います。

twigテンプレート内の{% block %}の補足

twigのテンプレートを見ていると、{% block block_name %}{% endblock %}の記述が至るとこに見られます。
これがテンプレートの継承の仕組みです。

感覚的にはプログラムのクラスメソッドにかなり近いです。

下に例を書きます。

基本その1

// layout.html.twig
<html>
{% block content %}
hello world!
{% endblock %}
</html>

これをレンダリングすると、<html>hello world!</html>と表示されます。

基本その2 ~ 継承編 ~

レイアウトを使うテンプレートを作ってみます。

// view.html.twig
{% extends '::layout.html.twig' %}
{% block content %}
hello twig!
{% endblock %}

これをレンダリングすると、<html>hello twig!</html>と表示されます。
動作はメソッドオーバーライドと同じです。

クラスで表すとこんな感じです。

<?php
class Layout
{
    public function content()
    {
        return 'hello world!';
    }
}
class View extends Layout
{
    public function content()
    {
        return 'hello twig!';
    }
}

echo (new View())->content(); // => 'hello twig!'

基本その3 ~ 継承その2編 ~

クラスメソッドに近いのなら、親クラスのメソッドも呼べるの?
答えは YES

view.html.twigを少し変えます。

// view.html.twig
{% extends '::layout.html.twig' %}
{% block content %}
{{ parent() }} hello twig!
{% endblock %}

大体想像付きますが、これをレンダリングすると <html>hello world! hello twig!</html> となります。
もちろん {{ parent() }} の位置はどこにでも置けます。

// view.html.twig
{% extends '::layout.html.twig' %}
{% block content %}
hello {{ parent() }} twig!
{% endblock %}

これもクラスで表すとこんな感じです。

<?php
class Layout
{
    public function content()
    {
        return 'hello world!';
    }
}
class View extends Layout
{
    public function content()
    {
        return parent::content() . 'hello twig!';
    }
}

echo (new View())->content(); // => 'hello world! hello twig!'

他にもFOSUserBundleの拡張はたくさん

全ては書けないのでとりあえず列挙しておきます。