[PHP]Symfony 4 とデータベースを使ったCRUDアプリケーションの作り方

Symfony 4 と MySQL などのデータベースの組み合わせで基本的なアプリケーションを作っていきます。今回は例としてブログ風に記事を表示できるようにします。

Symfony 本体のインストール

Composer を使って本体をインストールします。「symfony-crud」の部分はプロジェクト名なので必要に応じて変更して下さい。

composer create-project symfony/skeleton symfony-crud

Doctrine のインストールとデータベース周辺の設定

まずはデータベースを操作するためのライブラリをインストールします。

composer require doctrine maker

インストールが完了すると .env ファイルにデータベースへの接続設定に関する項目が書き足されます。
DATABASE_URL の箇所を接続するデータベースに合わせて書き換えて下さい。

DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
SQLite を使う場合は次のようにします。
DATABASE_URL=sqlite:///%kernel.project_dir%/var/data.db

接続するデータベースはあらかじめ用意しておくか、コマンドから作成することもできます。

php bin/console doctrine:database:create

次にエンティティ(モデル)を作成します。今回は記事を表す Post という名前で作っていきます。記事にはID、タイトル、本文、作成日時を持たせます。「Post」の部分は好きな名前をつけることができます。

php bin/console make:entity Post

エンティティのフィールド(タイトルなど)は対話式で作っていきます。コンソールで、カラム名やタイプを聞かれるのでそれを指定するだけで自動的に作られます。中断した場合でも上記コマンドで再度編集できます。今回は下記の列を用意します。

  • id integer(最初からあります)
  • title string, length=100
  • content text
  • created_at datetime, nullable

マイグレーション(テーブルの作成)はエンティティを作ってしまえばその情報をもとに自動的に作ることができるので自分でファイルを用意する必要はありません。下記のコマンドで現在のデータベースとの差分をもとにマイグレーションファイルが作られます。

php bin/console doctrine:migrations:diff

ファイルが作成されたら実際にデータベースにテーブルを作成します。

php bin/console doctrine:migrations:migrate

フォームの作成

登録するためのデータを入力してもらうためにはテキストボックスなどの入力フォームが必要になります。まずはフォームに必要なコンポーネントと、CSRF(クロスサイトリクエストフォージェリ)対策のためのコンポーネントをインストールします。

composer require form
composer require symfony/security-csrf

次に src/Form フォルダを用意し、PostType.php というファイルを作ります。

<?php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PostType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title')
            ->add('content')
            ->add('created_at')
            ->add('save', SubmitType::class);
    }
}

buildForm() の箇所で表示したい入力項目を追加します。今回はタイトル、本文、作成日時、保存ボタンです。

[PHP]Symfonyを使ったメールフォームの作り方

フレームワーク Symfony 4.0 を使ってメールフォームを作ります。新規プロジェクトとして作っていくのですでに作成済みの箇所は読み飛ばして下さい。

Symfony のインストール

本体のインストールは Composer で行います。「symfony-contact」の部分はプロジェクト名なので好きな名前に書き換えて下さい。

composer create-project symfony/skeleton symfony-contact

コントローラー

Symfony のコントローラーは src/Controller フォルダにあります。「ContactController.php」ファイルを作成して下さい。
今回必要なページはフォーム画面と送信完了画面です。Symfony ではルーティングの方法がいくつかありますが、今回はコントローラーのアノテーション(コメント)を使います。

メールフォームを作成する上でいくつかのコンポーネントをインストールする必要があります。


アノテーションによるルーティングを行うためのコンポーネント

composer require annotations

フォームを表示するためのコンポーネント

composer require form

CSRF(クロスサイトリクエストフォージェリ)対策のためのコンポーネント

composer require symfony/security-csrf

バリデーション(入力値の検証)を行うためのコンポーネント

composer require validator

メールを送信するための Swift Mailer

composer require mailer

コントローラーの全体像は次のようになりました。

<?php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

class ContactController extends Controller
{
    /**
     * @Route("/contact", methods={"GET", "POST"}, name="contact")
     */
    public function form(Request $request)
    {
        $form = $this->createFormBuilder()
            ->add('name', TextType::class, [
                'constraints' =>[
                    new Assert\NotBlank(),
                    new Assert\Length(['max' => 100])
                ]
            ])
            ->add('email', EmailType::class, [
                'constraints' => [
                    new Assert\NotBlank(),
                    new Assert\Email()
                ]
            ])
            ->add('message', TextareaType::class, [
                'constraints' => [
                    new Assert\NotBlank()
                ]
            ])
            ->add('submit', SubmitType::class, ['label' => 'Submit'])
            ->getForm();

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $this->send($data);
            return $this->redirectToRoute('contact_result');
        }

        return $this->render('contact/form.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    public function send($data)
    {
        $subject = 'Test Mail';
        $message = (new \Swift_Message($subject))
			->setFrom('from@example.com')
			->setTo('to@example.com')
			->setBody(
				$this->renderView(
					'emails/contact.html.twig',
					[
						'name' => $data['name'],
						'email' => $data['email'],
						'message' => $data['message']
					]
				),
				'text/html'
			);

        $this->get('mailer')->send($message);
    }

    /**
     * @Route("/contact/result", methods="GET", name="contact_result")
     */
    public function result()
    {
        return $this->render('contact/result.html.twig');
    }
}

createFormBuilder() でフォームを作っています。今回の入力項目は名前、メールアドレス、本文の3つで、それぞれを add() で追加しています。constraints がバリデーションルールで、必要に応じてルールを追加します。利用可能なルールについてはこちらを見て下さい。

ビュー

ビューを表示するためには Twig テンプレートエンジンをインストールします。

composer require twig

ビューファイルは templates フォルダに作成していきます。今回は Bootstrap 4 を利用するのでひとまず base.html.twig の <head> に次の行を追記します。

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

次に contact フォルダを作り、その中に contact.html.twig ファイルと result.html.twig ファイルを作ります。

contact.html.twig

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

{% block body %}
<div class="container">
    {{ form_start(form) }}
        {{ form_errors(form) }}

        <div class="form-group">
            {{ form_label(form.name) }}
            {{ form_widget(form.name, {'attr': {'class': 'form-control'}}) }}
            {{ form_errors(form.name) }}
        </div>

        <div class="form-group">
            {{ form_label(form.email) }}
            {{ form_widget(form.email, {'attr': {'class': 'form-control'}}) }}
            {{ form_errors(form.email) }}
        </div>

        <div class="form-group">
            {{ form_label(form.message) }}
            {{ form_widget(form.message, {'attr': {'class': 'form-control'}}) }}
            {{ form_errors(form.message) }}
        </div>

        <div class="form-group">
            {{ form_widget(form.submit, {'attr': {'class': 'btn btn-primary'}}) }}
        </div>
    {{ form_end(form) }}
</div>
{% endblock %}

result.html.twig

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

{% block body %}
<div class="container">
    <div class="card">
        <div class="card-body">
            <h3>Thanks.</h3>
            <p>Your email has been successfully sent.</p>
        </div>
    </div>
</div>
{% endblock %}

メールの送信

メールを送信するには送信サーバーの設定が必要です。先程の mailer コンポーネントをインストールすると .env ファイルに設定箇所が追加されるので、MAILER_URL の部分をお使いのメールアカウントに合わせて書き換えて下さい。
詳細についてはこちらをお読み下さい。

次にメールの文面を用意します。場所は templates/emails です。

contact.html.twig

<p>Name: {{ name }}</p>
<p>Email: {{ email }}</p>
<p>{{ message|e|nl2br }}</p>

これでメールフォームは完成です。


Bootstrap や Foundation などの有名な CSS フレームワークを使う場合は今回のように手作業でクラス名を付けずに Form Themes を利用するという手があります。見た目をビュー以外でコントロールする構造は賛否が分かれるところではありますが簡単に変更できるので必要に応じて使ってみて下さい。

Symfony 3 のインストールと基本構成

この記事では Symfony 3.2 のインストールと重要なフォルダの階層、ルーティングの基本などについて説明します。

インストーラーの準備

Mac, Linux

下記のようにして Symfony のインストーラーをダウンロード、インストールします。

 $ sudo mkdir -p /usr/local/bin
 $ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
 $ sudo chmod a+x /usr/local/bin/symfony
 

プロジェクトの作成は次のようにします。my_project_name の部分はプロジェクトのフォルダ名になりますので好きな名前をつけて下さい。

$ symfony new my_project_name

Windows

Windows の場合次のようにして実行ファイルをダウンロードします。

php -r "readfile('https://symfony.com/installer');" > symfony

ダウンロードした symfony というファイルをプロジェクトのフォルダに移動し「php symfony」のようにして使います。

プロジェクトのルートフォルダに移動しプロジェクトを作成します。
my_project_name の部分はプロジェクトのフォルダ名になりますので好きな名前をつけて下さい。

C:¥> cd projects
C:¥projects¥> php symfony new my_project_name

Composer を使う

PHP のバージョンが古かったり、Symfony のインストーラーが使えない場合は Composer を使ってインストールします。

composer create-project symfony/framework-standard-edition my_project_name

インストールされる主要コンポーネント

Symfony 3 にはデータベースまわりを扱う Doctrine ORM、メール送信のための Swift Mailer、テンプレートエンジンの Twig などが予めインストールされています。

主要ファイル構成

各種設定ファイルは app/config にまとめられています。
設定は YAML 形式のファイルで保存されており、ロケールやメールサーバー情報、データベースの接続情報などはここで編集します。

コントローラーは src/AppBundle/Controller にあります。
ホームページを表示するための DefaultController.php が見本として用意されています。

テンプレートファイルは app/Resources/views にあります。
テンプレートエンジンには Twig が使われています。

パブリックフォルダの名前は web です。独自ドメインもこのフォルダに割当てます。
画像や CSS、JS などのアセットはここに配置します。

ルーティング

ルーティングの指定方法は app/config/routing.yml で変更できますが、デフォルトではコントローラーでのアノテーション(コメント)が利用されます。

	/**
     * @Route("/api/posts/{id}")
     * @Method({"GET","HEAD"})
     */
    public function showAction($id)
    {
        //
    }

それぞれのビューコントローラーのメソッドの上にアノテーションとしてルーティングを記述します。
@Route にはパスを定義します {id} のように括弧で囲むと引数として受け取れるようになります。
@Method は HTTP メソッドを指定するために使います。デフォルトでは全てのメソッドが受け入れられる状態です。
@Method(“PUT”) のようにすると PUT メソッドのみに反応するようにできます。複数のメソッドに対応する場合 {“GET”,”HEAD”} のように波括弧を用いて GET か HEAD のときにみに限定できます。
コントローラーメソッドの命名は indexAction(), showAction(), editAction() のように 役割 + Action() の形にします。

パスの書式を指定したり、ルートに名前を付ける場合は次のようにします。

/**
 * @Route("/blog/{page}", name="blog_list", requirements={"page": "\d+"})
 */

name はルートの名前です。アプリケーション全体でユニークな名前にして下さい。
名前をつけておくと URL の出力が簡単になります。
requirements は正規表現を使ったルールです。
上記の例では {page} が数字であることが条件となっています。

デバッグ

web/app_dev.php にアクセスするとデバッグモードになります。
IPアドレスによる制限がかかっているため、Vagrant を使用している場合などでアクセス元が 127.0.0.1 でない場合 app_dev.php 内の in_array() に許可する IP を追加します。

!(in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])

ビューのレンダリング

コントローラーで直接レスポンスを出力するには次のようにします。

return new Response( "Hello, World!" );

Twig テンプレートを使ってレンダリングする場合はこのようになります。

return $this->render('default/index.html.twig', ['key' => 'value']);

Twig の詳細に関しては割愛しますが render() の第1引数にテンプレートファイル名を指定し、第2引数にテンプレートに渡す変数とその内容を連想配列の形で記述します。配列のキー名がそのまま変数名として渡されます。