シムノート

PHPフレームワークSymfonyの学習帳

ユーザ用ツール

サイト用ツール


サイドバー

メニュー



このエントリーをはてなブックマークに追加

blog:2015-12-17:超入門_symfony3_bundle

超入門 Symfony3 : (13) Bundle

SymfonyではBundleと言う仕組みを使って機能を拡張することができます。

ここでは、おみくじサイトにFOSUserBundleを組み込んで機能を拡張してみます。FOSUserBundleはフォームを使った認証(ログイン)の機能を提供してくれるBundleです。最も利用されている定番Bundleです。

Bundleのインストール手順

Bundleのインストール手順は以下の3ステップです。

  1. Composerパッケージのインストール
  2. Bundleの有効化
  3. Bundle固有の設定

Composerパッケージのインストール

$ composer require ベンダー名/パッケージ名 バージョン指定

BundleはComposerのパッケージとして提供されています。上記コマンドを実行するとvendorディレクトリ以下にパッケージが追加されます。

Composerをまだインストールしていない方は、こちらを参照してインストールしてください。

Bundleの有効化

AppKernel.phpを以下のように修正します。

<?php
// ...
public function registerBundles()
{
    $bundles = array(
        // ...
        new name\space\to\XXXXBundle(),  // Bundleを追加して有効化
        // ...
    );
}

Bundle固有の設定

後は使用するBundleのドキュメントに従って設定を行います。

FOSUserBundle

では、実際におみくじサイトにFOSUserBundleを組み込みます。

事前準備

プロジェクトの日本語化

FOSUserBundleには日本語メッセージが含まれているので、プロジェクトを日本語化しておきます。

parameters:
    # en -> ja 変更します
    locale: ja

framework:
    ...
    # コメントアウトして有効にします
    translator:      { fallbacks: ["%locale%"] }

Userエンティティの作成

FOSUserBundleはユーザー情報をDBに格納する為、Userエンティティを作成しておきます。

以下は、ジェネレーターを使って、idプロパティのみを持つUserエンティティをAppBundleに作成する例です。

$ php bin/console doctrine:generate:entity

The Entity shortcut name: AppBundle:User [ENTER]

Configuration format (yml, xml, php, or annotation) [annotation]:  [ENTER]

New field name (press <return> to stop adding fields):  [ENTER]

> Generating entity class src/AppBundle/Entity/User.php: OK!
> Generating repository class src/AppBundle/Repository/UserRepository.php: OK!

インストール

ComposerでFOSUserBundleをインストールします。

$ composer require friendsofsymfony/user-bundle "~2.0@dev"

FOSUserBundleを有効化します。

<?php
// ...
public function registerBundles()
{
    $bundles = array(
        // ...
        new FOS\UserBundle\FOSUserBundle(),
        // ...
    );
}

設定

Userエンティティの修正

以下のように修正します。

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser; // FOSUserBundleのUserをインポート

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User extends BaseUser  // BaseUserを継承
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;  // protectedに変更

    public function __construct()  // コンストラクタを追加
    {
        parent::__construct();     // 親クラスのコンストラクタをコール
    }
    // ...
}

セキュリティ設定

セキュリティ設定を以下のように修正します。前回行ったHTTP Basic認証の設定は破棄します。

security:
    # 認証方法
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            # フォームログイン
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
            logout: true
            anonymous: ~

    # アクセス制御
    access_control:
        - { path: ^/unsei, role: ROLE_ADMIN }

    # ユーザー情報取得方法
    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

    # パスワード暗号化方法
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

FOSUserBundleの設定

config.ymlにFOSUserBudleの設定を追加します。

...
# FOSUserBundle
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: AppBundle\Entity\User

ルーティング設定

routing.ymlにFOSUserBudleのルーティング設定を追加します。

...

# 全ての機能を使う場合(セルフ登録、パスワード変更、プロフィール変更等)
# fos_user:
#     resource: "@FOSUserBundle/Resources/config/routing/all.xml"

# ログイン、ログアウトだけを使う場合
fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

使用する機能毎に、いくつかのxmlリソースファイルがあります。おみくじサイトではログイン、ログアウトしか使わないためfos_user_securityのみを設定します。

DBスキーマの更新

Userエンティティを修正したので、DBスキーマを更新します。

$ php bin/console doctrine:schema:update --force

設定の確認

DBスキーマの確認

$ sqlite3 var/data.db3

sqlite> .table
unsei  user

sqlite> .schema user
CREATE TABLE user (
    id INTEGER NOT NULL,
    username VARCHAR(255) NOT NULL,
    username_canonical VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    email_canonical VARCHAR(255) NOT NULL,
    enabled BOOLEAN NOT NULL,
    salt VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    last_login DATETIME DEFAULT NULL,
    locked BOOLEAN NOT NULL,
    expired BOOLEAN NOT NULL,
    expires_at DATETIME DEFAULT NULL,
    confirmation_token VARCHAR(255) DEFAULT NULL,
    password_requested_at DATETIME DEFAULT NULL,
    roles CLOB NOT NULL,
    credentials_expired BOOLEAN NOT NULL,
    credentials_expire_at DATETIME DEFAULT NULL,
    PRIMARY KEY(id)
);
CREATE UNIQUE INDEX UNIQ_8D93D64992FC23A8 ON user (username_canonical);
CREATE UNIQUE INDEX UNIQ_8D93D649A0D96FBF ON user (email_canonical);

UserエンティティはFOSUserBundleのUserクラスを継承している為、項目が大分増えていることが分かります。

※ awk を使うとスキーマを見やすく整形して表示できます。

$ sqlite3 var/data.db3 '.schema' | awk '/CREATE TABLE/ {sub("\\(", "\(\n\t"); gsub(", ", ",\n\t"); print $0}'

ルートの確認

$ php bin/console debug:router --show-controllers | grep user

fos_user_security_login   GET|POST  ANY  ANY  /login        FOSUserBundle:Security:login
fos_user_security_check   POST      ANY  ANY  /login_check  FOSUserBundle:Security:check
fos_user_security_logout  GET       ANY  ANY  /logout       FOSUserBundle:Security:logout

ログイン、ログアウトのルートが追加されています。

Commandの確認

$ php bin/console list | grep user

fos:user:activate          Activate a user
fos:user:change-password   Change the password of a user.
fos:user:create            Create a user.
fos:user:deactivate        Deactivate a user
fos:user:demote            Demote a user by removing a role
fos:user:promote           Promotes a user by adding a role

FOSUserBundleのコマンド追加されていることが分かります。

ユーザー情報の追加

コマンドを使ってDBにユーザー情報を追加します。

$ php bin/console fos:user:create

Please choose a username: admin [ENTER]
Please choose an email: admin@omikuji.com [ENTER]
Please choose a password: admin123 [ENTER]

Created user admin


$ php bin/console fos:user:promote

Please choose a username: admin [ENTER]
Please choose a role: ROLE_ADMIN [ENTER]

Role "ROLE_ADMIN" has been added to user "admin".

DBのデータを確認します。

$ sqlite3 var/data.db3

sqlite> .mode line
sqlite> select * from user;

                   id = 1
             username = admin
   username_canonical = admin
                email = admin@omikuji.com
      email_canonical = admin@omikuji.com
              enabled = 1
                 salt = pvafp49po7kcok8c8c000ssg0o44gc8
             password = $2y$13$TVWcRVX0k3wdSrYH/uZrs.xjYDKyqtbA9WHLe2TWBO7DIZEoVTTTG
           last_login = 
               locked = 0
              expired = 0
           expires_at = 
   confirmation_token = 
password_requested_at = 
                roles = a:1:{i:0;s:10:"ROLE_ADMIN";}
  credentials_expired = 0
credentials_expire_at = 

adminユーザーが追加されて、roleROLE_ADMINになっていることが分かります。

動作確認

  1. http://localhost:8000/unsei にアクセスすると、
    http://localhost:8000/login にリダイレクトされます。
  2. ユーザー名:admin、パスワード:admin123 でログインすると、
    http://localhost:8000/unsei が表示されます。
  3. http://localhost:8000/logout にアクセスすると、ログアウトしてホームが表示されます。

Viewのオーバーライド

ログイン画面がイマイチなので、BundleのViewをオーバーライドして修正します。

app/Resourcesディレクトリの直下ににBundle名でディレクトリを作ることで、Bundleのリソースファイルをオーバーライドすることができます。

omikuji
├── app
│   └── Resources
│       ├── FOSUserBundle  # Bundle名のディレクトリを作り、①と同じ構成にする
│       │   └── views
│       │       ├── Security
│       │       │   └── login.html.twig  (a)をコピーして修正
│       │       └── layout.html.twig     (b)をコピーして修正
│       └── views
│           └── ...
└── vendor
     └── friendsofsymfony
          └── user-bundle
              └── Resources  ①
                   └── views
                       ├── Security
                       │   └── login.html.twig  (a)
                       └── layout.html.twig     (b)

以下がオーバーライドして修正したViewになります。

{% extends "base.html.twig" %}

{% block body %}
    {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
        <div>
            {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} |
        </div>
    {% else %}
        {% 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>
    {% endif %}
{% endblock %}

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

{% trans_default_domain 'FOSUserBundle' %}

{% block fos_user_content %}
    {% if error %}
        <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}

    <form action="{{ path("fos_user_security_check") }}" method="post">
        <input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />

        <div>
            <label for="username">{{ 'security.login.username'|trans }}</label>
            <input type="text" id="username" name="_username" value="{{ last_username }}" required="required" />
        </div>

        <div>
            <label for="password">{{ 'security.login.password'|trans }}</label>
            <input type="password" id="password" name="_password" required="required" />
        </div>

        <div>
            <input type="checkbox" id="remember_me" name="_remember_me" value="on" />
            <label for="remember_me">{{ 'security.login.remember_me'|trans }}</label>
        </div>

        <input type="submit" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans }}" />
    </form>
{% endblock fos_user_content %}

メニューの修正

ユーザーのログイン状態に応じて、メニューの内容を変更するよう修正します。

<ul class="menu">
    <li><a href="{{ path('omikuji') }}">おみくじ</a></li>

    {% if app.user %}  {# もし、ログインしていたら #}
        <li><a href="{{ path('unsei_index') }}">管理</a></li>
        <li><a href="{{ path('fos_user_security_logout') }}">ログアウト</a></li>
    {% else %}
        <li><a href="{{ path('fos_user_security_login') }}">ログイン</a></li>
    {% endif %}
</ul>


以上で、おみくじサイトへのFOSUserBundleの組み込みは完了です。Bundleのおかげで、テスト済でノウハウの詰まった機能をプラグインできました。とてもありがたいですね。何か欲しい機能がある時は、既にBundleがないか以下を探してみるのが良いでしょう。


Comments



159​ +13 = ?
blog/2015-12-17/超入門_symfony3_bundle.txt · 最終更新: 2016/02/02 16:30 by tsubo