「N-DEV」DB設計→SQL、API、クラスを自動生成

2023/12/21に公開

N-DEV

以下(Vercel)で公開中です。現在無料!!
N-DEV

前記事

自動生成機能

こんなに急ぐつもりはなかったですが、今ある案件に対応するために急いで自動生成機能を付けました。
まだ実際には使用していませんので、不具合等ある箇所があるかと思います。お気付きの点等ありましたらコメントでお知らせください。

SQL自動生成機能

名前そのままテーブルに対するCRUD(add、get、update、delete)のSQLを自動で作成します。
例えば以下のような「app」テーブルをDB設計で設計した場合、

次の4つのSQLがファイルとして自動生成されます。

getApp.sql
SELECT app.* 
FROM app 
WHERE 1 AND app.app_id=:appId
addApp.sql
INSERT INTO app
(app_name, app_description, app_code, app_rand, db_name, db_data) 
VALUES (:appName, :appDescription, :appCode, :appRand, :dbName, :dbData)
updateApp.sql
UPDATE app
SET app_name=:appName, app_description=:appDescription, app_code=:appCode, app_rand=:appRand, db_name=:dbName, db_data=:dbData 
WHERE 1 AND app.app_id=:appId_w
deleteApp.sql
DELETE app
FROM app
WHERE 1 AND app.app_id=:appId

値はプレイスホルダー的な記述をしています。「mysql2」などのライブラリを使用する場合に便利かと思います。
また、例えばADD時には「A_I(オートインクリメント)」や「CURRENT_TIMESTAMP」のカラムは無視されているのがわかると思います。
カラムの設計を参考に必要と思われるものだけを操作するように作成しています。

API自動生成機能

かなり独自実装です。
SQL自動生成を核としてその周辺も合わせて作成しています。
以下例

クラス自動生成

「Host」テーブルを例にすると、長いですが以下のTSクラスを自動で生成します。
ファイル名はパスカルケースを採用していることに注意してください。
テーブルのクラスと共に、そのクラスを活用するためのSetクラスを自動で作成します。
SetクラスではそのクラスをAPI経由で操作する関数などが自動で用意されます。

Host.ts
import {postApi} from "~/utils/myUtils";
import Swal from "sweetalert2";
import {BaseSetClass} from "~/utils/class/baseSetClass";

export class Host {
    hostId?: number;
    appId?: number;
    host: string = "";
    userName: string = "";
    userPass: string | null = null;
    savePass: boolean = false;
    port: string = "3306";
    hostOrder: number = 10;
    dbName: string = "";
    dateTime?: string;

    constructor() {

    }
}


export class HostSet extends BaseSetClass<Host> {
    constructor() {
        super(Host);
    }

    async init() {
        //テスト用
        //const config = useRuntimeConfig();
        //if (!config.public.isProduction && this.newItem) {
        //    this.newItem. = "";
        //}
        await this.getHost();
    }

    async getHost() {
        let res = await postApi("host/getHost", {
            appId: this.selectedApp?.appId,
        });
        console.log(res.data);
        if (!res) return;
        this.items = res.data;
    }

    async addHost() {
        if (!this.newItem) return;

        if (!this.newItem.host) {
            this.addMessage({
                text: "ホスト名を入力して下さい。",
            })
            return;
        }

        if (!this.newItem.userName) {
            this.addMessage({
                text: "アクセスユーザ名を入力して下さい。",
            })
            return;
        }

        if (!this.newItem.dbName) {
            this.addMessage({
                text: "接続DB名を入力して下さい。",
            })
            return;
        }

        let res = await postApi("host/addHost", {
            appId: this.selectedApp?.appId,
            item: this.newItem,
        });
        console.log(res);
        if (!res) {
            return;
        }
        this.addMessage({
            text: `ホストを追加しました。`,
            risk: 0,
        });
        this.newItem = new Host();
        await this.getHost();
    }

    async updateHost(dialog: any) {
        if (!this.editing) return;

        if (!this.editing.host) {
            this.addMessage({
                text: "ホスト名を入力して下さい。",
            })
            return;
        }

        if (!this.editing.userName) {
            this.addMessage({
                text: "アクセスユーザ名を入力して下さい。",
            })
            return;
        }

        if (!this.editing.dbName) {
            this.addMessage({
                text: "接続DB名を入力して下さい。",
            })
            return;
        }


        let res = await postApi("host/updateHost", {
            appId: this.selectedApp?.appId,
            item: this.editing,
        });
        console.log(res);
        if (!res) {
            return;
        }
        this.addMessage({
            text: `ホストを更新しました。`,
            risk: 0,
        });
        dialog.close();
        await this.getHost();
    }

    async deleteHost(dialog: any) {
        if (!this.selected) return;
        let res = await postApi("host/deleteHost", {
            appId: this.selectedApp?.appId,
            item: this.selected,
        });
        console.log(res);
        if (!res) {
            return;
        }
        this.addMessage({
            text: `ホストを削除しました。`,
            risk: 0,
        });
        dialog.close();
        await this.getHost();
    }
}

BaseSetClass

Setクラスはその基底となる「BaseSetClass」を継承します。「BaseSetClass」の内容は以下の通りです。

BaseSetClas.ts
import {App} from "~/utils/class/app";
import {useStore} from "~/composables";

export class BaseSetClass<T> {
    selectedApp?: App | null;
    addMessage?: any;

    newItem: T | null = null;
    items: T[] = [];
    selects: number[] = [];
    selected: T | null = null;
    editing: T | null = null;

    constructor(cls: new () => T) {
        let {selectedApp, addMessage} = useStore();
        this.selectedApp = selectedApp.value;
        this.addMessage = addMessage;
        this.newItem = new cls();
    }

    edit(item: T) {
        this.editing = {...item};
    }

    select(item: T) {
        this.selected = {...item};
    }
}

Discussion