東京都府中市、渋谷区のWEB制作会社Maromaroのブログです

2020.05.25

Sasaki

docker + laravelで作る超簡易フォーム

今回は勉強会で使ったネタです。
docker + laravelで作る超簡易フォーム
を作ってみました。

少々説明が必要な部分もありますが、基本的にはコピペで進めるようになっています。(※動かない部分があるかもしれない・・・)

今回は入力画面、完了画面からなるシンプルなお問合せフォームメール送信のみを行います。

HTMLは味気ないですが、ご容赦ください・・・。

では、早速いってみましょう!

前提条件

勉強会で行った際に確認をしたOSは下記です。

MacOS 10.15(Catalina)
Windows 10pro

dockerを事前に登録&インストールする必要があります。

※無料です
https://www.docker.com/
こちらで、アカウントを作成し、自身のOSにあったdockerをインストールしましょう。
※Windows10 Homeではそのままインストールできないため、茨の道をいくことになるかもしれません。(非推奨

まずはDockerで環境構築

docker-compose.ymlを作成します

version: '3'
services:
  php:
    container_name: php
    build: ./docker/php
    volumes:
      - .:/var/www
    depends_on:
      - db
  nginx:
    image: nginx
    container_name: nginx
    ports:
      - 8011:80
    volumes:
      - .:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: XXXXXXXX
    ports:
      - '8001:3306'
    volumes:
      - mysql_data:/var/lib/mysql
volumes:
  mysql_data:


続いて、下記のディレクトリとファイルを作成しましょう。
docker/nginx/default.conf
docker/php/Dockerfile
docker/php/php.ini

それぞれのファイルは下記のようにします。

docker/nginx/default.conf

server{
    listen 80;
    index index.php index.html;
    root /var/www/public;
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
        index  index.html index.php;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

docker/php/Dockerfile

FROM php:7.2-fpm
COPY php.ini /usr/local/etc/php/

RUN apt-get update \
  && apt-get install -y zlib1g-dev mariadb-client \
  && docker-php-ext-install zip pdo_mysql exif

#GDのインストール
RUN apt-get install -y wget libjpeg-dev libfreetype6-dev
RUN apt-get install -y libmagick++-dev \
libmagickwand-dev \
libpq-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
libwebp-dev \
libxpm-dev

RUN docker-php-ext-configure gd
RUN docker-php-ext-install -j$(nproc) gd

#Composer install
COPY --from=composer /usr/bin/composer /usr/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER 1

ENV COMPOSER_HOME /composer

ENV PATH $PATH:/composer/vendor/bin

WORKDIR /var/www

#RUN composer global require "laravel/installer"

docker/php/php.ini

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

dockerを起動する

つづいて下記のコマンドを打っていきます。

docker-compose up

☆テスト用DBを作る
docker-compose exec db bash
mysql -u root -p
XXXXXXXX
create database test;

☆laravel インストール
docker-compose exec php bash
composer global require laravel/installer
laravel new .

インストールができたら.envファイルを設定します

.env.exampleを.envにリネーム
※このとき.envはgitなどの対象としないようにしましょう!

下記の設定を行う。

#下記はSMTPアカウントを所持していればそれを使いましょう。
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
#ここまで

MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
DB_CONNECTION=mysql
#※今回DBは使いません
#ここがdbになるのが肝
DB_HOST=db
DB_PORT=3306
#testは create database test; で作ったものです。
DB_DATABASE=test

#DBのusernameはrootにし、DB_PASSWORDはdocker-compose.ymlで指定したものを使います。
DB_USERNAME=root
DB_PASSWORD=XXXXXXXX

routesの設定

routes/web.phpに追加する

Route::get('contact', 'ContactController@index')->name('contact.index'); 
Route::post('contact/complete', 'ContactController@complete') ->name('contact.complete');

1番目の引数がマッチするURLです。
完了画面はRoute::getではなく、Route::postとなっていることに注目!

Controllerファイルの作成

app/Http/Contollers/ContactController.phpを作成します。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ContactController extends Controller
{
  public function index()
  {
    //フォーム入力ページのviewを表示
    return view('contact.index');
  }

  public function complete(Request $request)
  {
    $this->validate($request,
      [
        'contact_type'=> 'required|string|max:191',
        'name' => 'required|string|max:191',
        'tel' => array('required','string','max:191','regex:/^([0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}|[0-9]{5,12})$/'),
        'email' => 'required|email|string|email|max:191',
        'message' => 'string|max:400',  ]
    );

    // 二重送信防止のためトークンを発行
    $request->session()->regenerateToken();

    \Mail::send(new \App\Mail\ContactMail([
      'email' => $request->email,  //フォームに入力したメールアドレス
      'name' => $request->name,  //フォームに入力した名前
      'from' => env('MAIL_FROM_ADDRESS'),
      'from_name' => env('MAIL_FROM_NAME'),
      'subject' => 'お問い合わせ受付完了のお知らせ',  //メールの件名

      'contact_type' => $request->contact_type, //お問い合わせの種類
      'tel' => $request->tel, //お電話番号
      'message' => $request->message, //ご利用目的
    ]));  //to.blade.phpに反映する

    //完了ページのviewを表示
    return view('contact.complete');
  }
}

Viewの作成を行います

resources/views/layouts/common.blade.php

レイアウトの親ファイルです。

<!doctype html>
<html>
<head>
  <title>タイトル</title>
  <meta charset="UTF-8">
  <meta name="format-detection" content="telephone=no">
</head>
<body>
@yield('content')
@yield('scripts')
</body>
</html>

resources/views/contact/index.blade.php

入力画面です。

@extends('layouts.common')

@section('content')

  {{-- エラーメッセージ --}}
  @if (count($errors) > 0)
    <ul class="alert alert-danger" role="alert">
      @foreach ($errors->all() as $error)
        <li class="ml-4">{{ $error }}</li>
      @endforeach
    </ul>
  @endif

  {{-- 本体 --}}

  <div id="form" data-target="form">


    <div id="contact_form01">
      {{ Form::open(['route' => 'contact.complete']) }}
      <table border="1" class="list_03 books_table01">
        <tbody>
        <tr>
          <td>お問い合わせの種類</td>
          <td>
            {{Form::select('contact_type', [
              '' => 'お問い合わせの種類をご選択ください',
              '出版物の入手方法' => '出版物の入手方法',
              'その他' => 'その他']
            )}}
          </td>
        </tr>
        <tr>
          <td>ご氏名</td>
          <td>{!! Form::text('name', null, ['class' => 'form_control']) !!}</td>
        </tr>
        <tr>
          <td>お電話番号</td>
          <td>{!! Form::tel('tel', null, ['class' => 'form_control']) !!}
          </td>
        </tr>
        <tr>
          <td>メールアドレス</td>
          <td>{!! Form::email('email', null, ['class' => 'form_control']) !!}
          </td>
        </tr>
        <tr>
          <td>本文</td>
          <td>{!! Form::textarea('message', null, ['class' => 'form_control', 'rows' => '6']) !!}
          </td>
        </tr>
        </tbody>
      </table>
      <div class="form_submit_btn">{!! Form::submit('送信', ['class' => 'btn']) !!}</div>
    </div>
    {{ Form::close() }}
  </div>
@endsection

@section('scripts')
@endsection

resources/views/contact/complete.blade.php

完了画面です。

@extends('layouts.common')

@section('content')
<p>送信が完了しました</p>
@endsection

@section('scripts')
@endsection

Formクラスがつかえるようにする

きっと、「Class ‘Form’ not found」なんて出るので、下記のコマンドを打ち込む

composer require laravelcollective/html

する。

ここまでで、入力画面が表示されているはずです。

http://localhost:8011/contact
にアクセスしてみましょう!
※8011は「docker-compose.yml」で設定したporstの値です。

翻訳ファイルの修正

エラーが英語でしたよね?
resources/lang/ja/validation.phpを作成して、対訳を行います。

https://readouble.com/laravel/5.6/ja/validation-php.html
こあたりをありがたく使わせていただきます。

config/app.php
localeをja(日本語)にしましょう。

'locale' => 'ja',

このままだと中途半端に日本語になっているため、「resources/lang/ja/validation.php」ファイルをさらに調整します。

    'attributes' => [
      'contact_type'=>'お問合せの種類',
      'name'=>'名前',
      'tel'=>'電話番号',
      'email'=>'メールアドレス',
      'message'=>'本文',
    ],

こうすることで、name値も日本語に置き換わります。

メール送信用のViewを作成

{{ $content['name'] }} 様<br>
<br>
この度はお問い合わせいただき誠にありがとうございます。<br>
お客様からのお問い合わせを受付けましたので、ご連絡いたします。<br>
<br>
============================<br>
<br>
お問い合わせ種別:{{ $content['contact_type'] }}<br>
氏名{{ $content['name'] }}<br>
電話番号:{{ $content['tel'] }}<br>
メールアドレス:{{ $content['email'] }} <br>
本文:{!! nl2br($content['message']) !!}<br>
<br>
============================<br>
<br>
内容を確認のうえ、担当よりご回答いたします。<br>
<br>

最後にメール送信用のクラスを作成します

app/Mail/ContactMail.phpファイルを作成しましょう。

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class ContactMail extends Mailable
{
  use Queueable, SerializesModels;

  protected $content;
  protected $viewStr;

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

  /**
   * Build the message.
   *
   * @return $this
   */
  public function build()
  {
    return $this->view('contact.emails.to')
      ->to($this->content['email'], $this->content['name'])
      ->from($this->content['from'], $this->content['from_name'])
      ->subject($this->content['subject'])
      ->with([
        'content' => $this->content,
      ]);
  }
}

おめでとうございます!? 恐らくここまででメールが送信できるようになったと思います。