zoukankan      html  css  js  c++  java
  • 三步实现PHP低代码框架

    突然想到这么一个标题党的事情,试试看。注:仅做简单梳理,未完成成品。

    先想清楚核心原理,然后分别从后端、前端设计实现。

    核心原理

    低代码,如果简单理解为针对常规应用的CRUD场景,以一种DSL语言的形式,实现系统的开发。这种形式,减少了程序员的重复劳动,甚至可以让不太懂程序开发的人也能完成系统的开发-这也许正是“低”的含义。实则,我们知道多数人的认知是模型驱动的开发思想。

    要达到后者的目的,首先得定义一套DSL语言,以便一般用户容易掌握,从而通过对CRUD这类功能所需信息的定义来实现系统的开发。

    软件工程中,传统过程是要先设计,再实现;通过低代码的DSL语言,实际上将设计和实现两个动作合二为一了,设计即实现。

    软件设计方法分结构化设计和面向对象设计,我们所熟知的画ER图就是结构化设计的经典步骤。数据实体、属性、关系,作为我们对现实世界事物静态状态下的认知的映射,是最基础的分析和设计工作。因此,我们的DSL语言中,首先要支持实体的定义。

    这里,我们可以考虑使用PlantUML中的实体关系图语言作为我们的DSL语言,这样有一个很大的好处,实体图可以渲染成图形格式便于评审交流。

    不过稍微有点尴尬的是,目前PlantUML还没有原生的PHP解析引擎,需要封装其官方的Jar包调用执行解析和转换,才能最终生成PHP版的实体类代码。https://github.com/mk-conn/plant2code

    后端实现

    路由

    选一MVC框架,如ThinkPHP v6,基于其灵活的路由机制,实现按实体的CRUD界面和API请求入口调度。

    在路由定义文件(config/route.php)中加上:

    use thinkfacadeRoute;
    
    // 主界面
    Route::get('lc/:entity$', 'EntityCRUD/index');
    // API
    Route::rule('lc-api/:entity/:action', 'EntityCRUD/:action');
    
    

    CRUD

    基于 think-orm 的特性,可以很便捷的实现相应功能。下面做个示范:

    https://www.kancloud.cn/manual/think-orm/1257998

    <?php
    
    namespace appcontroller;
    
    use appBaseController;
    use thinkfacadeDb;
    
    class EntityCRUD extends BaseController
    {
        /**
         * 构造 CRUD UI
         * @param $entity
         */
        public function index($entity)
        {
            echo $entity . ' Entity/index';
        }
    
        /**
         * 查询API
         * @param $entity
         */
        public function retrieve($entity)
        {
            echo $entity . ' Entity/retrieve';
        }
    
        /**
         * 增加API
         * @param $entity
         */
        public function create($entity)
        {
            $data = input('post.');
    
            $id = Db::name($entity)->insertGetId($data);
    
            echo $entity . ' Entity/create:' . $id;
        }
    
        /**
         * 修改API
         * @param $entity
         * @param $id
         */
        public function update($entity, $id)
        {
            $data = input('post.');
            Db::name($entity)->where('id' , $id)->update($data);
            echo $entity . ' Entity/update ' . $id;
        }
    
        /**
         * 删除API
         * @param $entity
         * @param $id
         */
        public function delete($entity, $id)
        {
            Db::table($entity)->delete($id);
            echo $entity . ' Entity/delete ' . $id;
        }
    }
    

    实体定义

    基于plantuml 的语法,如果数据库使用mongodb,则直接用plantuml的实体关系图特性即可完成实体类的完整定义,都不用考虑与数据库层字段类型的映射问题;如果基于传统的关系型数据库如mysql,则需要进一步实现字段类型的映射。

    app/definition/entity/company.puml

    @startuml
    entity "Company" as e01 {
      *id: int <<generated>>
      *name: string
     *phone1: string
      --
      phone2: string
      fax: string
      address1: string
      address2: string
      city: string
      state: string
      zip: string
      primary_url: string
      owner: int
      *type: int
      email: string
      description: text
    }
    
    @enduml
    

    或者基于plantuml也能处理的JSON或YAML格式:

    
    @startyaml
    name: account_receivable_invoice
    label: 收票
    icon: account
    enable_api: true
    enable_files: true
    fields:
      name:
        label: 标题
        type: text
        required: true
      bill_id:
        label: 付款单ID
        omit: true
        hidden: true
        type: text
      amount:
        label: 发票总金额
        type: number
        required: true
      invoice_number:
        label: 发票张数
        type: number
        required: true
      payable_id:
        label: 应付记录
        type: lookup
        reference_to: account_receivable
        relatedList: true
        required: true
      contract_id:
        label: 合同
        type: master_detail
        reference_to: contracts
        required: true
      owner:
        label: 上传人
        omit: false
        readonly: true
        type: lookup
        reference_to: users
      company_id:
        omit: false
        hidden: false
        label: 我方单位
    list_views:
      all:
        label: 所有
        columns:
          - name
          - amount
          - invoice_number
          - owner
          - created
    permission_set:
      user:
        allowCreate: false
        allowDelete: false
        allowEdit: false
        allowRead: true
        modifyAllRecords: false
        viewAllRecords: false
        modifyCompanyRecords: false
        viewCompanyRecords: true
      admin:
        allowCreate: true
        allowDelete: true
        allowEdit: true
        allowRead: true
        modifyAllRecords: true
        viewAllRecords: true
    @endyaml
    

    注:取自 steedos 项目中代码,最终语法与前端框架统一或不统一均可,仅演示思路。

    对应的SQL

    
    CREATE TABLE `company` (
      `id` INT(10) NOT NULL auto_increment,
      `module` INT(10) NOT NULL default '0',
      `name` varchar(100) default '',
      `phone1` varchar(30) default '',
      `phone2` varchar(30) default '',
      `fax` varchar(30) default '',
      `address1` varchar(50) default '',
      `address2` varchar(50) default '',
      `city` varchar(30) default '',
      `state` varchar(30) default '',
      `zip` varchar(11) default '',
      `primary_url` varchar(255) default '',
      `owner` int(11) NOT NULL default '0',
      `description` text,
      `type` int(3) NOT NULL DEFAULT '0',
      `email` varchar(255),
      `custom` LONGTEXT,
      PRIMARY KEY (`id`),
    	KEY `idx_cpy1` (`owner`)
    );
    

    以前一直眼馋的是yii框架中的db相关工具,比较专业又易懂,如果能将其抽取出来独立使用就好了。

    DB初始化

    实现实体定义中字段类型与相应数据库引擎的字段映射关系解析和处理后,再借助于框架的数据库迁移工具,可以比较容易实现DB Shcema的初始化,包括Schema升级.

    https://www.kancloud.cn/manual/thinkphp6_0/1118028

    UI 渲染

    选一前端低代码框架如百度的 amis,其中自带CRUD专用组件,可以很简单的方式实现。

    https://baidu.gitee.io/amis/zh-CN/components/crud

    按照其CRUD所需返回的相应格式在后端组装数据格式,返回传递给amis即可。

    <!DOCTYPE html>
    <html lang="zh">
    <head>
        <meta charset="UTF-8" />
        <title>amis demo</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta
                name="viewport"
                content="width=device-width, initial-scale=1, maximum-scale=1"
        />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <?php
        $base    = request()->root();
        $root    = strpos($base, '.') ? ltrim(dirname($base), DIRECTORY_SEPARATOR) : $base;
        if ('' != $root) {
            $root = '/' . ltrim($root, '/');
        }
        ?>
        <link rel="stylesheet" href="<?php echo $root ?>/static/amis-sdk-v1.1.6/sdk.css" />
        <link rel="stylesheet" href="<?php echo $root ?>/static/amis-sdk-v1.1.6/helper.css" />
        <!-- 从 1.1.0 开始 sdk.css 将不支持 IE 11,如果要支持 IE11 请引用这个 css,并把前面那个删了 -->
        <!-- <link rel="stylesheet" href="sdk-ie11.css" /> -->
        <!-- 不过 amis 开发团队几乎没测试过 IE 11 下的效果,所以可能有细节功能用不了,如果发现请报 issue -->
        <style>
            html,
            body,
            .app-wrapper {
                position: relative;
                 100%;
                height: 100%;
                margin: 0;
                padding: 0;
            }
        </style>
    </head>
    <body>
    <div id="root" class="app-wrapper"></div>
    <script src="<?php echo $root ?>/static/amis-sdk-v1.1.6/sdk.js"></script>
    <script type="text/javascript">
        (function () {
            let amis = amisRequire('amis/embed');
            // 通过替换下面这个配置来生成不同页面
            let amisJSON = {
                "type": "page",
                "body": [
                    {
                        "label": "新增",
                        "type": "button",
                        "actionType": "dialog",
                        "level": "primary",
                        "className": "m-b-sm",
                        "dialog": {
                            "title": "新增表单",
                            "body": {
                                "type": "form",
                                "api": "post:https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/sample",
                                "controls": [
                                    {
                                        "type": "text",
                                        "name": "engine",
                                        "label": "Engine"
                                    },
                                    {
                                        "type": "text",
                                        "name": "browser",
                                        "label": "Browser"
                                    }
                                ]
                            }
                        }
                    }, {
                    "type": "crud",
                    "api": "https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/sample",
                    "syncLocation": false,
                    "columns": [
                        {
                            "name": "id",
                            "label": "ID"
                        },
                        {
                            "name": "engine",
                            "label": "Rendering engine"
                        },
                        {
                            "name": "browser",
                            "label": "Browser"
                        },
                        {
                            "name": "platform",
                            "label": "Platform(s)"
                        },
                        {
                            "name": "version",
                            "label": "Engine version"
                        },
                        
                        {
                            "type": "operation",
                            "label": "操作",
                            "buttons": [
                                {
                                    "label": "修改",
                                    "type": "button",
                                    "actionType": "drawer",
                                    "drawer": {
                                        "title": "修改表单",
                                        "body": {
                                            "type": "form",
                                            "initApi": "https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/sample/${id}",
                                            "api": "post:https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/sample/${id}",
                                            "controls": [
                                                {
                                                    "type": "text",
                                                    "name": "engine",
                                                    "label": "Engine"
                                                },
                                                {
                                                    "type": "text",
                                                    "name": "browser",
                                                    "label": "Browser"
                                                }
                                            ]
                                        }
                                    }
                                },
                                {
                                    "label": "删除",
                                    "type": "button",
                                    "actionType": "ajax",
                                    "level": "danger",
                                    "confirmText": "确认要删除?",
                                    "api": "delete:https://3xsw4ap8wah59.cfc-execute.bj.baidubce.com/api/amis-mock/sample/${id}"
                                }
                            ]
                        }
                    ]
                },
                ]
            };
            let amisScoped = amis.embed('#root', amisJSON);
        })();
    </script>
    </body>
    </html>
    
    

    注:未完成动态拼接。

    进阶

    权限、安全、代码缓存、细节完善等。

    本文来源:http://www.cnblogs.com/x3d/,转载请注明。
  • 相关阅读:
    视图容器组件使用
    组件的学习
    伸展树
    二叉搜索树
    二叉树
    笛卡尔树
    二叉堆
    vim配置
    使用vim-pathogen 进行插件管理
    C/C++中的变量和静态变量
  • 原文地址:https://www.cnblogs.com/x3d/p/14748219.html
Copyright © 2011-2022 走看看