├── .gitignore ├── CODESTYLE.md ├── LICENSE ├── LazyRest4minibook.pdf ├── README.md ├── _build.php ├── _lp ├── config │ ├── core.php │ └── exception.php ├── lib │ ├── Lazyphp │ │ ├── Core │ │ │ ├── Application.php │ │ │ ├── Database.php │ │ │ ├── Datameta.php │ │ │ ├── Dispatcher.php │ │ │ ├── Ldo.php │ │ │ ├── LpObject.php │ │ │ └── Object.php │ │ └── Doc │ │ │ ├── Builder.php │ │ │ ├── Extractor.php │ │ │ └── Resources │ │ │ └── views │ │ │ └── template │ │ │ └── index.html │ ├── flight │ │ ├── Engine.php │ │ ├── Flight.php │ │ ├── autoload.php │ │ ├── core │ │ │ ├── Dispatcher.php │ │ │ └── Loader.php │ │ ├── net │ │ │ ├── Request.php │ │ │ ├── Response.php │ │ │ ├── Route.php │ │ │ └── Router.php │ │ ├── template │ │ │ └── View.php │ │ └── util │ │ │ └── Collection.php │ └── functions.php └── lp.init.php ├── assets ├── css │ ├── app.css │ ├── app.less │ ├── bootstrap.min.css │ ├── stylish-portfolio.css │ └── toastr.min.css ├── fonts │ ├── lrfont.eot │ ├── lrfont.svg │ ├── lrfont.ttf │ └── lrfont.woff ├── image │ ├── logo.lr4.png │ └── news.jpg ├── script │ ├── app.js │ ├── jquery.base64.min.js │ └── toastr.min.js └── template │ ├── inputcode.tpl.php │ ├── logiccode.tpl.php │ └── outputcode.tpl.php ├── auth.demo.txt ├── behat.yml ├── bower.json ├── coderemote.demo.txt ├── codetpl ├── cache │ └── 77 │ │ └── 77ab97689b8f8003b5f352979c1d248abc1b30032a8d71bb7ecc1bc186ab613c.php └── code │ ├── controller.tpl.code │ ├── custom.tpl.code │ ├── delete.tpl.code │ ├── insert.tpl.code │ ├── list.tpl.code │ └── update.tpl.code ├── compiled └── route.php ├── composer.full.json ├── composer.json ├── composer.lock ├── config.yaml ├── config ├── app.php ├── database.php └── exception.php ├── controller ├── AuthedController.php ├── Controller.php └── LazyphpController.php ├── docs └── index.html ├── features ├── bootstrap │ └── FeatureContext.php └── user.feature ├── index.php ├── lib └── functions.php ├── migrations └── 20140422224730_lazy_php_db.php ├── mink.txt ├── phinx.yml ├── phpunit.xml ├── route.php ├── sample.bowerrc ├── sample.htaccess ├── tests ├── app │ └── dbTest.php ├── data │ └── dev.sql ├── framework │ ├── dbtClass.php │ ├── functionTest.php │ └── sampleTest.php └── loader.php ├── upload2sae.php └── view ├── ajax ├── default.tpl.php └── main │ ├── lazyphp_code_functions.tpl.php │ ├── lazyphp_field_add.tpl.php │ └── lazyphp_settings.tpl.php └── web ├── default.tpl.php ├── footer.lr.tpl.php ├── footer.tpl.php ├── header.lr.tpl.php ├── header.tpl.php ├── info.tpl.php ├── lazyrest.tpl.php └── main ├── lazyphp_index.tpl.php ├── lazyphp_interface_create.tpl.php ├── lazyphp_interface_list.tpl.php ├── lazyphp_login.tpl.php └── lazyphp_projects.tpl.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | assets/bower_components/ 4 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 5 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 6 | composer.lock 7 | -------------------------------------------------------------------------------- /CODESTYLE.md: -------------------------------------------------------------------------------- 1 | 编码规范 2 | ======== 3 | 4 | ## 自动加载及PSR4 5 | LP4采用Composer PSR4进行自动加载,第三方组件请使用自己的Namespace,做成独立的包即可。 6 | 7 | ## 函数命名 8 | my_method 9 | 10 | ## 方法命名 11 | myFirstMethod 12 | 13 | 14 | ## 文件和目录名 15 | 以.php结尾,目录和文件名采用驼峰写法分隔单词。 16 | 17 | ## 括号对齐 18 | ``` 19 | function name() 20 | { 21 | 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /LazyRest4minibook.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/easychen/LazyRest4/42b99dc4f39ff14cd7ebaf10727cb60c409859a8/LazyRest4minibook.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LazyRest4 完全手册 2 | 3 | [TOC] 4 | 5 | ## 简介 6 | LazyRest,是一个基于Web的类Rest风格API生成器。LazyRest4(以下简称LR4)是为LazyPHP4专门定制的全新设计的版本,但只要简单的修改代码模板,就可以为其他的框架生成代码。 7 | 8 | 和之前的版本相比,LR4具有以下特性: 9 | 10 | - 直接生成最终代码,更好调试、更高性能。 11 | - 全新的交互界面,更漂亮和便捷。 12 | - 按接口而非数据表设计界面,具有更大的灵活性。 13 | - 本身不需要数据库,项目配置直接保存成文件,方便分享。 14 | 15 | 请使用Chrome浏览器访问本应用,其他浏览器并未做兼容性处理。 16 | 17 | ## 安装 18 | 19 | ### 获取源文件 20 | 下载安装包,并解压。或者直接Git Clone。 21 | Repo地址: https://github.com/easychen/LazyRest4 22 | 23 | ### 将apache指定到对应目录 24 | 25 | 注意,必须将Document Root指定到LR4的根目录,否则会出现路径问题。 26 | 27 | ### 初始化项目 28 | #### 使用composer安装PHP依赖 29 | 30 | ``` 31 | composer install --no-dev 32 | ``` 33 | 34 | #### 使用Bower安装JS依赖 35 | 36 | ``` 37 | cp sample.bowerrc .bowerrc 38 | bower install 39 | ``` 40 | 41 | #### 修改URL重写规则 42 | 43 | ``` 44 | cp sample.htaccess .htaccess 45 | ``` 46 | 47 | #### 设置默认管理帐号 48 | 49 | 打开 /config/app.php 50 | 修改以下内容 51 | 52 | ``` 53 | $GLOBALS['lpconfig']['admin_email'] = 'your email'; 54 | $GLOBALS['lpconfig']['admin_password'] = 'your password'; 55 | ``` 56 | 57 | 如果配置正常,这时候访问Web根目录,LazyRest就已经可以使用了。 58 | 59 | ## 实战指南 60 | 61 | 为了更好的演示,我们选择一个真实需求来看看LR4到底怎么用。 62 | ### 需求 63 | 我们需要一个公司部门内使用的函数共享库。这个共享库位于公司内网,公司员工可以注册帐号,登入帐号以后可以添加、下载、修改和删除函数。添加函数时可以将其设置为私有,这样别人就无法浏览和下载该函数了。 64 | 65 | ### 设计MySQL表 66 | 67 | 我们使用PHPMyAdmin来设计数据表。新建一个库,coderemote。总共两张表,一张user表用来存放开发者帐号、领一张code表,用来存放函数。 68 | 69 | user表结构如下: 70 | 71 |  72 | 73 | 设计数据表时需要注意以下两点: 74 | 75 | - 每个表需要有id字段,这个字段在查询接口中用于since_id,这个后文详细讲。 76 | - 每个字段需要添加注释,注释的内容是字段的中文名。 77 | 78 | code表结构如下: 79 | 80 |  81 | 82 | 为了方便显示,我们对账户名做了冗余(uname)字段,同时通过private字段来标识是否私有。 83 | 84 | 索引一般在整个API完成后,根据SQL来添加,这里就暂时跳过了。 85 | 86 | ### 创建新项目 87 | 88 | LR4采用项目文件保存数据,所以使用方式类似软件。已有项目可以直接上传项目文件,然后继续,这里我们直接「创建新项目」。 89 | 90 |  91 | 92 | 在设计接口之前,我们需要把数据库配置下。这样在添加字段时,可以便捷的使用数据库的信息。点击顶导航右侧的⚙图标,设置好Mysql信息。注意这个数据库可以是远程的,并不要一定和LR在一台机器上。 93 | 94 |  95 | 96 | 点击保存后,LR会连接数据库,并把数据表信息缓存起来。所以如果后续有数据库变动,请再到设置页面,点一遍保存来刷新数据表信息。 97 | 98 | ### 创建接口 99 | 100 | 然后我们来创建接口。首先是帐号注册接口。 101 | 102 |  103 | 104 | 一般我会把写操作设置为POST,把读操作设置为GET。这里我并不严格遵守RESTful的规范,因为觉得这样更简单一些。 105 | 106 | 目标数据表是我们生成SQL时需要操作的数据表,如果要使用联表操作,则目标数据表可以不选。 107 | 108 |  109 | 110 | 然后是输入字段和输入过滤代码。简单的说,「输入字段」部分,会帮你自动生成输入过滤代码,包括输入检查,类型转换等。而如果你觉得生成的代码不好用,或者没有把事情做完时,可以在「输入过滤代码」部分进行补充。 111 | 112 | 然后我们来添加字段,点击「添加新字段」,然后会出现字段设置界面。因为是LazyRest,所以当然不会让你挨个手工去填写,我们在右边做了快速输入。在「来自数据库」tab下,选择「user」表,然后选上Email字段,点「填至左侧」,LR会根据数据表信息自动填上一部分信息。 113 | 114 |  115 | 116 | 因为email字段设置为了not null,所以检查函数默认启用了check_not_empty。但是则会这个函数不够强,我们需要检查是否为email,所以我们自己写一个,先把名字写上(注意LP4中的检查函数必须以check_开头,过滤函数则没有限制): 117 | 118 |  119 | 120 | 保存好。LR也直接提供了公共函数的在线编写界面,点击顶导航第二个图标🖋,会弹出代码编辑图层。 121 | 122 |  123 | 124 | 125 | 保存后,点右上角关掉。接着再把其他帐号注册接口需要的字段添进来。 126 | 我们不希望密码明文存储,所以我们在输入过滤代码中对它进行sha1编码(生产环境需要更强的密码策略)。 127 | 128 | ``` 129 | 134 | ``` 135 | 136 | 这样输入部分就完成。接下来看业务逻辑代码。LR预置了添加、修改、列表、删除四大内业务逻辑模板,这些模板你都可以在```/codetpl/code```目录下进行修改。 137 | 138 | 这里我们选添加,添加模板预置了唯一字段检查逻辑。在我们的需求里,email字段是唯一的。于是把email字段加上。 139 | 140 | 这样核心逻辑就完成了。为了方便使用,在预置逻辑里,添加、修改和删除都会返回对应的那条数据的信息(用于提示,某某数据已经删除之类)。 141 | 142 | 所以这里还用到输出字段和输出过滤代码。这输入很像,但记住password字段其实是不需要返回的。 143 | 144 | 选完以后我们就可以保存接口了。 145 | 146 | ### 生成和测试代码 147 | 148 | 点击顶导航右三图标,代码会生成到controller目录下的LazyRestController.php。为了调试和部署方便,LR目前所有代码都放到这个文件里。 149 | 150 | 这时候就可以开始测试了。 151 | 152 | ``` 153 | 如果生成代码中有语法错,会导致LR整体报错,只需要删除生成的文件就好。 154 | ``` 155 | 156 | 如果路由没有更新,请记得把LazyPHP4的自动重建路由开关打开。在/config/app.php中 157 | 158 | ``` 159 | $GLOBALS['lpconfig']['buildeverytime'] = true; 160 | ``` 161 | 162 | ### 测试接口 163 | 164 | 我一般使用[PostMan](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop)来测试API,所以以下以它为例。 165 | 166 |  167 | 168 | 注意几个点: 169 | 170 | - 我们设置了这个接口只支持POST,所以用GET会提示404 171 | - LazyPHP4.5开始因为支持了Web界面,所以要在添加一个特殊的Header才能强制返回json。 172 | 173 | ``` 174 | 'LP4-Request-Type':'json' 175 | ``` 176 | 177 | 填好参数,接口已经正常工作了。 178 | 179 |  180 | 181 | ### 授权机制 182 | 183 | 在这个项目里,我们采用token机制:通过email和密码换取一个token,需要授权的接口,带上这个token来访问。 184 | 185 | 首先我们来做这个接口,在LR中再新建一个。顺便说句,点击这个图标就可以返回接口列表。 186 | 187 |  188 | 189 | 将email和password添加为输入字段,设置为必填。 190 | 191 |  192 | 193 | 然后我们这次直接自定义业务逻辑代码 194 | 195 | ``` 196 | auth(); 269 | lazyrest_auth_check(); 270 | } 271 | } 272 | } 273 | ``` 274 | 275 | 可以看到,所有授权控制的「任意访问」设置为否的接口,都会通过lazyrest_auth_check函数进行权限检查。但是lazyrest_auth_check函数默认是没定义的,所以默认不生效。我们只需要在公共函数中把这个函数实现了,就完成了权限控制。 276 | 277 | 先看看$this->auth()的实现,在/controller/AuthedContrllor.php里边: 278 | 279 | ``` 280 | protected function auth() 281 | { 282 | $token = t(v('_token')); 283 | if( strlen( $token ) > 0 ) 284 | { 285 | session_id( $token ); 286 | } 287 | 288 | session_start(); 289 | } 290 | ``` 291 | 它接受_token参数,并把其作为session id,自动enable了session。所以我们只需要在lazyrest_auth_check里边简单的检查下$_SESSION数据是否正常就OK了。 292 | 293 | ``` 294 | function lazyrest_auth_check() 295 | { 296 | if( intval( $_SESSION['level'] ) < 1 ) 297 | { 298 | send_error( 'AUTH' , '当前权限不足以访问本接口' ); 299 | exit; 300 | } 301 | } 302 | ``` 303 | 304 | 同时,如果对auth函数的实现不满意,可以直接覆盖它的实现。 305 | 保存后Build下代码,就可以测试了: 306 | 307 |  308 | 309 | 带上token,已经可以访问了。 310 | 311 |  312 | 313 | ### 其他预置接口 314 | 315 | #### 修改接口 316 | id和code作为输入字段;业务逻辑代码选「 修改 」,WHERE子句添加id,选完全匹配。 317 | 318 |  319 | 320 | 然后因为只有自己才能修改自己的代码,所以在业务逻辑代码中加上权限检查: 321 | 322 | ``` 323 | if( $last['uid'] != uid() ) return send_error( 'AUTH' , '只能修改自己发布的函数' ); 324 | ``` 325 | 注意在UPDATE和DELETE预置模块中,会查询对应的记录,并保存到$last中供使用。 326 | 327 | #### 列表接口 328 | 329 | 列表接口很简单,预置的业务逻辑也是WHERE匹配,和前文一样处理就好。 330 | 331 |  332 | 333 | 下边是列表接口支持的参数: 334 | 335 | - _order:asc/dsec。 336 | - _order_by:字段名。 337 | - _since_id:从某个id开始返回。 338 | - _count:每次返回的数据条数,最多1000条。 339 | 340 | 但上文我们发现了一个问题,私有函数也被列出来。所以我们需要在业务逻辑处,进行调整,给where子句再加一个条件: 341 | 342 | ``` 343 | $add_where = " (`private` != 1 OR `uid` = '" . intval( $uid ) . "' ) "; 344 | 345 | if( strlen( t( $where_sql ) ) < 1 ) $where_sql = 'WHERE ' . $add_where; 346 | else $where_sql .= ' AND (' . $add_where . ')'; 347 | ``` 348 | 349 | 要了解如何调整sql的拼接,直接查看生成好的代码是最好的办法。 350 | 351 | 另外,为了让查询的实现更简单点,LR预置的删除是直接删除,而非标记删除。 352 | 353 | CodeRemote项目的LR文件,可以 [点击这里查看](./coderemote.demo.txt) ;我还准备了一个[只有用户注册和授权验证的LR文件](./auth.demo.txt),方便大家直接开始做业务逻辑。 354 | 355 | 356 | ## 部署 357 | 首先你要有一个可以运行的LazyPHP4环境,然后把LazyRestController.php放到controller目录下,再php _build.php 更新下路由就能用了。 358 | 359 | ## 其他 360 | ### 继续开发和贡献代码 361 | 362 | 如果你有兴趣给LR贡献代码,有几个我觉得很值得做的地方供参考: 363 | 364 | - 自动生成API的JS SDK。 365 | - 自动打包LazyRestController.php,并发布到云平台。可以生成phing文件来完成。 366 | - 整理并分享LR项目文件,让有同样需求的同学不用重复开发(记得分享的文件要干点数据库信息)。 367 | - 根据LR项目文件里边的数据库信息,在数据表不存在时,自动创建数据表。 368 | 369 | #### 几个开发Tips 370 | 371 | - 访问 ```/session``` 页面可以查看全部元数据。 372 | - 代码模板使用Twig引擎,挺好用的。 373 | - css使用less完成,php _build.php的时候会自动编译app.less到app.css 374 | 375 | ### 捐助 376 | 本来只是做来给自己用的,但因为很多同学表示也想用,就放出来了。写文档解释如何使用什么的挺费事,所以当LazyRest帮你节省了时间和精力之后,可以考虑打赏我一点零花钱 😁 377 | 378 | 微信 379 | 380 |  381 | 382 | 支付宝 383 | 384 |  385 | 386 | 387 | -------------------------------------------------------------------------------- /_build.php: -------------------------------------------------------------------------------- 1 | generate(); 56 | } catch (Exception $e) { 57 | echo 'There was an error generating the documentation: ', $e->getMessage(); 58 | } 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /_lp/config/core.php: -------------------------------------------------------------------------------- 1 | '10000' , 'message' => 'route error' ); 4 | $GLOBALS['rest_errors']['INPUT'] = array( 'code' => '10001' , 'message' => 'input error' ); 5 | $GLOBALS['rest_errors']['DATABASE'] = array( 'code' => '30001' , 'message' => 'database error' ); 6 | $GLOBALS['rest_errors']['DATA'] = array( 'code' => '40001' , 'message' => 'data error' ); 7 | $GLOBALS['rest_errors']['AUTH'] = array( 'code' => '20001' , 'message' => 'auth error' ); 8 | $GLOBALS['rest_errors']['TMPLATE'] = array( 'code' => '50001' , 'message' => 'template error' ); 9 | -------------------------------------------------------------------------------- /_lp/lib/Lazyphp/Core/Application.php: -------------------------------------------------------------------------------- 1 | request = new Request(); 21 | $this->response = new Response(); 22 | $this->router = new Router(); 23 | $this->dispatcher = new Dispatcher(); // LP自己的Dispatcher 24 | 25 | } 26 | 27 | public function run() 28 | { 29 | $dispatched = false; 30 | 31 | if ($this->request->ajax) 32 | { 33 | $this->response->cache(false); 34 | } 35 | 36 | while ($route = $this->router->route($this->request)) 37 | { 38 | $params = array_values($route->params); 39 | //TODO 修复参数顺序和meta不一致 40 | 41 | $continue = $this->dispatcher->execute( 42 | $route->callback, 43 | $params 44 | ); 45 | 46 | $dispatched = true; 47 | 48 | if (!$continue) break; 49 | 50 | $this->router->next(); 51 | } 52 | 53 | if (!$dispatched) { 54 | $this->notFound(); 55 | } 56 | 57 | } 58 | 59 | public function notFound() 60 | { 61 | $code=404; 62 | if('OPTIONS'==$_SERVER['REQUEST_METHOD']) 63 | { 64 | //ajax跨域请求之前的options请求,为了避免如上传失败,返回200 65 | $code=200; 66 | } 67 | $this->response 68 | ->status($code) 69 | ->write( 70 | '