フレームワークと管理ツールを自作してプロトタイプ開発してみた

本投稿は [TECOTEC Advent Calendar 2019](https://qiita.com/advent-calendar/2019/tecotec) の17日目の記事です。

RDBとフレームワークを連携して、プロトタイプ作成するWebツール「project-manager」を個人的に作成しているので紹介します。

* CRUD処理やら Viewとか毎回手で書きたくない
* DBを設計しながらプロトタイプレベルである程度自動化したい（scaffold、artisan などを拡張）
* ファイル設計をある程度可視化＆管理したい

最近では、Scratchプログラミングなど「コードを書かないプログラミング」もちらほら世に出ていたり、会社によっては既に自社システムを運用していたりしているかと思います。

詳細なロジック段階に入ると、やはり人の記述が必須となりますが、出来るだけプログラムを書かなくても良い部分もあるとは思い作成しています。

## 注意
* 自作フレームワークを勧める記事ではないので、セットアップや使い方は割愛
* 個人開発中で未搭載処理や、一切情報公開していないため動作保証しません
* なので未実装＆バグなどはご了承ください

## とりあえずCRUD処理画面を作成してみる
「project-manager」でブラウザをぽちぽちして、プロトタイプ（プログラムテンプレート）を作成してみます。

1. プロジェクト設定
2. DB作成
3. Model＆テーブル定義作成
4. Controller/View作成
5. フレームワークにファイルエクスポート（今回はPHP）

### 結果
登録・一覧・編集画面が出来ました。（コーディングは一切していません）
<img width="615" alt="pm_edit.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/443e4a05-7697-5db6-7565-28f4c4f550aa.png">
（「せい」「めい」の入力間違えてました・・・）
<img width="948" alt="school_student_list.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/1cd5e387-8055-6ce1-bd1a-25b0175d6d7b.png">
<img width="652" alt="pm_edit2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/6f4e13ed-3e39-a8ef-9ce5-45142715a4e8.png">

CRUD処理におけるリクエスト、セッション、バリデーション、リダイレクトなどなど、とりあえずやってくれます。
（これはどちらかというと「php-work」の方ですが）
項目の追加・修正・削除が発生した場合は、MVC設定を修正して上書きエクスポートを繰り返して作成します。

書き出した Controller ファイルの中身はこんなの感じです。

<img width="254" alt="vscode_controllers.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/276bf271-25eb-25ea-21c8-e3ce0c746885.png">


```php
...
class StudentController extends AppController {

...
    function action_list() {
        $this->student = DB::model('Student')->all();
    }
...

    function action_edit() {
        $this->student = DB::model('Student')->editPage();
    }
...

    function action_update() {
        $this->redirectForUpdate($this->updateByModel('Student'));
    }

...
    function action_delete() {
        $this->redirectForDelete($this->deleteByModel('Student'));
    }

...
    function action_update_sort() {
        $this->updateSort('Student');
    }
...
}
```

### 命名規則の重要性
本題ではないですが、命名規則について
上記はかなり横着過ぎる記述ですが、命名規則やルールが決まっていると、Contoller をある程度汚さずに書くことができます。

* プログラム開発をする前に「命名規則」は一番重要な項目
* 命名規則に則り、DB連携やプログラム自動・効率化する事が可能

[参考]
https://qiita.com/softark/items/63e68a0172a1d2f92b5c
https://qiita.com/genzouw/items/35022fa96c120e67c637
https://qiita.com/Ted-HM/items/7dde25dcffae4cdc7923

# 「project-manager」の概要
<img width="727" alt="pm_flow.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/365a7dd0-7f2b-a25b-9162-1aa654138594.png">
### project-manager
* 管理ツール
* Github：https://github.com/yoo16/project-manager
* PHP7以降
* PostgreSQL9以降
* mod_rewirte 起動

### php-work
* Github：https://github.com/yoo16/php-work
* PHPフレームワーク
* PHP7以降
* PostgreSQL9以降
* mod_rewirte 起動
* MVC、ORM、ActiveRecord設計
* Railsベースでもともと先人はいたのですが、一から作り直しました


## スキーマ作成
* PostgreSQL（以降DB）に対して「createdb」コマンド実行[^1]
<img width="1012" alt="pm_db_list.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/7760f093-9e3d-1526-e15c-621ba38153ad.png">

## プロジェクト & 作業スペース設定
* プロジェクト作成でメインDBを登録[^2]
* フレームワークにファイルエクスポートできるよう設定[^3]
* Webサーバユーザのパーミッション設定が必要

<img width="1165" alt="pm_index.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/58119fdb-5962-4a1b-fb8d-e2baa9c4ab9d.png">
<img width="932" alt="pm_user_project_setting.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/65e11478-c9b4-9e52-edf5-3969691d8fcd.png">

## Model作成・管理（SQL連動）
Model作成すると同時にCREATE TABLE します。[^4]

<img width="1003" alt="pm_model_list.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/58b9b27a-145b-578c-739e-3f10dccacc0e.png">

Modelカラムの修正をすると「ADD COLUMN」「DROP COLUMN」「RENAME」「COMMENT」etc... のSQLを発行しながら管理します。
<img width="1058" alt="pm_intro.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/2fd0a129-675a-2e07-3c6e-d8e2cd443ed4.png">
その他、リレーションによる外部キー、制約 etc...

## DBとModelの比較同期
「project-manager」は単に名称だけでDB管理するだけでなく、「pg_class」や「pg_attribute」などにアクセスしてDB情報を取得します。

* DB → Model 差分更新
* Model → DB 差分更新

といった処理も行えます。

ただし、「pg_class_id」「attnum」などはデータが再構築された場合、仕様上復旧できません

### 内部の pg_class アクセスの一例

```sql

SELECT origin.*,
       pg_class.oid as foreign_class_id,
       pg_class.relname as foreign_relname,
       pg_attribute.attnum as foreign_attnum,
       pg_attribute.attname as foreign_attname
       FROM 
       (
       SELECT 
            pg_constraint.conrelid
            , pg_constraint.conname
            , pg_class.oid as pg_class_id
            , pg_class.relname
            , pg_attribute.attnum
            , pg_attribute.attname
            , pg_constraint.confrelid
            , pg_constraint.confkey
            , pg_constraint.contype
            , pg_constraint.confupdtype
            , pg_constraint.confdeltype
        FROM  pg_constraint
        LEFT JOIN pg_attribute ON pg_constraint.conrelid = pg_attribute.attrelid 
        AND pg_attribute.attnum = ANY(pg_constraint.conkey)
        LEFT JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
                    WHERE pg_constraint.contype = 'f' AND pg_constraint.conrelid = '16848'
                ) AS origin
                LEFT JOIN pg_attribute ON origin.confrelid = pg_attribute.attrelid 
                AND pg_attribute.attnum = ANY(origin.confkey)
                LEFT JOIN pg_class ON origin.confrelid = pg_class.oid;
```

## PHPファイル書き出し
Modelを設定したら「PHP Export > PHP Model」で、Model定義ファイルをエクスポートします。
<img width="801" alt="pm_export_window.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/83416b15-5440-001a-7774-3c1bb3210e4b.png">
Model以外にもエクスポートでき、「All Files」するとMVCの基本プログラムを全て書き出します。
ちなみに、Model定義ファイルだけは必ず上書きします。

### Model定義ファイル書き出しの例
「php-work」の場合「app/models/vo/」に書き出す
<img width="254" alt="vscode_vo.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/f9d6a81e-0919-2026-532c-7be6a3397b1a.png">
モデル定義ファイルの内容 ※このファイルは人間は書きません

```php
...
class _Report extends PwPgsql {

    public $id_column = 'id';
    public $name = 'reports';
    public $entity_name = 'report';

    public $columns = [
        'created_at' => ['type' => 'timestamp'],
        'report' => ['type' => 'text'],
        'reported_at' => ['type' => 'timestamp', 'is_required' => true],
        'sort_order' => ['type' => 'int4'],
        'student_id' => ['type' => 'int4', 'is_required' => true],
        'updated_at' => ['type' => 'timestamp'],
    ];

    public $primary_key = 'reports_pkey';
    public $foreign = [
            'reports_student_id_fkey' => [
                                  'column' => 'student_id',
                                  'class_name' => 'Student',
                                  'foreign_table' => 'students',
                                  'foreign_column' => 'id',
                                  'cascade_update_type' => 'a',
                                  'cascade_delete_type' => 'c',
                                  ],
    ];

    public $index_keys = [
    'reports_pkey' => 'CREATE UNIQUE INDEX reports_pkey ON public.reports USING btree (id)',
    ];

....

}
```

## Controller/View作成
ここからはまだ、未実装な機能がたくさんありますが・・・
「Quick Create」を利用すると、Modelから、Controller、Viewを自動作成します。
### Controller作成
* Model を起点に Controller情報を作成
* 継承ファイル（親）指定可能
<img width="1047" alt="pm_page.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/7c6f5561-65f8-f454-0026-ba30a3489d80.png">

### View(Action)作成
「Quick Create」を利用すると、デフォルトで「edit」「list」を作成します。
これらは、Controller では Action であり View ではテンプレートにあたります。
<img width="882" alt="pm_view_list.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/c875eadf-0488-f52e-1548-4cc983e7021d.png">

<img width="590" alt="pm_view_item_list.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/9eec905f-8238-c5ed-00d6-f492108aac18.png">


### 書き出したViewファイルの例
action: edit が存在すると、「new.phtml」「edit.phtml」「form.phtml」テンプレートファイルがエクスポートされます。
![pm_view_file.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/d18654b6-f059-22cc-711a-8944f5c6b3a2.png)

「list.phtml」の例

```html
...
  <? if ($this->student->values): ?>
  <? foreach ($this->student->values as $values): ?>
  <tr row-id="<?= $values['id'] ?>">
    <td><?= $this->student->linkEdit([], $values) ?></td>
    <td><?= $values['birthday_at'] ?></td>
    <td><?= $values['last_name'] ?></td>
    <td><?= $values['last_name_kana'] ?></td>
    <td><?= $values['first_name'] ?></td>
    <td><?= $values['first_nam_kana'] ?></td>
  </tr>
  <? endforeach ?>
  <? endif ?>
```
「form.phtml」の例

```html
...
    <tr>
      <th><?= LABEL_STUDENTS_CODE ?></th>
      <td>
        <?= $this->student->formInput('code', ['class' => 'form-control']) ?>
      </td>
    </tr>
    <tr>
      <th><?= LABEL_STUDENTS_FIRST_NAME ?></th>
      <td>
        <?= $this->student->formInput('first_name', ['class' => 'form-control']) ?>
      </td>
    </tr>
```

このように、Model, View, Controller をフレームワークに対してプログラムソースを書き出します。
（現時点では「php-work」専用）

##その他もろもろ
### CSV管理
* 「db/recoreds/」内に、CSVファイルをエクスポート
* アプリの設定項目など静的マスターなどで利用
* ローカライズ対応

### SQLファイルエクスポート
Model定義ファイルから、DB全定義のSQLをエクスポートできます。
（フレームワーク内でスクリプト実行することも可能）
<img width="690" alt="pm_export_sql.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/d5d029cc-0887-9478-c285-89e30f6b8a6c.png">
ちなみに、Migrateファイルは現在対応してません（ぽちぽちする度にバージョン管理は大変なので・・・）

* 「PHP Export > SQL」で書き出し
* 「php-work」の scripts/sql/create_sql_from_model.php を実行
* エクスポート先は「php-work」の「db/sql/DB名.sql」


### 簡易DB定義書Excel書き出し
PostgreSQLのDBから、簡単なDB定義書をExcelでエクスポートできます。
（composer phpoffice/phpexcel が必要）
<img width="719" alt="pm_php_excel.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/66e56ad0-eb95-e265-7f5f-64dbf1960efa.png">

### DB簡易閲覧
ざっとDBの定義を見ることもできますが、ER図作成機能は現状ありません。
![pm_db_detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528848/005e71e2-1310-47a0-89b6-82629a226f77.png)

とりあえず、ざっと紹介してみましたが、
## 自作してみた感想
* 他のフレームワークの中身が想像できたり、今まで知らなかった発見がある
* PostgreSQLの内部をちょっと垣間見た
* 実際に案件で長年回してたので、それなりには使える（自称）
* レイアウト作成やJSフレームワーク未対応だったり、テンプレートが柔軟性にかける
* プログラム自動化を解放するタイミングが難しい（Modelは永続的に使える）
* もっと効率を上げるには、設計・アルゴリズムの勉強が必要
* 自作フレームワークは諸刃の剣なので、基本汎用フレームワークを使いましょう

#### プログラムメソッド解析
プログラム内のメソッドやパラメータの視覚化機能を旧バージョンで作ってましたが、現在は作成中・・・

## 今後は・・・
今回は、自作フレームワークの管理でしたが、将来的に「Laravel」「iOS/Android」「Python」対応も考えてはみたい（気持ちだけ・・・）

そのうち優秀な人が何かしら作って利用している可能性の方が高そうです。
ゆくゆくは全自動プログラム！・・・いや多分当分無理だと思います。

[^1]: Webサーバユーザの権限設定や、pg_hba.confなどの設定が必要
[^2]: ModelでDBスキーマが異なる場合は、Model毎に設定
[^3]: php-work を、git clone 処理機能あり
[^4]: Railsの設計を基本に「id」「created_at」「updated_at」「sort_order」を自動追加（必須項目）