【備忘録】初めてのCakePHP① - 環境構築編

## 環境
* MacOS Catalina10.15.5
* Docker for Mac（Version 3.3.1）
* PHP7.3
* Laravel7.30
* vue 2.6.12


## Dockerでローカルの環境構築

### プロジェクト用ディレクトリを用意

```terminal:Terminal
$ mkdir -p ~/workspace/cakeblog
$ cd ~/workspace/cakeblog
$ mkdir -p cakeapp mnt/mysql /docker/{php,nginx}
$ mkdir docker/nginx/{conf.d,logs}
```
### .envの準備

#### 先に.env.exampleを編集してからコピーして.envをつくる

```terminal:cakeblog
$ touch .env.example .gitignore
```

#### .env.exampleと.gitignoreを編集

```.env:.env.example
PROJECT_PATH=src
TZ=Asia/Tokyo
WEB_PORT=10080
DB_PORT=13306
DB_TESTING_PORT=13307
DB_CONNECTION=mysql
DB_NAME=app_name
DB_USER=willrewrite
DB_PASS=willrewrite
DB_ROOT_PASS=willrewrite
MAILHOG_PORT=18025
MAIL_HOST=mail
MAIL_PORT=1025

COMPOSE_HTTP_TIMEOUT=70
```

#### .env.exampleをコピー

```terminal:cakeblog
$ cp .env.example .env 
```

コピー後、個別のプロジェクトに合わせて.envの環境変数を上書きする

```env:.env
PROJECT_PATH=cakeapp
TZ=Asia/Tokyo
WEB_PORT=10080
DB_PORT=13306
DB_TESTING_PORT=13307
DB_CONNECTION=mysql
DB_NAME=cake_blog
DB_USER=cakeuser
DB_PASS=cakepass
DB_ROOT_PASS=foobar
MAILHOG_PORT=18025
MAIL_HOST=mail
MAIL_PORT=1025

COMPOSE_HTTP_TIMEOUT=70
```

```:.gitignore
.env
.idea
# その他個別に追加
```

### 必要なファイルを作成

```terminal:cakeblog
$ touch docker-compose.yml \
docker/php/{php.ini,Dockerfile} \
docker/nginx/{conf.d/default.conf,Dockerfile} \
mnt/mysql/.gitignore
```

### 各ファイルを編集

```yml:docker-compose.yml

version: "3"

services:
  web:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
      args:
        - TZ=${TZ}
    volumes:
      - ./docker/nginx/logs:/etc/nginx/logs
      - ./docker/nginx/conf.d:/etc/nginx/conf.d
      - ./${PROJECT_PATH}:/var/www/${PROJECT_PATH}
    ports:
      - ${WEB_PORT}:80
    links:
      - app
    depends_on:
      - app
  
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
      args:
        - TZ=${TZ}
    volumes:
      - ./${PROJECT_PATH}:/var/www/${PROJECT_PATH}
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
    links:
      - db_master
    environment:
      - DB_CONNECTION=${DB_CONNECTION}
      - DB_HOST=db_master
      - DB_DATABASE=${DB_NAME}
      - DB_USERNAME=${DB_USER}
      - DB_PASSWORD=${DB_PASS}
      - TZ=${TZ}
      - MAIL_HOST=${MAIL_HOST}
      - MAIL_PORT=${MAIL_PORT}

  db_master:
    image: mysql:5.7
    volumes:
      - ./mnt/mysql:/var/lib/mysql
      - ./docker/mysql/mysql_conf:/etc/mysql/conf.d
    environment:
      - MYSQL_DATABASE=${DB_NAME}
      - MYSQL_USER=${DB_USER}
      - MYSQL_PASSWORD=${DB_PASS}
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASS}
      - TZ=${TZ}
    ports:
      - ${DB_PORT}:3306
```

#### Nginxの設定

```Dockerfile:docker/nginx/Dockerfile
FROM nginx:1.15.7-alpine
# timezone
ARG TZ
COPY ./docker/nginx/conf.d/ /etc/nginx/conf.d/
COPY ./docker/nginx/logs/ /etc/nginx/logs/
COPY ./${PROJECT_PATH}/ /var/www/${PROJECT_PATH}/
RUN apk update && apk --update add tzdata && \
  cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
  apk del tzdata && \
  rm -rf /var/cache/apk/*
```

```conf:docker/nginx/conf.d/default.conf
server {
    listen       0.0.0.0:80;
    # server_nameで設定した名前をローカルマシンの/etc/hostsにも設定する
    server_name  www.cake-blog.org cake-blog.org;
    charset      utf-8;
    client_max_body_size 5M;

    root /var/www/cakeapp/webroot;

    index index.php;

    location / {
        access_log  /etc/nginx/logs/access.log main;
        error_log   /etc/nginx/logs/error.log;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_pass  app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}
```

#### PHPの設定
```Dockerfile:docker/php/Dockerfile

FROM php:7.3.0RC6-fpm-alpine3.8
# timezone
ARG TZ
WORKDIR /var/www/cakeapp
COPY ./docker/php/php.ini /usr/local/etc/php/
COPY ./${PROJECT_PATH}/ /var/www/${PROJECT_PATH}/

RUN apk upgrade --update && apk add --no-cache \
  mysql-client coreutils freetype-dev jpeg-dev libjpeg-turbo-dev libpng-dev libmcrypt-dev \
  git vim unzip tzdata \
  libltdl icu-dev autoconf make && \
  docker-php-ext-install pdo_mysql mysqli mbstring && \
  docker-php-ext-install -j$(nproc) iconv && \
  docker-php-ext-configure gd \
  --with-freetype-dir=/usr/include/ \
  --with-jpeg-dir=/usr/include/ && \
  docker-php-ext-install -j$(nproc) gd intl && \
  cp /usr/share/zoneinfo/${TZ} /etc/localtime && \
  apk del tzdata && \
  rm -rf /var/cache/apk/*

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
```

```ini:docker/php/php.ini
log_errors = on
error_log = /var/log/php/php-error.log
upload_max_filesize = 5M
memory_limit = -1
post_max_size = 100M
max_execution_time = 900
max_input_vars = 100000
default_charset = UTF-8

[Date]
date.timezone = ${TZ}
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"
```

#### mysqlのマウントによるファイルはコード管理の対象外
```.gitignore:mnt/mysql/.gitignore
/*
!/.gitignore
```

#### 立ち上げ
```terminal:cakeblog
$ docker compose run app composer create-project --prefer-dist cakephp/app:^3.8 .

> App\Console\Installer::postInstall
Created `config/app_local.php` file
Created `/var/www/cakeapp/tmp/cache/views` directory
Set Folder Permissions ? (Default to Y) [Y,n]? Y
Permissions set on /var/www/cakeapp/tmp/cache
Permissions set on /var/www/cakeapp/tmp/cache/models
Permissions set on /var/www/cakeapp/tmp/cache/persistent
Permissions set on /var/www/cakeapp/tmp/cache/views
Permissions set on /var/www/cakeapp/tmp/sessions
Permissions set on /var/www/cakeapp/tmp/tests
Permissions set on /var/www/cakeapp/tmp
Permissions set on /var/www/cakeapp/logs
Updated Security.salt value in config/app_local.php


$ docker compose up -d
$ docker compose exec app ash
```

## 初期設定

composer updateをすると以下のような注意が出る

```
  Action required!                                                           
                                                                                
  The CakePHP plugin installer v1.3+ no longer requires the                  
  "post-autoload-dump" hook. Please update your app's composer.json          
  file and remove usage of                                                   
  Cake\Composer\Installer\PluginInstaller::postAutoloadDump   
```

以下のことをやると注意書きが出なくなる

```json.composer.json
"scripts": {
  "post-install-cmd": "App\\Console\\Installer::postInstall",
  "post-create-project-cmd": "App\\Console\\Installer::postInstall",
  // ↓　この１行を削除
  "post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump",
  "check": [
    "@test",
    "@cs-check"
  ],
```

### 環境変数の準備
```php:config/bootstrap.php

// ↓の記述(55行目あたり)のコメントアウトを外す
if (!env('APP_NAME') && file_exists(CONFIG . '.env')) {
    $dotenv = new \josegonzalez\Dotenv\Loader([CONFIG . '.env']);
    $dotenv->parse()
        ->putenv()
        ->toEnv()
        ->toServer();
}
```

```bash:/var/www/cakeapp
$ cp config/.env.example config/.env
```

```config/.env
# とりあえず変えたところを抜粋
export APP_DEFAULT_LOCALE="ja_JP"
export APP_DEFAULT_TIMEZONE="Asia/Tokyo"
# ↓ config/app_local.phpの'Security' => 'salt'の値を持ってくる
export SECURITY_SALT="120f60674fb6f07df09ce19ecd1013e7fee34ad96c52604d99676496bbaa2c24"
```

### DB接続関係の設定
```php:config/app_local.php

// 変えたところ抜粋
'Datasources' => [
  'default' => [
    'host' => env('DB_HOST', 'localhost'),
    /*
     * CakePHP will use the default DB port based on the driver selected
     * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
     * the following line and set the port accordingly
     */
    //'port' => 'non_standard_port_number',
    'username' => env('DB_USERNAME', 'my_app'),
    'password' => env('DB_PASSWORD', 'secret'),
    'database' => env('DB_DATABASE', 'my_app'),
    'log' => true,
    'url' => env('DATABASE_URL', null),
  ],
],
```

`localhost:10080`にアクセスして、一旦初期画面を確認する
![default.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553584/468e255f-f27b-e35b-dfcb-7c3c43cf3c72.png)


## マイグレーション

```bash:/var/www/cakeapp
$ bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified

$ bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified
```

```php:config/Migrations/20210424054900_CreateArticles.php
<?php

use Migrations\AbstractMigration;

class CreateArticles extends AbstractMigration
{
  /**
   * Change Method.
   *
   * More information on this method is available here:
   * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
   * @return void
   */
  public function change()
  {
    $table = $this->table('articles');
    $table->addColumn('title', 'string', [
      'default' => null,
      'limit' => 255,
      'null' => false,
    ]);
    $table->addColumn('body', 'text', [
      'default' => null,
      'null' => false,
    ]);
    $table->addColumn('category_id', 'integer', [
      'default' => null,
      'limit' => 11,
      'null' => false,
    ]);
    $table->addColumn('created', 'datetime', [
      'default' => null,
      'null' => false,
    ]);
    $table->addColumn('modified', 'datetime', [
      'default' => null,
      'null' => false,
    ]);
    $table->create();
  }
}
```

```php:config/Migrations/20210424055111_CreateCategories.php
<?php

use Migrations\AbstractMigration;

class CreateCategories extends AbstractMigration
{
  /**
   * Change Method.
   *
   * More information on this method is available here:
   * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
   * @return void
   */
  public function change()
  {
    $table = $this->table('categories');
    $table->addColumn('parent_id', 'integer', [
      'default' => null,
      'limit' => 11,
      'null' => false,
    ]);
    $table->addColumn('lft', 'integer', [
      'default' => null,
      'limit' => 10,
      'null' => false,
    ]);
    $table->addColumn('rght', 'integer', [
      'default' => null,
      'limit' => 10,
      'null' => false,
    ]);
    $table->addColumn('name', 'string', [
      'default' => null,
      'limit' => 100,
      'null' => false,
    ]);
    $table->addColumn('description', 'string', [
      'default' => null,
      'limit' => 255,
      'null' => false,
    ]);
    $table->addColumn('created', 'datetime', [
      'default' => null,
      'null' => false,
    ]);
    $table->addColumn('modified', 'datetime', [
      'default' => null,
      'null' => false,
    ]);
    $table->create();
  }
}
```


```bash:/var/www/cakeapp
$ bin/cake migrations migrate
```


## MAMPで環境構築する場合

### PHPパスの変更

普通にプロジェクトを作成するだけでは、以下のような`intlをインストールしてください`というエラーが出る

```
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - cakephp/cakephp[3.9.0, ..., 3.9.9] require ext-intl * -> it is missing from your system. Install or enable PHP's intl extension.
    - Root composer.json requires cakephp/cakephp 3.9.* -> satisfiable by cakephp/cakephp[3.9.0, ..., 3.9.9].
```

MacデフォルトのPHPからMAMPのPHPにパスを向け直すと解決する。

```bash:~/
$ which php
/usr/bin/php
```

これを修正する

```bash:~/
vi ~/.bash_profile
```

```~/.bash_profile
# PHPのバージョン"7.4.12"の部分は、MAMPダウンロード時点で異なる場合があるので要確認
export PATH=usr/local/bin:/Applications/MAMP/bin/php/php7.4.12/bin:$PATH
```

```bash:~/
$ source .bash_profile
$ which php
/Applications/MAMP/bin/php/php7.4.12/bin/php
```

上記のようにパスが変わっていればOK。

### Composerのインストール

```bash:~/
$ curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

$ composer -V
Composer version 2.0.12
```

### httpd.confの編集

```conf:/Application/MAMP/conf/apache/httpd.conf
NameVirtualHost *:8888
<VirtualHost *:8888>
    ServerName cake-blog.org
    DocumentRoot /Applications/MAMP/htdocs/cakeblog/webroot
    <Directory "/Applications/MAMP/htdocs/cakeblog/webroot">
        Options All
        AllowOverride All
        Order Allow,Deny
        Allow from all
    </Directory>
</VirtualHost>
```

上記のServerNameと同じホスト名を`/etc/hosts`にも設定しておく

### DB作成

```bash:~/
$ cd /Applications/MAMP/Library/bin
$ ./mysql -u root -proot

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.32 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

// データベースの作成
mysql> create database cake_blog default character set utf8mb4 collate utf8mb4_unicode_ci;
Query OK, 1 row affected (0.00 sec)

// adminユーザーに権限の付与
mysql> grant all on cake_blog.* to admin@localhost identified by 'admin';
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> quit
Bye
```

### プロジェクトの作成
```bash:/Application/MAMP/htdocs
$ composer create-project --prefer-dist cakephp/app:^3.8 cakeblog
```

### その他

環境変数の設定(おもにDB接続らへん)はDockerの時と同じ要領。
`http://cake-blog.org:8888`にアクセスし、初期画面が出ればOK。