FuelPHPを使ってみる

公式ドキュメント：
http://fuelphp.jp/

# PHPバージョン
1.8.0ではPHP7.2までサポート、1.8.2ではPHP7.3まで

https://bmf-tech.com/posts/FuelPHP1.8.0%E2%86%921.8.2%E3%80%81PHP5.6%E2%86%92PHP7.3%E3%81%B8%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%83%E3%83%97%E3%81%97%E3%81%9F

公式：https://fuelphp.com/blogs/2019/06/fuel-releases-1-8-2
1.8.2のchangelog：https://github.com/fuel/core/wiki/Changelog-v1.8.2

# docker環境の構築

下記を利用させていただきました。
https://github.com/nemui-fujiu/docker_fuelphp

## 修正箇所
PHPのDockerfile内

### PHPバージョン

現状（2020/03/06）、「php:7-fpm」だとPHP7.4がインストールされてしまうので「php:7.3-fpm」に修正する
FuelPHP1.8.2においても、PHP7.3の対応までの為。

### mysql-client

debianでは「mysql-client」がなくなったのでエラー→default-mysql-clientに変更
https://yourmystar-engineer.hatenablog.jp/entry/2019/07/15/140644

### mcrypt

mcryptでもエラーが起きたので削除
※PHP7.3からはmcryptでなくてOpenSSLで実装する事が推奨されているようです。

error: /usr/src/php/ext/mcrypt does not exist
https://blog.websandbag.com/entry/2018/12/15/024508

# プロジェクトの作成

docker内で、composerのインストール、FuelPHPのクイックインストーラのインストールまで進んでいる為
下記実行し、FuelPHPのプロジェクトを作成する

※先に「app」コンテナ内に入ります。

```
# docker-compose exec app bash
```

```
# oil create fuel_server
```
※生成するプロジェクト名を変更したい場合は、nginxの設定ファイルも修正する必要があります。

ログディレクトリに権限がなくエラーになる為、下記ディレクトリに対して「その他のユーザ」に対して書き込み・実行権限をつける
※さぼって777にしてますが、権限は絞った方がいいです。

https://qiita.com/greymd/items/a0d0fff798b8daa1da88

```
# chmod 777 /var/www/fuel_server/fuel/app/logs/
```

上記まで終わったら、下記にアクセスして正常に表示されればOKです。  
http://localhost:9080/


mysqlが何故か動かず試行錯誤したときに、下記で全削除してから再度docker-compose buildを行いました。(キャッシュを使わない）

```
# docker-compose down --rmi all --volumes
# docker-compose build --no-cache
```
https://qiita.com/suin/items/19d65e191b96a0079417

# コーディングガイドライン

コーディングガイドラインがあるので、これに従ってコーディングを行います。
http://fuelphp.jp/docs/1.8/general/coding_standards.html

# FuelPHPの特徴

- 移植性が高く、多くのサーバで動く
- 規約より設定を重視して設計されている
    - フレームワークのルールに縛られない
    - 複雑な設定ファイルに悩まされない
- MVCフレームワークだが、ViewModel（Presenter）という表示用のデータを作成して渡す機能がある→コントローラの肥大化を防ぐ役割か（いわゆるファットコントローラを防ぐ）
- クラス名の命名について：アンダースコアはオートロードの際にディレクトリの区切り記号に変換する（ここ結構特殊かな...）
- ビュー
    - テンプレートエンジンを使っているわけではないので、親・子とビューファイルだけで継承させるのは素のFuelPHPでは出来ない
    - https://qiita.com/assa/items/4fef2f3abd95248ed626
- 参考
    - https://www.buildinsider.net/web/bookphplib100/096
    - http://fuelphp.jp/docs/1.7/general/configuration.html
    - https://www.slideshare.net/OhasiYuki/ss-52193845

## MVCP

- モデル
    - データを取得・操作する
    - SQLクエリを記述
- ビュー
    - ブラウザにデータを提供するファイル
    - ビューはhtml、javascriptあるいはcss(コントローラから渡される変数を含む）
    - 処理を含めない（かんたんな条件分岐等のみ）
- コントローラ
    - URL を通じてアクセス可能なクラス(リクエストを処理)
    - ントローラはモデルや他のクラスを呼び出して情報を取得→出力用にビューにすべてを渡す
- プレゼンター
    - ビューの生成に必要なロジックを含む
    - コントローラがユーザ入力を処理し、必要なアクションを処理すると、 プレゼンタにビューに必要なデータを取得するように処理を引き継ぐ
    - プレゼンタはデータの操作を一切すべきではない
    - データベースの呼び出しや他のデータの取得、 ビューの生成に必要な準備の操作を含む

# 設定

## DB

- 本番環境：fuel/app/config/production/db.php
- テスト環境：fuel/app/config/development/db.php
- 本番・テスト共通の設定：config/db.php
- デフォルト環境は development(テスト環境) になっている
- 環境変数（$_SERVER[‘FUEL_ENV’]）や bootstrap.php から環境を変更できる

http://fuelphp.jp/docs/1.8/classes/database/introduction.html
https://runble1.com/fuelphp-db-connection/

## マイグレーション



# ルーティング

- ルート設定は、fuel/app/config/routes.php
- http://fuelphp.jp/docs/1.8/general/routing.html
- 「article/:id」の場合、「:id」は数値や文字列等何でも取得できてしまう→数値だけ取得する場合は、「article/(?P\<id\>\d+)」と指定できる。
- ルーティングでHTTPメソッドを指定することは出来ず、コントローラ側でHTTPメソッドアクションを指定する
- PUT/DELETEの場合、どのように指定すればいい？（わからない）


# コントローラ

- fuel/app/classes/controller ディレクトリに置く
- ルーティングとコントローラの紐付け
    - '_root_' => 'article/index' -> パス指定ない場合、Controller_Articleクラスのaction_indexメソッドを実行
    - 'article/create' => 'article/create' -> article/createへのアクセスの場合、Controller_Articleクラスのaction_createメソッドを実行
- メソッド名
    - URLを通じてリクエストされたメソッドは、"action_" というプレフィックスを付ける（このメソッドは他のクラスからも使用できますが、ルーティングできない）
    - HTTP メソッド名をアクション名の前につけることもできる（get_index,post_index等）
- before/afterメソッド
    - コントローラで指定したメソッドの前後で共通の処理を実装できる
    - https://wingdoor.co.jp/blog/fuelphp%E3%81%AE%E3%83%97%E3%83%AC%E3%82%BC%E3%83%B3%E3%82%BF%E3%81%8C%E6%A5%BD%E3%81%97%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%8A%E8%A9%B1-2/

# ビュー

## View::forge

ブラウザに出力するビューを割り当てる
指定した変数（$data）は、app/config/config.phpに設定されているoutput_filterのフィルタを通る
(output_filterはデフォルトで「Security::htmlentities」が指定されているので、必ずhtmlentities()が変数に対して実行される）


```php
View::forge('home/index', $data);
```

## 親子ビューを作成する

レイアウトビューファイルを作成する

```html
<html>
    <head>
        <?php echo $head; ?>
    </head>
    <body>
        <?php echo $header; ?>
        <?php echo $content; ?>
        <?php echo $footer; ?>
    </body>
</html>
```

変数としてheadやheader,content等を指定する（コントローラで設定する必要がある）
コントローラとビューが密に癒着する・ビュー全体の見通しがしづらそうな感じがする。

- XSSは塞げるが、ビューが生PHPになる為処理が見づらくなる（テンプレートエンジンでも、条件分岐は見づらくなりがちではありますが）
- ビューとコントローラの分離が完全に出来ない為、フロントエンドとサーバサイドでの役割分担がしづらそう

https://blog.leko.jp/post/write-interface-of-modern-template-engine-in-php/

```php

class Controller_Home extends Controller
{
    public function action_index()
    {
        // レイアウトビューを作成する
        $view = View::forge('layout');

        // グローバル変数(すべてのビューがアクセスできる)を割り当てる
        $view->set_global('username', 'Joe14');
        $view->set_global('title', 'Home');
        $view->set_global('site_title', 'My Website');

        //変数としてビューを割り当てる、遅延レンダリング
        $view->head = View::forge('head');
        $view->header = View::forge('header');
        $view->content = View::forge('content');
        $view->footer = View::forge('footer');

        // ビューオブジェクトをリクエストに返す
        return $view;
    }
}
```