[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 を利用するという手があります。見た目をビュー以外でコントロールする構造は賛否が分かれるところではありますが簡単に変更できるので必要に応じて使ってみて下さい。

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

Laravel 5.6 を使い、お問い合わせ用のコンタクトフォームを作っていきます。
古いバージョンでも概ね同じですが、Bootstrap 4 が使われていることに注意して下さい。

まず最初にメールに関する初期設定を行っておきます。設定箇所は .env ファイルの「MAIL_」から始まる項目です。お使いのアカウントに合わせて各項目を書き換えて下さい。
MAIL_ENCRYPTION には tls, ssl, null のいずれかが設定できます。

次の2行がない場合は .env に書き足すか、 config/mail.php の from の項目を編集して下さい。

MAIL_FROM_ADDRESS=送信元のメールアドレス
MAIL_FROM_NAME=送信者の名前

次に routes/web.php でルーティングを用意します。

Route::get('/contact', 'ContactController@form')->name('contact');
Route::post('/contact', 'ContactController@send')->name('contact.send');
Route::get('/contact/result', 'ContactController@result')->name('contact.result');

それぞれフォーム画面、送信処理、送信結果の3つです。

artisan のコマンドを使ってコントローラーを作成します。

php artisan make:controller ContactController

app\Http\Controller に ContactController.php が作られます。
まずはフォーム画面となる form() メソッドを追加します。

    public function form()
    {
        return view('contact.form');
    }

ひとまず最低限ビューが表示できるようにしたので Blade テンプレートファイルを用意します。場所は resources/views です。contact フォルダを新しく作り、その中に form.blade.php ファイルを作成します。
すでに layouts/app.blade.php などの親テンプレートが存在する場合はそのまま使っていきますが、ない場合は作成します。

layouts/app.blade.php

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        @yield('content')
    </div>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

contact/form.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="card">
        <div class="card-body">
            <form method="POST" action="{{ route('contact.send') }}">
                {{ csrf_field() }}

                <div class="form-group">
                    <label for="formInputName">Name</label>
                    <input type="text" class="form-control" id="formInputName" name="name" value="{{ old('name') }}">

                    @if ($errors->has('name'))
                        <span class="help-block">
                            <strong>{{ $errors->first('name') }}</strong>
                        </span>
                    @endif
                </div>

                <div class="form-group">
                    <label for="formInputEmail">Email address</label>
                    <input type="text" class="form-control" id="formInputEmail" name="email" value="{{ old('email') }}">

                    @if ($errors->has('email'))
                        <span class="help-block">
                            <strong>{{ $errors->first('email') }}</strong>
                        </span>
                    @endif
                </div>

                <div class="form-group">
                    <label for="formInputEmail">Message</label>
                    <textarea class="form-control" id="formInputMessage" name="message" value="{{ old('message') }}">{{ old('message') }}</textarea>

                    @if ($errors->has('message'))
                        <span class="help-block">
                            <strong>{{ $errors->first('message') }}</strong>
                        </span>
                    @endif
                </div>

                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </div>
</div>
@endsection

Laravel ではメールの送信を Mailable クラスを通して行っています。メールの種類ごとに次のコマンドでメールを処理するためのクラスを作ります。「Contact」の部分は好きな名前にして下さい。(例: OrderShipped)

php artisan make:mail Contact

これで app\Mail フォルダに Contact.php が作られます。
入力内容に合わせて文面を表示するために、引数として受け取れるようにしていきます。

class Contact extends Mailable
{
    use Queueable, SerializesModels;

    public $data = [];

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($data)
    {
        $this->data['inputs'] = $data;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('emails.contact')->with($this->data);
    }
}

プロパティ $data を追加し、コンストラクタで inputs に代入します。(inputs の名前は何でも構いません) 代入したデータは build() の with() でビューに渡しています。
連想配列 inputs に入れずに直接代入するのが一般的ですが、Mailable では $message という変数は他のデータが入っていて使えないため、上書きされるのを防ぐためにこのような形をとっています。

そして build() で指定したメールの文面となるビューファイルを用意します。view() を使ったときは html メールになります。プレーンテキストで送りたい場合は代わりに text() を使って下さい。

emails/contact.blade.php

<p>Name: {{ $inputs['name'] }}</p>
<p>Email: {{ $inputs['email'] }}</p>
<p>{!! nl2br( $inputs['message'] ) !!}</p>

次は ContactController に戻り、冒頭に use を書き足します。

use App\Mail\Contact;
use Illuminate\Support\Facades\Mail;

そして送信処理 send() を実装します。

    public function send(Request $request)
    {
        $rules = [
            'name' => 'required|string',
            'email' => 'required|email',
            'message' => 'required'
        ];
        $this->validate($request, $rules);

        $to = [
            ['email' => 'example@gmail.com', 'name' => 'Your Name']
        ];

        $data = $request->only('name', 'email', 'message');
        Mail::to($to)->send(new Contact($data));

        return redirect()->route('contact.result');
    }

送信時にバリデーションを行って入力欄に空欄がないか、メールアドレスは正しいかをチェックしています。 入力内容に問題がなければ送信処理を行います。$to には送信先を指定します。複数指定することができます。 名前が必要ない場合は null にしておきます。

今回作った Contact クラスの引数として入力内容を渡し、send() で送信です。

送信が終わったら redirect() で完了ページに移動します。送信完了ページはテンプレートを表示するだけです。

    public function result()
    {
        return view('contact.result');
    }

contact/result.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="card">
        <div class="card-body">
            <h3>Thanks.</h3>
            <p>Your email has been successfully sent.</p>
        </div>
    </div>
</div>
@endsection

try catch や Mail::failures() を使ってエラー時の処理を追加するのも良いと思いますが、基本的には 送信時のエラーしか拾えません。送信には成功したものの実際には受信側の容量が不足していて受け取れなかったなどのケースもありえますので注意して下さい。

[PHP]2018年現在使っているおすすめの開発ツール・開発環境(Mac)

PHPでの開発を始めた頃と現在では随分使っているツールや開発環境が変わってきました。
そこで現在主に使用しているものを紹介してみます。

開発機

MacBook Pro (15-inch, Early 2011)

ものすごく古いマシンなのは承知していますが簡単にメモリやストレージを増設できるので重宝しています。メモリを 16GB に増設し、ハードディスクを SSD に換装したのでまだ使えますが、グラフィックメモリが 256MB しか無いので時間の問題です。もう少し新しいモデルも持っているのですがメモリが増設できないため(8GBでは足りませんでした)そちらは使っていません。

IDE, テキストエディタ

PhpStorm

もはや説明不要ですが JetBrains の PHP 専用 IDE です。これの代わりになるものは無いと思います。
最新バージョンの PHP や各種フレームワークへの対応も早いので安心感があります。

Visual Studio Code

Microsoft がオープンソースで開発しているテキストエディタです。
一時期 Atom を中心に使っていましたが現在は完全にこちらです。
プラグインがひと通り出揃ったので構文エラーのチェックや定義への移動など本格的な事もできます。

ローカルサーバー, データベース

Vagrant

Cent OS 7 を Virtual Box で利用しています。最初の構築が大変ですが自由度が高いので好きなようにカスタマイズできます。サーバー自体の勉強にもなるので触れておいて損はないと思います。
今は使い慣れた Apache で動かしていますがいずれは nginx も覚えないといけないなと考えています。

Valet

Laravel が作ったローカル開発環境です。MAMP に近いものではありますが、ホスト名とフォルダの割当てが簡単にできるのでかなり楽です。詳細なサーバー設定が必要ないのであればこれで十分です。

その他開発ツール

SourceTree

GitHub や BitBucket のリボジトリを管理する定番のツールです。
ターミナルで十分な時もありますがブランチの状況やコンフリクトの確認がしやすいので持っていると心強いです。

Postman

POST, GET などのリクエストをテストするためのツールです。Ajax 処理や API 開発の際にあると便利です。

Sequel Pro

GUI でデータベースを操作するためのツールです。ちょっとした修正や正しくテーブルが作られているかの確認に使ったりします。

ForkLift

2ペインに分割できる Finder のようなアプリケーションです。不可視ファイルの表示/非表示が簡単にできるので見やすいです。一応ファイル一括リネームや FTP 用のツールにもなります。

グラフィック

Photoshop

なんだかんだで手放せないツールです。Affinity Photo なども試しはしたものの他の方とデータをやり取りするのに不便なので変えられません。
主な用途はバナー作成や写真の加工です。以前は装飾されたボタンや文字を作るのに使うことが多かったですが最近はシンプルなデザインがほとんどなので立体的な文字や透明感のあるボタンなどは需要が減ってきました。

Illustrator

ロゴやアイコンを作るのに使用しています。JavaScript で自動処理ができるので各アイコンを一気に SVG として書き出すなどの処理が簡単にできます。最近ではウェブでもベクター画像を取り扱うことが珍しくなくなったので使う機会は多いです。

Sketch

ウェブデザインを組み上げるには Sketch を使っています。Photoshop で加工した画像や Illustrator で作ったロゴなどを配置してレイアウトを作ります。
ヘッダーやメニュー等のパーツの使いまわしや修正に強いのが便利です。最近はページをリンクさせて遷移のプレビューができるといったプロトタイピングツールとしての機能も追加されたのでサイトの雰囲気を確認するのにも役立っています。