Symfony7~Docker環境構築からログイン機能作成まで
数年ぶりに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のパッケージを準備します。
# ベースイメージとして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 /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配下で接続の設定をします。
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のポートは別々になっていますが、基本ポート被りがなければホストとコンテナ一緒の番号で大丈夫です。
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を修正します。
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下にファイルが生成されたことを確認されましたら、以下のようにバックエンド実装を行います。
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,
]);
}
}
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ファイルを作成します。
<!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>
© {{ "now"|date("Y") }} My Symfony App. All rights reserved.
</footer>
</body>
</html>
また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;
}
次にログイン画面を作成していきます。
{% 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 %}
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;
}
最後に質素ですがログイン後の遷移先のトップページを作成します。
{% extends 'base.html.twig' %}
{% block title %}Top Page{% endblock %}
{% block body %}
<h1>Welcome to the Top Page!</h1>
{% endblock %}
これでコード記述は完了です。実際にログイン画面で動作確認します。
-
ログイン画面
-
トップページ(ログイン画面から遷移)
まとめ
今回Dockerによる環境構築〜ログイン機能実装で、Symfonyの基本的な流れとなる実装を行なってみました。少しでも参考になれば幸いです。
またこれまで修正内容についてgithubよりダウンロードできますので良ければご参照ください。
Discussion