Symfony7~Docker環境構築からログイン機能作成まで

2025/02/20に公開

数年ぶりにPHP Symfonyフレームワークを触れる機会があったので、勉強がてらSymfony環境構築からログイン機能まで作成してみました。

開発環境

今回はSymfonyプロジェクト作成含めて、Dockerで開発環境構築を行います。

  • OS
    macOS
  • DB
     PostgreSQL
  • ローカルサーバー
     Docker

技術選定

記事投稿時点で最新のSymfony7とPHP8.2を採用しています。

  • フロント
     PHP,JavaScript
  • バックエンド
     PHP8.2(Symfony7)

開発環境構築

ディレクトリ構成

└── Symfony7Project/
    ├── nginx/
    │   └── default.conf
    ├── symfomy7_app/ //プロジェクトのソース
    ├── docker-composer.yml
    └── Dockerfile

プロジェクト作成

Symfonyインストール

まずはホスト側でsymfonyのインストールするため準備を行います。

  • symfony/flexをインストール
composer global require symfony/flex

また必要な場合、composer.jsonファイルにallow-plugins設定を追加(Composer 2.2以降、プラグインの実行を許可する必要があるため)

composer config --global allow-plugins.symfony/flex true

これで準備ができたのでsymfonyをインストールします。

composer create-project symfony/skeleton symfony7_app

さらに後々のモデルファイルやフロント作成に必要なライブラリをインストールしていきます。

composer require symfony/maker-bundle --dev
composer require symfony/security-bundle
composer require symfony/orm-pack
composer require doctrine/doctrine-bundle
composer require symfony/validator
composer require symfony/asset

docker関連ファイル作成

次にローカルサーバーのDockerを使用するために関連ファイルを作成していきます。
DockerfileではPHPのパッケージを準備します。

Dockerfile
# ベースイメージとしてPHP-FPMを使用
FROM php:8.2-fpm

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libpq-dev \
    && docker-php-ext-install pdo pdo_pgsql

# Composerをインストール
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer

# 作業ディレクトリを設定
WORKDIR /var/www/symfony7_app

# プロジェクトファイルをコピー
COPY . .

# Composerの依存関係をインストール
RUN composer install

# Symfonyのマイグレーションを実行する場合(任意)
# RUN php bin/console doctrine:migrations:migrate

nginx配下で接続の設定をします。

nginx/default.conf
server {
    listen 80;
    server_name localhost;

    root /var/www/symfony7_app/public;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass app:9000;  # PHP-FPM サービスへの接続
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;   # fastcgi_params ファイルが必要
    }

    location ~ /\.ht {
        deny all;
    }
}

そしてdocker-compose.ymlを準備。今回はapp、nginx、dbの3つのコンテナを用意します。
※DBのポートは別々になっていますが、基本ポート被りがなければホストとコンテナ一緒の番号で大丈夫です。

docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./symfony7_app:/var/www/symfony7_app
    networks:
      - symfony_network

  nginx:
    image: nginx:alpine
    volumes:
      - ./symfony7_app:/var/www/symfony7_app
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - "80:80"
    networks:
      - symfony_network

  db:
    image: postgres:latest
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: root
      POSTGRES_DB: app
    ports:
      - "5433:5432"
    networks:
      - symfony_network

networks:
  symfony_network:
    driver: bridge

Dockerビルド、初回環境動作確認

それでは一式ファイル準備が完了しましたのでdocker環境を立ち上げます。

docker compose up --build

初回ビルド終了後appコンテナとdbのコンテナに入れるか確認します。

  • appコンテナ
docker exec -it symfony7project_app_1_bash
  • dbコンテナ
docker exec -it symfony7project_db_1psql -U app

※DBを明示的に確認されたい方はDBeaver等、SQLクライアントソフトウェアアプリケーションを用いて確認ください。

最後にローカルホストが表示されることも確認します。
localhost:80
下のSymfonyの画面が表示されでば問題なしです。

ログイン機能作成

Docker環境が整いましたので、簡易ではありますがログイン機能を作成していきます。

エンティティファイル作成〜テーブル作成

まずはログインに必要なUserテーブルを作成していきます。
Symfonyはエンティティファイルを作成してDBテーブルを作成するのがお作法の一つなので使用していきます。
ホスト側で下記のコマンドを実行ください

php bin/console make:user

モデル作成の際、コマンド上で問われる質問は下記で回答していきます。

  • 名前: User
  • ログインの識別子: email
  • エンティティにしますか?: yes

するとEntity/User.phpが作成されます。
こちらのファイルは自動生成されたものをそのまま使用します。

エンティティファイルの作成ができましたら、下記のコマンドを実行してテーブル作成します。

  • マイグレーションファイル作成※User.phpを元に作成される
php bin/console make:migration
  • マイグレーション実行
php bin/console doctrine:migrations:migrate

これでテーブル作成まで完了です。

Userテーブルにサンプルデータ挿入

次にログイン用のサンプルデータを作成します。
まずはUserテーブルのpasswordカラム登録用のパスワードをハッシュ値としたものを生成します。

php bin/console security:hash-password

上記コマンドを実行後、指定したいパスワードを入力してEnter押下でハッシュ化したパスワードが表示されます。

パスワードが生成されましたら、dbコンテナ内かSQLクライアントソフトウェアアプリケーション内でインサートを実行します。

INSERT INTO "user" (email, password, roles) VALUES ('test@example.com', ‘パスワードハッシュ値, '["ROLE_USER"]');

これでUserのデータ準備は完了です。

ログインパーツファイルの作成、修正

それでは本格的にログイン機能周りのバックエンド、フロントの実装を行っていきます。
まずはログイン機能のパーツ設定としてconfig/packages/secutity.yamlを修正します。

secutity.yaml
security:
    password_hashers:
        App\Entity\User: 'auto'
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            form_login:
                login_path: app_login
                check_path: app_login
                enable_csrf: true
                csrf_token_id: authenticate
                username_parameter: email
                password_parameter: password
                default_target_path: /topPage # ログイン後の遷移先
            logout:
                path: /logout
                target: /login
以下略

ここまで書けましたら、LoginController.phpを生成します。

php bin/console make:controller LoginController

またログイン後遷移先のトップページ用のコントローラを作成します。

php bin/console make:controller TopPageController

それぞれsrc/Controller下にファイルが生成されたことを確認されましたら、以下のようにバックエンド実装を行います。

LoginController.php
class LoginController extends AbstractController
{
    #[Route('/login', name: 'app_login')]
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        // ログインエラー取得
        $error = $authenticationUtils->getLastAuthenticationError();

        // 最後に入力されたユーザー名
        $lastUsername = $authenticationUtils->getLastUsername();

        // ユーザーがログインしている場合は /topPage にリダイレクト
        if ($this->getUser()) {
            return $this->redirectToRoute('app_top_page');
        }

        return $this->render('login/index.html.twig', [
            'last_username' => $lastUsername,
            'error' => $error,
        ]);
    }
}
TopPageController.php
class TopPageController extends AbstractController
{
    #[Route('/topPage', name: 'app_top_page')]
    public function index(): Response
    {
        return $this->render('top_page/index.html.twig', [
            'controller_name' => 'TopPageController',
        ]);
    }
}

Symfonyの場合はコントローラファイルにルーティング指定するのが特徴で、あとはLaravelのbladeファイルのように、rendarで遷移先のhtmlや送りたいパラメータを指定します。
これでバックエンドの実装は完了です。

次にフロントの実装です。
Symfonyでフロント作成する場合は拡張子twigで作成していきます。
htmlファイルはtemplates/配下に作成していきます。
まずはヘッダ用の共通htmlファイルを作成します。

base.html.twig
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Welcome{% endblock %}</title>
    {% block stylesheets %}
        <link href="{{ asset('css/base.css') }}" rel="stylesheet" />
    {% endblock %}
</head>
<body>
    <header>
        <h1>My Symfony App</h1>
        {% if app.user %}
            <form action="{{ path('_logout_main') }}" method="post" style="display: inline;">
                <button type="submit">ログアウト</button>
            </form>
        {% endif %}
    </header>

    <main>
        {% block body %}{% endblock %}
    </main>

    <footer>
        &copy; {{ "now"|date("Y") }} My Symfony App. All rights reserved.
    </footer>
</body>
</html>

またcssも作成しておきます。

base.css
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f4f4;
  color: #333;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

header {
  background-color: #007bff;
  color: white;
  padding: 15px 20px;
  display: flex; /* フレックスボックスを有効化 */
  justify-content: space-between; /* 左右にスペースを配置 */
  align-items: center; /* 高さを中央揃え */
  width: 100%;
  box-sizing: border-box;
}

header h1 {
  margin: 0;
  font-size: 1.8rem;
  color: white;
}

header form {
  margin: 0; /* 不要な余白を削除 */
}

main {
  flex: 1;
  padding: 20px;
  width: 100%;
  box-sizing: border-box;
}

button {
  cursor: pointer;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  background-color: #007bff;
  color: white;
  font-size: 1rem;
  transition: background-color 0.3s, opacity 0.3s;
}

button:hover {
  background-color: #0056b3;
  opacity: 0.9;
}

footer {
  background-color: #333;
  color: white;
  text-align: center;
  padding: 10px 20px;
  font-size: 0.9rem;
  width: 100%;
  box-sizing: border-box;
}

次にログイン画面を作成していきます。

login/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}ログイン{% endblock %}

{% block stylesheets %}
    {{ parent() }}
    <link href="{{ asset('css/login.css') }}" rel="stylesheet" />
{% endblock %}
{% block body %}
    <h1>ログイン</h1>

    {% if error %}
        <div class="alert alert-danger">
            {{ error.messageKey|trans(error.messageData, 'security') }}
        </div>
    {% endif %}

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

        <div>
            <label for="inputEmail">メールアドレス</label>
            <input type="email" id="inputEmail" name="email" value="{{ last_username }}" required autofocus>
        </div>

        <div>
            <label for="inputPassword">パスワード</label>
            <input type="password" id="inputPassword" name="password" required>
        </div>

        <button type="submit">ログイン</button>
    </form>
{% endblock %}
login.css
body {
  font-family: Arial, sans-serif;
  background-color: #f7f7f7;
  margin: 0;
  padding: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

header {
  background-color: #007bff;
  color: white;
  padding: 15px;
  text-align: center;
  width: 100%;
  box-sizing: border-box;
}

header h1 {
  margin: 0;
}

main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
  box-sizing: border-box;
}

main h1 {
  margin-bottom: 20px;
  font-size: 1.8rem;
  color: #333;
}

form {
  background-color: #fff;
  padding: 30px;
  border-radius: 12px;
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
  width: 100%;
  max-width: 450px;
}

form div {
  margin-bottom: 15px; /* 項目間の余白を追加 */
}

input[type="email"],
input[type="password"] {
  width: 100%;
  padding: 10px;
  margin: 10px 0;
  border: 1px solid #ccc;
  border-radius: 4px;
}

button {
  width: 100%;
  padding: 10px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}

最後に質素ですがログイン後の遷移先のトップページを作成します。

top_page/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}Top Page{% endblock %}

{% block body %}
    <h1>Welcome to the Top Page!</h1>
{% endblock %}

これでコード記述は完了です。実際にログイン画面で動作確認します。

  • ログイン画面

  • トップページ(ログイン画面から遷移)

まとめ

今回Dockerによる環境構築〜ログイン機能実装で、Symfonyの基本的な流れとなる実装を行なってみました。少しでも参考になれば幸いです。
またこれまで修正内容についてgithubよりダウンロードできますので良ければご参照ください。
https://github.com/nakaryo19/Symfony7Project

Discussion