閲覧が無い故に良いメモ帳と化したプログラマーブログ

主にphp関連の技術ブログ。閲覧が無い為、マークダウンが使える良いメモ帳と化している。誤ってアカウントパスワードを書いても大丈夫だ。なぜなら誰も閲覧しないからな。安心のブログシステムである。

LaravelでWEBサービスを立ち上げる為のテンプレートを用意する

概要

Laravelを用いてWebサービスを立ち上げる際の基本的な構成を用意する。 最近リポジトリパターンが流行っているようなので、その構成をテンプレート化しておいてすぐに使えるように用意しておく。 この構成にどんな意味があるかは手を動かしながら理解していってほしい。

1.プロジェクト作成 Laravel-Installerでプロジェクトを作成。

$ laravel new SampleProject

2.ディレクトリを追加する DBへのアクセスを担うRepositoriesと、ビジネスロジックを纏めるServicesを置くディレクトリを作成する。基本的にどこに配置してもいいが、よく見るのはappの配下に置くパターン。

├── app
│   ├── Console
│   ├── Exceptions
│   ├── Http
│   ├── Services // 追加
│   ├── Repositories // 追加
│   └── ...
├── bootstrap
├── config
├── ...
...

3.マイグレーションファイルの作成 今回は既存のUserテーブルに加えCompanyテーブルを用意する。下記のコマンドでモデルとマイグレーションファイルがセットで出来上がる。その後はphp artisan migrateでテーブルの作成を行う。

php artisan make:model CompaniesTable --migration
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCompaniesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('companies', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name', 100)->default("");
            $table->string('address', 200)->default("");
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('companies');
    }
}

4.コントローラーの作成 最近はRestApiでの開発がメインになりつつあるので下記でコントローラーを作成する。

php artisan make:controller CompanyController --resource

5.各ファイルの手配 2.で用意したSercices、Repositoriesディレクトリに下記のファイルを用意する。

・CompanyService.php // Servicesディレクトリ内に配置 ・CompanyRepository.php // Repositoriesディレクトリ内に配置 ・CompanyRepositoryInterface.php // Repositoriesディレクトリ内に配置

6.ロジックを組み立てる 下地が整ったので、あとはロジックを組み立てていく。まずはコントローラーにメソッドを記述する。ここでは同時にCompanyServiceを注入している。Companyが不動産関係なら所有物件の有無を確認し...といったビジネスロジックはCompanySrerviceに記述することになるので、コントローラーではCompanyServiceで「何をするか」が分かるように記述する。実際のロジックはCompanyServiceの中で記述すること。

<?php

namespace App\Http\Controllers;

use App\Http\Requests\CompaniesRequest;
use App\Services\CompanyService;

class CompanyController extends Controller
{

    /** @var companyService */
    protected $companyService;

    /**
     * @param CompanyService $companyService
     *
     */
    public function __construct(CompanyService $companyService)
    {
        $this->companyService = $companyService;
    }


    /**
     * Store a newly created resource in storage.
     *
     * @param  CompaniesRequest  $request
     * @return array
     */
    public function store(CompaniesRequest $request)
    {
        $company = $this->companyService->createCompany($request->only(["name", "address"]));

        return response()->json($company, 200);
    }

}

次にCompanyService.phpを作成していく。上で述べたように、ここにコントローラーで使うであろうメソッドを定義していく。上の流れに沿って不動産関係の会社でゴニョゴニョしたいならisEstateCompany()isOwner()など必要なメソッドをコントローラーを意識して用意する。

<?php


namespace App\Services;


use App\Repositories\CompanyRepositoryInterface;

class CompanyService
{
    protected $companyRepository;

    public function __construct(CompanyRepositoryInterface $companyRepository)
    {
        $this->companyRepository = $companyRepository;
    }

    public function createCompany($request)
    {
        return $this->companyRepository->createCompany($request);
    }

}

次にInterfaceを用意する。Interfaceはメソッドを保証する目的で作成する。例えばcreateCompanyはDBに会社を新規登録するメソッドである為、このInterfaceを実装するのはCompanyRepositoryになるが、CompanyRepositoryには必ずcreateCompany()が定義されていなければならない。なぜこのような面倒なことをするか、と言うのはこの後説明する。

<?php


namespace App\Repositories;


interface CompanyRepositoryInterface
{
    public function createCompany($request);

}

ここでAppServiceProvider.phpにこのInterfaceとRepositoryの紐付けを行う。Interfaceを使用する意味はここにある。ここではCompanyRepositoryInterfaceとCompanyRepositoryを紐付けているが、このRepositoryは差し替えが可能である。例えば.envがAPP_ENV=localならばCompanyRepositoryでAPP_ENV=productionならばApiCompanyRepositoryを使用するといったDBの切り替えをここで定義することができる。この他にも、例えば会社登録などほぼ固定化され変わることのないメソッドは必ず用意してくれと言う意味でもInterfaceの存在意味は出てくる。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->app->bind(\App\Repositories\CompanyRepositoryInterface::class, \App\Repositories\CompanyRepository::class);
    }
}

最後にCompanyRepositoryのコーディングを行う。ここではModelのCompany.phpを注入する。Modelにはリレーションやfillableなど基本的な共通事項を定義しておけばRepositoryを複数用意する場合にも便利だろう。

<?php


namespace App\Repositories;


use App\Company;

class CompanyRepository implements CompanyRepositoryInterface
{
    protected $company;

    public function __construct(Company $company)
    {
        $this->company = $company;
    }

    public function createCompany($request)
    {
        return $this->company->create($request);
    }
}

7.まとめ DBへのアクセスロジックはRepositoryへ、ビジネスロジックはServiceへ、コントローラーへは派生先で何が行われるかが見ただけで分かるようにするのが理想である。

Homesteadのphp.iniを編集する

Homesteadのphp.iniを編集する。 ディストリビューションUbuntuなので下記手順で更新を行う。

vagrant@homestead:~$ cat /etc/issue
Ubuntu 16.04.3 LTS \n \l

1. php.iniの場所を確認する

// 1. phpのバージョンを確認
vagrant@homestead:~$ php -v
PHP 7.2.2-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Feb  1 2018 16:01:26) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.2-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
    with blackfire v1.18.2~linux-x64-non_zts72, https://blackfire.io, by SensioLabs

// 2. etc配下にphpフォルダがあるので使用しているバージョンのフォルダに入る。
vagrant@homestead:~$ cd /etc/php
vagrant@homestead:/etc/php$ ls -l
total 16
drwxr-xr-x 5 root root 4096 Feb  5  2018 5.6
drwxr-xr-x 5 root root 4096 Feb  5  2018 7.0
drwxr-xr-x 5 root root 4096 Feb  5  2018 7.1
drwxr-xr-x 5 root root 4096 Feb  5  2018 7.2

// 3. このバージョンフォルダの中のfpmというフォルダ内にphp.iniがある。
vagrant@homestead:~$ cd /etc/php/7.2/fpm

2. php.iniのRW権限付与

$ sudo chmod -R ugo+rw php.ini

3. php.ini編集

$ sudo vi php.ini

Laradock環境構築

Laradockとは

Laravelのアプリを開発する上で必要なComposer、MySQL、Nginxなど諸々のDockerコンテナを作成する為のイメージが入ったパッケージ。これらのコンテナを組み合わせアプリ開発をしていく。VagrantでいうHomesteadのような存在。

Dockerイメージとは

コンテナを作成するためのファイルシステム。イメージは公式な「リポジトリ」で配布されているもの(ベースイメージ)を取得してきてもよいし、自作することもできる。例えばRubyMySQLのパッケージを追加してひとつのイメージにしておけば、これを様々な環境で同一に使いまわすことができる。何と何を組み合わせるかはDockerfileに定義しておく。 今回はLaradockが提供しているイメージを使用することになるが、LaradockはLaravelやComposer等プログラム系を纏めたworkspace、WebサーバーであるNginx、DBのMySQLという単位でイメージを切り分けてある。(他にもRedis、phpMyAdminといった単位でたくさん用意してある) とりあえずのアプリを作成するのならworkspace、nginx、mariadbのイメージからコンテナを作成すればいい。

環境構築

準備

今回のフォルダ構成は下記の通り。 laradockと同階層にプロジェクトフォルダを置く。 projectフォルダ配下にはapp,bootstrap,config...とLaravelのフォルダが続く。
ちなみに既にプロジェクトフォルダがある場合、後ほど記載するlaradockの.envのマウントフォルダのパス設定を変えれば良い。
使うDBをプロジェクトによって切り替えたい場合は、laravelの.envを変えること。

Documents/
  ├ laradock/
  ├ project/

手順1.LaradockをCloneする

git clone https://github.com/LaraDock/laradock.git

手順2.laradock用の.envを用意する

cd laradock
cp env-example .env

これはLaravelの.envではなくLaradockの.envである。 この.envに、コンテナとマウントするプロジェクトフォルダ等を定義していく。

「準備」記載のフォルダ構成を前提にlaradockの.envにマウントフォルダを下記のように定義する。

APP_CODE_PATH_HOST=../project

手順3.コンテナを作成する

docker-compose up -d nginx mariadb phpmyadmin redis workspace

最初はなかなか時間が掛かる。次からは早い。

手順4.コンテナの起動を確認する

docker-compose ps

stateがUpになっていればコンテナが起動している。


※ここでmariadbが動いていない問題が発生!!とりあえず下記の対応で何とかなった。

volumes:
- - ${DATA_PATH_HOST}/mariadb:/var/lib/mysql
+ #- ${DATA_PATH_HOST}/mariadb:/var/lib/mysql 

手順5.Laravelプロジェクトの作成

workspaceというコンテナに入りcomposerでプロジェクトを作成する。 まずは下記のコマンドでworkspaceコンテナに入る。

docker exec -it laradock_workspace_1 /bin/bash

ここでls -lすると、どのフォルダ階層かが分かる。「準備」記載の階層にプロジェクトフォルダを作るとして、Composerでプロジェクトを作成していく。

composer create-project laravel/laravel docker_app

完了

workspaceでphp artisan serveで稼働を確認できる。

その他

Laravelの.envのDB初期値設定は下記である。

DB_CONNECTION=mysql
DB_HOST=mariadb
DB_PORT=3306
DB_DATABASE=default
DB_USERNAME=default
DB_PASSWORD=secret