文章为从0到1了解内容管理系统搭建与编写,因为一篇文章内容篇幅过长,文章内容通过压缩,该项目中相同逻辑的实现只以一个实例做为描述,主要以核心关键功能的开发做为主要的讲解步骤。若有想学习完整内容系统编写可在留言区留言,我会尽快完成完整版的实战教程发布。谢谢。本篇不涉及vue、nodejs的前端框架。javascript
如下内容有过一些了解便可:php
注意:本篇文章部分细节因为篇幅关系并不会去深刻完善,而且相同逻辑的实现只以一个实例做为描述,主要以核心功能的开发做为主要的讲解步骤。本篇不涉及vue、nodejs的前端框架。css
在了解搭建网站前,须要普及一些基本的知识概念,防止某些同窗在一方面有概念性的错误,而且我我的认为在学习一方面知识前须要对这一方面的知识有一个广度的了解,这里所指的广度为这东西是用来干什么的、做用是什么、为何要这样写;因此在正式开始介绍如何编写CMS前将会介绍这一部份内容。为了方便阅读第一点内容引入我另一篇原创文章。html
在学习一门技术的时候,每每是了解总体体系架构才能更好的学习,否则在学习的过程当中会出现不知道为何这样作,作出这一部分是该总体部分的哪一个区域,只会跟着作,可是并不了解这是在干啥。可能一些萌新体会颇深,就照着打,老师教怎么写,我就怎么写,反正作出来了。前端
本篇博文,就来用最接地气的方式对基本的web开发作一个总体的讲解,带各个萌新过一遍web开发的流程,好让各位萌新知道学习的时候学习了什么知识点,这个知识点可以干哈。vue
最开始,咱们就以我的浏览网站的方式给你们说一下这一个过程是如何运做的。
html5
咱们访问网站,通常先打开浏览器(不要杠),输入一个网址,随后浏览器打开一个网页。在你在请求这一个网址数据的时候,已经发生了一系列的操做。java
假设你输入的是“csdn.net”,浏览器想要去访问你这个网站,首先须要的是得到你这个网站的IP地址。可能就有萌新问了**“什么是IP地址?”。IP地址就是“指互联网协议地址,或者说网际协议地址”。又有萌新说了“你这么说我怎么懂?”**,好了如今容我慢慢道来。node
IP地址就是在网络中,定位你这台电脑,或者说是设备的一个标记,这个标记是人们指定好的标准协议而产生的(协议就是你和我说好了一件事,拉钩了,之后要这样作)。就像你家的门牌号例如叫作“CSDN市,CSDN区,CSDN街道的CSDN小区第CSDN栋的第CSDN号”…这是由有关机构制定的一套规范名称,不容许随意更改;咱们换个例子,例如你家是“深圳市南山区深南大道某某小区第八栋808”,你写快递的收件地址确定是写这个,难道你写“宇宙第一星球第一市第一栋第一号”?地址是由专门组织规范且制定的一套定位规范,遵循这个规范可使遵循该规范的设备或者人之间相互通讯,这个通讯指能够传达交互,可以定位、找到。综上所述,IP地址就不要纠结为何要这样写,只要知道这个IP地址是你要用的就行。mysql
如今IP地址知道是什么了,那么怎么得到IP地址?这个时候就须要用到DNS了,啥是DNS??!!
DNS的英文全称是 Domain Name System,翻译过来就是域名系统。好了,这个时候问题又来了。
啥是域名?域名就是用来标识IP地址的一个标记,或者说是昵称。“为何不直接用IP地址?”这个问题问得好,若是咱们人不用名称,就用身份证号,我叫你的时候就会叫“450333333333333333…”。。。我以为这样不是很好。。。当人们以为使用IP地址不方便记忆后,就产生了域名地址,就像CSDN,咱们就知道是CSDN就行了,难道还要去记她的IP地址吗?例如CSDN的地址是192.168.1.1,难不难受…之后可能你记网站名称就在记数字了,又不方便又崩溃。好了,回归正题,咱们输入了网址后,按下Enter键后,浏览器将会去DNS请求这个域名对应的IP是什么,若是找到了,就返回一个IP地址。可能又有萌新问了,“浏览器会自动去找DNS?”,会是会,可是咱们也会给它一个目标,在咱们的网络链接里面,本地链接右键属性,里面有个IPV4,双击进去就能够查看本身配置的DNS了,通常别乱改,否则很难过的,有时候浏览器打不开网址,就是这个缘由。
记住,网络IP冲突可能会致使上不了网,这种状况在学校的机房里很常见,只要改为自动获取IP就ok了,会自动分配闲置的IP地址。
当找到了IP地址,这个时候就会向该IP地址的设备去请求数据,请求数据的意思就是,这个设备或者说服务器就像一个大型的分发机构,就是送情报的一个部门,一共有65535个窗口,每一个窗口送不一样的情报;例如咱们须要请求网站之类的数据,就经过第80个窗口请求,这个时候浏览器派来的小弟来到这个80号窗口,可能会排一下队,拿到数据后,回到浏览器,浏览器把拿到的数据显示给你看。
其实在这个时候,浏览器显示的数据会根据一些标记,进行排版,这些标记就称是HTML,HTML是 Hyper Text Markup Language 的缩写,中文名是超级文本标记语言,其实说那么深奥还不方便理解;简单来讲就是经过特定的标签,把一段文本信息标记起来,表示这段文本信息要怎么样去进行显示,或者是这个文本信息是啥东西;例如 <title>CSDN-专业IT技术社区</title>
是CSDN官网首页的标题,用了title这个标签把文本信息标记,标记好后,浏览器就知道这个文本要显示在哪里,要怎么进行显示,最终浏览器把这一段信息显示在了浏览器标题头位置:
咱们再看看另外的一个例子:
这一段HTML语言所标记了一个博客的文本,整个标记的状况为了清晰的看清楚,我在这里列出:<a href="//blog.csdn.net/" class="toolbar_to_feed" title="博客">博客</a>
,标记语言HTML那一些标记并不会进行显示,只显示了博客这个这个文本在网页上:
那是由于浏览器是经过标记语言的内容去进行显示,标记语言的做用就是告诉浏览器这里你要怎么显示这个内容,或者说这个内容有什么功能。这里是博客的一个跳转,使用的是a标签,a标签是什么?a标签就是<a>这里是要显示的文本</a>
,在a标签里面能够添加一些固定的操做,例如a标签的做用是跳转到指定的页面,那么这个页面确定是有一个连接的,那么这个连接须要什么来指定呢?
答案就很简单了,使用href来指定,这个href呢就须要把要跳转到的页面的地址给加上,在咱们查看到的HTML代码中是href="//blog.csdn.net/"
,这就表示会跳转到blog.csdn.net
这个地址,有人点击就会跳转到博客了。
那 class="toolbar_to_feed"
是什么东西?在这里咱们能够把它当作给定了一个样式,给定了一个style,要怎么样显示,你要显示的样子是什么?可能红色的底,绿色的字,俗话说,红配绿。。。这个样式的名称就叫作 toolbar_to_feed 。在这里并不会深刻的讲解这个样式要让博客这个文本显示成啥样,你们只要经过例子知道这个html是用来告诉浏览器怎么样显示这个文本,或者这个文本有什么用就ok了。其实还有些动态的数据,可是在这里并不会讲解,基本的理解这样就没问题了。专业点的说法就是构件编排用户界面。
经过以上描述就很清楚的知道,若是咱们作web开发的话,作html相关的就是给页面制做布局,怎么样好看,甚至能够作特效,让页面显示多姿多彩;通常咱们称作HTML这种,是为了数据的显示的排版工做,或者说是为了包装数据工做的这类职位叫作前端;不过前端是个相对概念,在web上能够这样理解是没问题的,不过如今的前端,若是不去大厂,基本上要作的不止是包装数据的排版那么简单,可能还会作得更多。若是咱们去作前端工做的话,还要掌握跟服务器交互的一些操做,打个比方,用户点击了一个按钮,这个按钮的功能是获取到大家的用户人数,这个时候你须要编写一个逻辑,去服务器获取到这个用户想要的数据。不过这点只是做为一个提醒,当真正接触前端的话会了解的。
有不少小问号的朋友可能会记得刚刚说的,前端可能要向服务器请求数据,那么这个数据,是否是就是传说中的后端作的?(听没听事后端某问题,反正就是后端)
后端能够理解为一些业务逻辑的代码编写实现,就是须要后端,什么是业务逻辑?简单的举个例子,就像你淘宝买东西,你点了这个物品,下单了,我要在代码上怎么实现这个下单这个背后的操做;由于下单后你还须要交易,交易要收钱,收钱你还要把这个记录记载到你存放数据的地方,咱们能够叫作数据库,存进去后,用户查看本身的下单记录,你还须要把这个记录取出来,用代码实现这个取出来这个过程给用户看到,否则没有记录那就很尴尬了,只收钱不卖货!流批!因此通常是指的是数据库(由于要存储数据,例如你网站的用户数据,确定要用东西来存储,这个东西就是数据库)进行交互以处理相应的业务逻辑。虽而后端要考虑不少东西,可是通常来讲这样举例子就比较方便理解,就不过多的谈论其它东西了。
如今整个逻辑基本上就通了,简单的理解,后端就是实现一些数据操做,业务逻辑的实现(其实可能会运维),前端呢就是负责用户的页面数据的展现排版;嗯,大致这样理解问题不大。
既然理解通了,咱们就来讲说一个网站搭建的流程是什么吧!
首先咱们须要租一个服务器,嗯…这个萌新不理解,那咱们降一个档次,那就是咱们在咱们本身的本地电脑进行试验,这样就问题不大了,方便快捷。
搭建一个简单企业门户网站其实贼简单,不吹不黑,几年前的时候,作这个仍是挺得钱的,接接外包,舒舒服服,如今就不行了,毕竟技术在更新,过期的技术也变得更加廉价了,可是依旧是基本。
如下我使用一个静态网站做为例子演示一个网站的搭建;“啥是静态网站?”。静态网站就是没有后端,好吧,简单来讲就是这样,因为后端须要一些其它语言,本篇博文针对于广泛人群,为了方便理解就不用后端了,直接静态网站做为演示,列出html的代码,到时候萌新们能够直接复制代码拿去本身试验,舒舒服服,美滋滋。
首先咱们下载一个集成环境。“啥是集成环境?”。
集成环境打个比方,就像你作菜、须要火源、锅、锅铲,这种就是环境;我作网站也要一个环境,这个环境有人给你作好了,你直接拿过来用就好,就不须要本身搭建,有些初学者就喜欢本身搭建,而后发现一堆问题,搞着搞着发现太难就不学了,简直嘤嘤嘤!初学者我我的建议先别增长本身的难度,先学,否则没搞懂就上会一脸懵圈的。如今咱们下载一个叫作phpstudy的软件,下载点这里
去官网。而后进行傻瓜式安装。
安装完后打开服务:
Apache可能会有人问是什么,Apache是服务器软件,它就是你作菜须要的必要工具之一,开启了就对了,可能你只开启Apache只能作汤,那也没事,毕竟我如今演示的是静态网站。
首先咱们把咱们的资源文件带到网站根目录下:
根目录不会找?不要紧,咱们打开网站,点击管理找到根目录就ok:
找到后把资源文件放到根目录下,删除之前的根目录下的内容便可。
而后在浏览器输入:http://127.0.0.1/ 或者输入 http://localhost/ 就能够访问咱们本地电脑上的网站了!
在以上第一节内容中,咱们已经作好了一个静态的网站,但该网站并无一些后台功能。例如后台设置网页的所展现的内容,那为何要后台设置网页展现的内容呢?当咱们的网站成功架设后,假设该网站是双十一的推广网站,图片这些所有都是标有双十一字样,当双十一事后该网站难道就不能继续使用了吗?答案固然是不,只须要编写一管理后台,用户在后台中可自由设置图片要显示哪一张。该功能完成后,用户可根据本身的须要更改对应的图片;既然图片均可以更改了,那么文章也一样能够更改,这时网站的自由度将会更高。
更改网站图片的显示与更改文字内容的显示都须要使用数据库,固然其它方式也能够,但咱们在这里使用一种较为常规与成熟的数据库方式进行存储,而且使用一个php的开发框架thinkphp来方便咱们的搭建。thinkphp的版本是5.1版本。可能有些小伙伴们问为何要使用框架?这不是增长学习成本吗?其实使用框架并不会增长你的开发时长,而且会增长你的开发效率;框架就像搭建房子时的地基,直接使用一个地基比你本身再去作一个地基更加简单方便,并且更为标准;若是你是一个新手,本身去搭建一个地基,每每会作到一半就“塌”了,这种状况也不是不可能。
首先咱们下载thinkphp5.1,解压后目录以下:
目录参考能够根据thinkpp5.1手册:
thinkphp5.1的目录结构在本文并不须要了解过多,本文将会说明须要了解的目录。
咱们复制解压出来的文件至网站根目录下,而且删除原有网站根目录下的内容:
因为thinkphp框架的入口在public目录下,咱们打开public目录进行查看:
在public目录下找到了index.php文件。因为该框架的入口文件是index.php,须要更改网站的根目录为public。打开phpstudy,依次点击其它菜单选项->软件设置->端口常规设置:
在弹出来的根目录设置中,选择public做为根目录:
此时输入localhost进行访问:
出现如上示例则表示当前thinkphp部署成功。接下来就能够进行相应的代码编写了。
在第一节中,咱们实现了一个静态网站的搭建,如今将第一节编写好的静态网站index.html文件复制到以下路径中:
个人目录是 E:\devlop\phpstuy\PHPTutorial\WWW\application\index\view\index,若是没有该目录能够本身建立。咱们浏览器再次输入localhost查看,发现依旧出现以前的web页提示,这是什么回事呢?由于咱们须要在thinkphp的控制器中,添加一行跳转到该html文件的代码。控制器文件在 E:\devlop\phpstuy\PHPTutorial\WWW\application\index\controller 下:
该目录是存放当前模块下全部控制器的地方(固然你能够不这样),控制器在thinkphp框架中用于对用户访问进行控制,例如用户须要访问首页则须要访问首页的控制器,默认是index控制器;index控制器能够对index这个页面进行逻辑控制,能够传值、权限控制等一些列操做。换句话说则是控制用户访问指定资源的逻辑(不理解也不要紧)。咱们打开index.php这个控制器:
<?php namespace app\index\controller; class Index { public function index() { return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5.1<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>'; } public function hello($name = 'ThinkPHP5') { return 'hello,' . $name; } }
改php控制器默认index为访问方法,index方法将会返回一条html的字符串,改字符串经过渲染将会显示成咱们以前所看到的部署成功的欢迎界面。在这里须要将该代码删除。换成:
return $this->view->fetch();
整个php文件则为:
<?php namespace app\index\controller; use think\Controller; class Index extends Controller{ public function index() { return $this->view->fetch(); } public function hello($name = 'ThinkPHP5') { return 'hello,' . $name; } }
return $this->view->fetch(); 咱们能够查看thinkphp5.1手册:
使用 fetch 方法将会自动定位到模板文件。thinkphp已经帮咱们写好了必定的规则,自动定位到默认view目录下对应控制器名下的index文件。在此注意,是自动定位到view目录下与控制器同名的目录下的文件,不加参数自动定位到index.html,也就是view/控制器名/index.html,因为控制器名是index,则是view/index/index.html;view目录下的index目录则是以前复制静态网站html文件的目录。
保存php文件,访问localhost:
这时发现整个web页错乱,这时由于全部css文件、js文件、img文件的路径都有所改变,这时须要更改到正确的资源加载目录。为了方便加载,在网站根目录public目录下新建一个home目录,复制该页面所需的资源文件到该目录下:
网站根目录资源的访问路径是“/”表示网站根目录下,因为在根目录下建立了一个home目录,则进一步能够写为“/home/”,在home目录下有一个asset,则能够写为“/home/assets/”,assets下的文件访问则能够根据目录进行具体访问,例如asset下的目录img有一个图片叫作1.png,那么访问则能够写成“/home/assets/img/1.png”。
了解了访问的规则后,修改index.html文件,将全部 assets/ 都更换为 /home/assets/,我使用的编辑器是 vscode,快捷键 ctrl+h 便可调出一键替换:
点击如上图中的一键替换便可完成资源内容的目录修改,随后保存,再次访问:
完美呈现,是否是贼爽?那么接下来就实现这些图片资源的可后台更换。
首先复制application目录下的index目录:
更改index-副本名为admin:
随后更改admin目录下controller目录中的index.php文件内容,原文件内容以下:
<?php namespace app\index\controller; use think\Controller; class Index extends Controller{ public function index() { return $this->view->fetch(); } }
更改成:
<?php namespace app\admin\controller; use think\Controller; class Index extends Controller{ public function index() { return $this->view->fetch(); } }
以上的内容主要更改在命名空间,从 namespace app\index\controller;更改成了 namespace app\admin\controller;。命名空间主要是为了区分不一样区域或空间内的不一样“东西”。例如学校中A班的小明与B班的小明,这二者有着班别的区别,命名空间也是如此,表示不一样区域不一样空间内的值。
更改完成后访问 http://localhost/index.php/admin/index,这行url地址表示该网站中admin模块下的index方法,其中index.php在访问首页的时候是默认隐藏,即http://localhost/index.php等于localhost,因为当下访问其余模块在此须要写全(固然能够配置隐藏,但不是本节内容则不过多增长难度)。访问后发现该页面与访问localhost出现的内容一致,这是由于admin模块中的index方法也用了return $this->view->fetch();这一行代码输出了html文件的代码,这个html文件并非index模块下的view/index下的index.html,而是admin模块下的view/index下的index.html,由于刚刚整个模块咱们都进行了复制。这时该html不符合咱们的需求,须要更换html内容,在此我使用了一模板(该模板编写是前端内容,在此并不过多赘述,实现逻辑与index.html类型,均是修改页面的资源路径便可),访问效果以下:
注:本节项目代码将会打包分享给你们。
完成后台管理页的搭建后,发现该后台全部用户都可访问,这对于一个网站是很差的权限行为;必须实现可控的权限管理,使得网站内容不得随意更改。
首先打开sqlyog,输入数据库的账号密码,通常账号为root密码为root或空:
链接成功后,邮件你本地数据库点击建立数据:
输入数据库名,我建立数据库名为minimalism_cms,而且选择字符集,字符集为utf8便可,点击建立:
在出现的新建数据库中,选择建立表:
输入表信息如如下:
以上全部所需的数据库表我将会导出sql文件,同窗们使用时在数据库导入便可,导入步骤以下:
在对应数据库中右键选择导入点击执行sql脚本便可。
导入完将会出现以下的数据库表:
以上数据库表考虑排错等操做并无过多约束。
权限管理首先须要有帐户,帐户属于什么角色,该角色又有什么权限,这是实现权限管理的思想。例若有个帐户名为admin,admin属于超级管理员这个角色,该角色拥有全部的权限。接下来首先建立管理员用户。
在权限管理下拉列表中选择管理员管理进入页面:
咱们查看url链接:http://localhost/index.php/admin/auth/adminauth.html
以上连接中,admin表示admin这个模块,auth表示控制器,adminauth表示方法名;auth控制器咱们还未建立,在admin模块下的index控制器同目录建立一个名为Auth.php文件,内容以下:
<?php /** * |----------------------- * | 页面跳转 * |----------------------- */ namespace app\admin\controller; use think\Controller; class Auth extends Controller{ //Auth 管理首页 public function adminAuth(){ return $this->view->fetch(); } }
经过以上控制器,可使url链接访问到该控制器而且访问adminAuth所对应的html文件,该html对应的文件在view目录下的auth目录中。在thinkphp中,对应的view目录根据控制器名分配,Auth控制器须要一个名为auth的目录存放该控制器下的html文件;在auth目录下建立一个名为admin_auth的html文件,为何要名为admin_auth?thinkphp会访问方法名默认控制器对应的目录中一同方法名的html文件,如方法名有大写,则表示在该名称前有一下划线,则adminAuth则为admin_auth。该html代码将会打包下载即用。
点击添加,添加管理员进入页面:
该url为:http://localhost/index.php/admin/auth/adminadd.html
在控制器中添加方法:
<?php /** * |----------------------- * | 页面跳转 * |----------------------- */ namespace app\admin\controller; use think\Controller; class Auth extends Controller{ //Auth 管理首页 public function adminAuth(){ return $this->view->fetch(); } //管理员添加页 public function adminAdd(){ return $this->view->fetch(); } }
该页面拥有管理员帐户、管理员密码、名称及角色组内容。暂时咱们并无角色组,首先建立一个管理员帐户。查看html中的关键代码:
<form class="form-horizontal" role="form"> <div class="form-group"> <label class="col-md-2 control-label">管理员帐户</label> <div class="col-md-10"> <input id="user" type="text" class="form-control" placeholder="账号"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">管理员密码</label> <div class="col-md-10"> <input id="password" type="password" class="form-control" placeholder="密码"> </div> </div> <div class="form-group"> <label class="col-md-2 control-label">名称</label> <div class="col-md-10"> <input id="realname" type="text" class="form-control" placeholder="输入名称或代号"> </div> </div> </form>
经过以上html得知,id为user是帐户,id为password为密码,id为realname为真实姓名。在此使用ajax进行数据提交到php后台实现内容访问。查看ajax代码:
<script> function add(){ var user=$('#user').val(); var password=$('#password').val(); var realname=$('#realname').val(); $.ajax({ type:'post', url:'/index.php?s=/admin/Authpost/adminAdd/', data:{ "user":user,"password":md5(password),"realname":realname,"group":group}, dataType:"json", success:function(data){ if(data.success==1){ alert(data.msg); }else{ alert(data.msg); } },error:function(jqXHR){ } }) } </script>
从以上ajax代码中,使用jq获取了id为user、password、realname元素的值,在此并无作检查是否合规,但愿小伙伴们在使用该代码的时候注意。在获取密码时使用了md5加密,md5我是在线引入的,引入以下:
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>
获取值后使用ajax传递给 /index.php?s=/admin/Authpost/adminAdd/ 这个url地址。该地址使用了兼容模式,由于担忧一些同窗本地环境有问题,因此特意在此使用该模式进行传值。该模式的格式为:http://serverName/index.php(或者其它应用入口文件)?s=/模块/控制器/操做/[参数名/参数值…]。则admin为模块名,Authpost表示控制器名,adminAdd表示控制器中的方法。咱们在admin的控制器目录建立一个名为Authpost的控制器,而且编写adminAdd方法,代码以下:
<?php /** * |----------------------- * | 对数据库操做 * |----------------------- */ namespace app\admin\controller; use think\Controller; use think\Db; use think\facade\Request; use app\admin\model\Admin; use app\admin\code\ReturnCodeInfo; class Authpost extends Controller{ //administartor add public function adminadd(){ $request_data = Request::post(); $data['password'] = md5(trim($request_data['password'])); $data['username']=$request_data['user']; $data['realname']=$request_data['realname']; $data['group']=$request_data['group']; $data['logintime'] = time(); $data['create_time'] = time(); $data['loginip'] = Request::ip(); $data['status'] = 1; $res = Admin::create($data); if($res){ return json((new ReturnCodeInfo())->actionSuccess()); }else{ return json((new ReturnCodeInfo())->actionError()); } } }
先不看以上代码,咱们查看须要存储值的数据库字段有哪些:
经过表得知,数据库字段包括 username、password、logintime、loginip、realname、create_time。咱们接收值须要设定这几个初始字段,Authpost 控制器adminadd方法中这部分代码为:
$request_data = Request::post(); $data['password'] = md5(trim($request_data['password'])); $data['username']=$request_data['user']; $data['realname']=$request_data['realname']; $data['group']=$request_data['group']; $data['create_time'] = time(); $data['loginip'] = Request::ip(); $data['status'] = 1;
以上代码使用了 Request::post();接收post值,在使用Request时必须引用use think\facade\Request;;随后将值赋给$request_data变量。随后使用 $data 变量存储即将要存储到数据库的值。在存储password密码时使用了md5加密,提升安全性。最后使用模型的create方法将数据库的值存储:
$res = Admin::create($data);
模型方法能够方便的使值进行存储。模型对应的是一个数据库,例如我数据库名为tp_admin,设置前缀为tp_后能够直接建立一个名为Admin的模型,其实也就是名为Admin的php文件,文件中类名也为Admin,该类集成model基类故此有模型特性。建立模型的方法以下,在admin下的controller同目录,注意是同目录建立一个model文件夹,在该文件夹下建立一个Admin的php文件,内容以下:
<?php namespace app\admin\model; use think\Model; class Admin extends Model { }
建立完成后,在须要使用到该模型的文件中引入,咱们在Authpost头部引入 ,添加代码:
use app\admin\model\Admin;
其实以上代码是经过模型所在目录进行引入,app表示根目录,根目录下的admin模块中model目录下的Admin模型。
在引入后还差很关键的一步,须要配置数据的链接。
在application目录下config文件夹中找到database.php文件,打开修改hostname为127.0.0.1或者是localhost、修改database为咱们建立的数据库的名称例如minimalism_cms、修改username账号为root、修改password密码为root。配置内容以下:
<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: liu21st <liu21st@gmail.com> // +---------------------------------------------------------------------- return [ // 数据库类型 'type' => 'mysql', // 服务器地址 'hostname' => '127.0.0.1', // 数据库名 'database' => 'minimalism_cms', // 用户名 'username' => 'root', // 密码 'password' => 'root', // 端口 'hostport' => '', // 链接dsn 'dsn' => '', // 数据库链接参数 'params' => [], // 数据库编码默认采用utf8 'charset' => 'utf8', // 数据库表前缀 'prefix' => 'tp_', // 数据库调试模式 'debug' => true, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 'deploy' => 0, // 数据库读写是否分离 主从式有效 'rw_separate' => false, // 读写分离后 主服务器数量 'master_num' => 1, // 指定从服务器序号 'slave_no' => '', // 自动读取主库数据 'read_master' => false, // 是否严格检查字段是否存在 'fields_strict' => true, // 数据集返回类型 'resultset_type' => 'array', // 自动写入时间戳字段 'auto_timestamp' => false, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 是否须要进行SQL性能分析 'sql_explain' => false, // Builder类 'builder' => '', // Query类 'query' => '\\think\\db\\Query', // 是否须要断线重连 'break_reconnect' => false, // 断线标识字符串 'break_match_str' => [], ];
修改完成后保存。
修改完成后查看 adminadd 方法中:
if($res){ return json((new ReturnCodeInfo())->actionSuccess()); }else{ return json((new ReturnCodeInfo())->actionError()); }
以上代码为返回通用的操做返回值码,减小代码冗余。使用 json返回json值,该值为new ReturnCodeInfo中的actionSuccess方法返回值,代码内容为:
<?php namespace app\admin\code; class ReturnCodeInfo{ //验证器成功代码 10001,错误 10002 private $validate_success=10001; private $validate_error=10002; private $validate_info='验证成功'; //数据库存 private $action_success=10003; private $action_error=10004; private $action_sinfo='操做成功'; private $action_einfo='操做失败'; //验证器 Code public function validataSuccess(){ return ['code'=>$this->validate_success,'msg'=>$this->validate_info]; } public function validataError($msg){ return ['code'=>$this->validate_error,'msg'=>$msg]; } //规则 Code public function actionSuccess(){ return ['code'=>$this->action_success,'msg'=>$this->action_sinfo]; } public function actionError(){ return ['code'=>$this->action_error,'msg'=>$this->action_einfo]; } }
以上类定义了操做失败或成功的返回状态,方便以后的操做调用该状态码。该php文件我写在controller同目录下的code目录中,名为ReturnCodeInfo的php文件。则在Authpost代码中使用了以下代码引入:
use app\admin\code\ReturnCodeInfo;
接着咱们返回到html中,在提交按钮上绑定onclick事件,固然你使用别的方式也行,代码以下:
<button type="button" onclick="add()" class="btn btn-success btn-bordered waves-effect w-md waves-light m-b-5">提交</button>
随后在页面中填入内容:
点击提交:
操做成功。
咱们按照如上方式建立角色组的建立,点击角色组管理:
角色组实现逻辑与管理员实现逻辑相似,再也不赘述。点击添加进入添加页:
填入组名后点击提交,操做成功:
该逻辑实现一致,均是建立模型后进行数据插入。
随后开始添加规则,进入规则添加页:
点击提交进行添加。
上述相同过程完成后开始实现权限认证的逻辑。
接下来彻底用户对角色组的绑定,进入管理员管理页,点击编辑:
进入编辑页后选择超级管理员:
点击提交,完成绑定:
提交方法使用ajax,所访问的接口为Authpost下的groupBindUser方法:
//组绑定用户 public function groupBindUser(){ $request_data = Request::post(); $data['uid']=$request_data['uid']; $data['group_id']=$request_data['gid']; $data['create_time']=$data['update_time']=time(); //存储 $res = AuthGroupAccess::create($data); if($res){ return json((new ReturnCodeInfo())->actionSuccess()); }else{ return json((new ReturnCodeInfo())->actionError()); } }
该方法主要使用AuthGroupAccess模型调用create方法进行数据插入。数据库存储以下:
为用户id对应的组id。
随后进入组页面,进行组绑定规则的操做,点击编辑进入超级管理员编辑页:
选择须要的规则,点击提交完成规则与组的绑定:
数据库存储以下:
以上表中,id为组id,rules则为规则的id。
为了使验证层可以灵活的使用,在admin目录下建立一个AuthRuleValidate目录,新建一php文件名为AuthRuleValidateBase,内容以下:
<?php namespace app\admin\AuthRuleValidate; use think\Controller; use think\Db; class AuthRuleValidateBase extends Controller{ //传入uid 与当前 路由验证是否有此权限 public function check($uid,$access){ $res=Db::table('tp_admin') ->alias('a') ->field('rules') ->join('tp_auth_group_access agc','a.id = '.$uid) ->join('tp_auth_group ag','ag.id = agc.group_id') ->find(); $rules=Db::name('auth_rule')->field('rule')->where('id','in',$res['rules'])->select(); $rules=array_column($rules, 'rule'); in_array($access,$rules)?:$this->error('权限不足'); } }
逻辑很简单,该方法接受当前的uid用于查询用户所属组,改组拥有的规则,再经过规则与当前规则进行匹配,如含有则表示拥有该权限。
首先查询tp_admin管理员表所在的组:
$res=Db::table('tp_admin') ->alias('a') ->field('rules') ->join('tp_auth_group_access agc','a.id = '.$uid) ->join('tp_auth_group ag','ag.id = agc.group_id') ->find();
获得结果后,查询与改组id匹配的规则,最后判断该权限是否在当前的规则内,是的话不作任何操做,不然提示权限不足。
随后在controller控制器目录下建立一基类php文件,名为Base。内容为:
<?php namespace app\admin\controller; use think\Controller; use think\facade\Session; use app\admin\AuthRuleValidate\AuthRuleValidateBase; class Base extends Controller{ protected $beforeActionList = [ 'ruleCheck'=>['except' => 'login'] ]; protected function ruleCheck() { session('?admin')?:$this->error('未登陆或已失效','Index/login'); $AuthRuleValidate=new AuthRuleValidateBase(); $s=session('admin'); /*echo request()->module().'/'.request()->controller().'/'.request()->action(); */ $AuthRuleValidate->check($s['id'],strtolower(request()->controller()).'/'.strtolower(request()->action())); } }
该文件引入了刚刚建立的权限判断类,在此基础上而且判断了该用户是否登陆。
查看代码:
protected $beforeActionList = [ 'ruleCheck'=>['except' => 'login'] ];
该代码为设置前置曹祖,其中except表示除什么方法以外,在这里设置除login登陆方法外,由于全部用户都必须登陆后才能判断权限,登陆方法则不受此影响。随后查看ruleCheck方法,该方法首先判断用户是否登陆:
session('?admin')?:$this->error('未登陆或已失效','Index/login');
随后新建权限判断类:
$AuthRuleValidate=new AuthRuleValidateBase();
接着使用seesion获取uid:
$s=session('admin');
最后调用权限判断方法传入当前控制器方法与uid进行权限判断:
$AuthRuleValidate->check($s['id'],strtolower(request()->controller()).'/'.strtolower(request()->action()));
彻底权限判断基类后,使全部管理后台的控制器继承与该方法,例Auth控制器(该操做能够等待登陆页编写后再进行):
class Auth extends Base{
在admin模块中,index控制器添加方法login,内容为:
public function login(){ return $this->view->fetch(); }
前端代码使用ajax传值,前端页显示以下:
随后填入账号及密码经过ajax传值到admin模块下的Authpost控制器中login方法中,内容以下:
//登陆 public function login(){ $request_data = Request::post(); $data['username']=$request_data['user']; $data['password']=md5(trim($request_data['password'])); $res=db('admin')->where($data)->find(); $res?session('admin', $res):$this->error('账号或密码错误'); }
使用find方法对传入值进行对比,密码正确则将值传入到seesion不然将提示账号密码错误。
在基本权限实现完成后,使用验证器对传入值进行判断,毕竟外部值都是不可靠的值。
在controller同级下建立一目录validate,建立目录后在该目录下建立一php文件名为BaseValidate做为对数据进行判断类的基类,代码内容以下:
<?php namespace app\admin\validate; use think\Validate; use think\Controller; use app\admin\code\ReturnCodeInfo; class BaseValidate extends Validate{ public function gocheck($validata){ if(!$this->check($validata)){ return (new ReturnCodeInfo())->validataError($this->getError()); } return (new ReturnCodeInfo())->validataSuccess(); } }
该类继承验证器类,具备验证器特性。验证器的使用查看tp5.1文档。
查看gocheck方法,gocheck方法调用了验证器自己的check方法,其接收的参数$validata为须要验证的数据。check判断错误则调用 ReturnCodeInfo类中的报错数据返回,不然则返回正确。
假设在管理员添加时须要验证数据是否合规,那么在validate目录中建立一名为AdminValidate的php文件,内容为:
<?php namespace app\admin\validate; use app\admin\validate\BaseValidate; class AdminValidate extends BaseValidate{ protected $rule = [ 'password' => 'require|max:50', 'username' => 'require|max:30', 'realname' => 'require|max:30', 'group' => 'require|max:30' ]; }
在管理员添加方法中(Authpost控制器中的adminadd方法)添加:
//验证器 $valires=(new AdminValidate())->gocheck($data); if ($valires['code']==10002){ return json($valires); }
便可完成,但必定要注意,须要引入该验证器:
use app\admin\validate\AdminValidate;
咱们首先实现查看轮播图区域元素:
发现元素包含轮播图标题、简介,以及轮播图标题一、简介1以及背景图。数据库设计以下:
咱们经过sqlyog的可视化操做添加轮播图所须要资源的数据,能够经过邮件检查直接获取资源路径及内容:
首先获得轮播图第一张图片的数据:
复制内容填入sqlyog表中:
同理获取全部的内容填入至表:
全部内容填入数据库:
回到index模块下的index控制器中,在index方法中添加获取轮播图数据表中数据:
<?php namespace app\index\controller; use think\Controller; use think\Db; class Index extends Controller{ public function index() { $banner_res=Db::table('tp_home_banner') ->order('id', 'desc') ->limit(4) ->select(); print_r($banner_res); die; return $this->view->fetch(); } }
在以上代码中,使用select方法查询轮播图数据表中的数据,查询方式是id的降序,这样使轮播图将会以最新添加的做为显示依据,而且每次只查询前4条;查询结构复制给变量banner_res,使用print_r对该变量进行输出,随后在输出模板前使用die终止,查看输出。
访问localhost成功得到数据:
在index方法中添加代码,像前端传递banner_res变量,而且删除die代码:
<?php namespace app\index\controller; use think\Controller; use think\Db; class Index extends Controller{ public function index() { $banner_res=Db::table('tp_home_banner') ->order('id', 'desc') ->limit(4) ->select(); $this->view->assign('banner',$banner_res); return $this->view->fetch(); } }
接下来咱们将在html代码中使用tp的前端模板语法对一些html元素进行控制。咱们经过元素查询得知轮播图元素id为homev1:
在代码中找到id为homev1的元素,查看代码,每一个轮播图标签相似,只有默认选项多了个class修饰:
可是有些小伙伴以为很麻烦,那咱们换一种方式,使用tp框架前端的模板语法,相似if判断,从而输出内容:
首先使用volist标签进行循环,在标签中设置循环变量key,该key循环第一次的值为1,当为1使用eq标签判断,是1则输出第一个轮播图的html代码:
{ eq name="k" value="1"}
须要输出的html代码须要使用成对的eq标签包含,结束的eq标签为 {/eq}。
代码以下:
<div class="carousel-inner" role="listbox"> <!-- Third Slide --> {volist name="banner" id="vo" key="k" } {eq name="k" value="1"} <div class="item active"> <!-- Slide Background --> <img src="{$vo.img}" alt="SeoPress Slider" /> <div class="bs-slider-overlay"></div> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <div class="slide-text slide_style_center"> <h1 class="text-white" data-animation="animated zoomInRight">{$vo.title}</h1> <p class="text-white m-top-10" data-animation="animated fadeInLeft">{$vo.content}</p> <a class="btn btn-primary btn-round m-top-30" data-animation="animated fadeInLeft" href="" target="_blank">Read More</a> <a class="btn btn-default btn-round m-top-30" data-animation="animated fadeInRight" href="" target="_blank">Read More</a> </div> </div> </div> </div> </div> {/eq} <!-- End of Slide --> {eq name="k" value="2"} <!-- Second Slide --> <div class="item"> <img src="{$vo.img}" alt="SeoPress Slider" /> <div class="bs-slider-overlay"></div> <div class="container"> <div class="row"> <!-- Slide Text Layer --> <div class="col-md-6"> <div class="slide-text slide_style_left"> <h1 class="text-white" data-animation="animated fadeInRight">{$vo.title}</h1> <p class="text-white m-top-10" data-animation="animated zoomInLeft">{$vo.content} </p> <a class="btn btn-default btn-round m-top-30" data-animation="animated fadeInRight" href="" target="_blank">Read More</a> <a class="btn btn-primary btn-round m-top-30" data-animation="animated fadeInLeft" href="" target="_blank">Read More</a> </div> </div> </div> </div> </div> <!-- End of Slide --> {/eq} {eq name="k" value="3"} <!-- Third Slide --> <div class="item"> <img src="{$vo.img}" alt="SeoPress Slider" /> <div class="bs-slider-overlay"></div> <div class="container"> <div class="row"> <!-- Slide Text Layer --> <div class="col-md-6"> <div class="slide-text slide_style_left"> <h1 class="text-white" data-animation="animated fadeInDown">{$vo.title}</h1> <p class="text-white m-top-10" data-animation="animated fadeInLeft">{$vo.content} </p> <a class="btn btn-primary btn-round m-top-30" data-animation="animated fadeInLeft" href="" target="_blank">Read More</a> <a class="btn btn-default btn-round m-top-30" data-animation="animated fadeInRight" href="" target="_blank">Read More</a> </div> </div> </div> </div> </div> {/eq} {eq name="k" value="4"} <!-- Fourth Slide --> <div class="item"> <img src="{$vo.img}" alt="SeoPress Slider" /> <div class="bs-slider-overlay"></div> <div class="container"> <div class="row"> <!-- Slide Text Layer --> <div class="col-md-6"> <div class="slide-text slide_style_left"> <h1 class="text-white" data-animation="animated fadeInLeft">{$vo.title} <br /> Online Marketing Needs</h1> <p class="text-white m-top-10" data-animation="animated fadeInRight">{$vo.content} </p> <a class="btn btn-primary btn-round m-top-30" data-animation="animated fadeInLeft" href="" target="_blank">Read More</a> <a class="btn btn-default btn-round m-top-30" data-animation="animated fadeInRight" href="" target="_blank">Read More</a> </div> </div> </div> </div> </div> {/eq} {/volist} <!-- End of Slide --> </div><!-- End of Wrapper For Slides -->
接着咱们往下查看首页内容:
我的以为该区域能够放一个“有利于”之类的宣传语,那么建一表存放标题、图片、内容信息:
在该表中填入网页中原有的数据:
在index控制器中添加查询tp_home_advantageous表数据的代码并将结果传至前端:
<?php namespace app\index\controller; use think\Controller; use think\Db; class Index extends Controller{ public function index() { $banner_res=Db::table('tp_home_banner') ->order('id', 'desc') ->limit(4) ->select(); $advantageous_res=Db::table('tp_home_advantageous') ->order('id', 'desc') ->limit(6) ->select(); $this->view->assign('advantageous',$advantageous_res); $this->view->assign('banner',$banner_res); return $this->view->fetch(); } }
修改前端代码,发现该区域代码的html几乎一致,前3个的class=“service-item sm-m-top-65”,后3个的class=“service-item m-top-65”:
<div class="main-service-area text-center m-top-80"> <div class="col-md-4 col-sm-6"> <div class="service-item sm-m-top-65"> <div class="service-icon"> <img src="/home/assets/images/service1.png" alt="" /> </div> <h5 class="text-info m-top-50">Search Engine Optimization</h5> <p class="text-black m-top-20">With our 17+ years of experience, our SEO services will get your site ranking.</p> </div> </div> <div class="col-md-4 col-sm-6"> <div class="service-item sm-m-top-65"> <div class="service-icon"> <img src="/home/assets/images/service3.png" alt="" /> </div> <h5 class="text-info m-top-50">Content Marketing</h5> <p class="text-black m-top-20">From blogs and social posts to infographics videos we create and promote quality.</p> </div> </div> <div class="col-md-4 col-sm-6"> <div class="service-item sm-m-top-65"> <div class="service-icon"> <img src="/home/assets/images/service2.png" alt="" /> </div> <h5 class="text-info m-top-50">Social Media Marketing</h5> <p class="text-black m-top-20">Boost brand awareness and reach your customers on a human level.</p> </div> </div> <div class="col-md-4 col-sm-6"> <div class="service-item m-top-65"> <div class="service-icon"> <img src="/home/assets/images/service4.png" alt="" /> </div> <h5 class="text-info m-top-50">Web Design & Development</h5> <p class="text-black m-top-20">Our designers and developers will create an attractive, SEO-friendly & fully functional.</p> </div> </div> <div class="col-md-4 col-sm-6"> <div class="service-item m-top-65"> <div class="service-icon"> <img src="/home/assets/images/service5.png" alt="" /> </div> <h5 class="text-info m-top-50">eCommerce Solutions</h5> <p class="text-black m-top-20">With our 17+ years of experience, our SEO services will get your site ranking.</p> </div> </div> <div class="col-md-4 col-sm-6"> <div class="service-item m-top-65"> <div class="service-icon"> <img src="/home/assets/images/service6.png" alt="" /> </div> <h5 class="text-info m-top-50">Inbound Marketing</h5> <p class="text-black m-top-20">With our ecommerce solutions, you'll provide an enjoyable, seamless.</p> </div> </div> </div>
这是把其它div删除,留下1个div,使用volist标签进行遍历输出值,而且设置循环变量key,使用tp框架的前端判断标签,判断小于4时输出class为col-sm-6:
{lt name="k" value="4"}col-sm-6{/eq}
当循环后3三位,则是k值大于3,大于3输出col-sm-6,使用gt标签:
{gt name="k" value="3"}col-sm-6{/eq}
将两个前端代码编写与div中,完整代码以下:
<div class="main-service-area text-center m-top-80"> {volist name="advantageous" id="vo" key="k" } <div class="col-md-4 col-sm-6"> <div class='service-item {lt name="k" value="4"}col-sm-6{/eq} {gt name="k" value="3"}col-sm-6{/eq}'> <div class="service-icon"> <img src="{$vo.img}" alt="" /> </div> <h5 class="text-info m-top-50">{$vo.title}</h5> <p class="text-black m-top-20">{$vo.content}</p> </div> </div> {/volist} </div>
运行结果:
接着往下,查看页面区域:
咱们将该页面编写成产品展现区域。新建一数据库表:
填入内容:
在index控制器index方法中添加product数据库查询代码并传至前端:
<?php namespace app\index\controller; use think\Controller; use think\Db; class Index extends Controller{ public function index() { $banner_res=Db::table('tp_home_banner') ->order('id', 'desc') ->limit(4) ->select(); $advantageous_res=Db::table('tp_home_advantageous') ->order('id', 'desc') ->limit(6) ->select(); $product_res=Db::table('tp_home_product') ->order('id', 'desc') ->limit(2) ->select(); $this->view->assign('product',$product_res); $this->view->assign('advantageous',$advantageous_res); $this->view->assign('banner',$banner_res); return $this->view->fetch(); } }
随后在html代码中输出内容便可:
<section id="leading" class="leading bg-primary sections2"> <div class="container"> <div class="row"> <div class="main-leading"> <div class="col-md-6"> <div class="leading-content"> <div class="head-title"> <h2 class="text-white">{$product[0]['title']}</h2> <p class="m-top-30 text-white">{$product[0]['specs']}</p> <div class="separator2 hv2"><span></span><span></span><span></span></div> </div> <p class="m-top-40">{$product[0]['content']}</p> <a href="" class="btn btn-default btn-round m-top-20">Our Team</a> </div> </div> <div class="col-md-5"> <div class="leading-img sm-m-top-50"> <img src="{$product[0]['img']}" alt="" /> </div> </div> </div> </div><!-- End off row--> </div><!-- End off container --> </section><!-- End off leading section--> <!--Allies Section--> <section id="allies" class="allies sections"> <div class="container"> <div class="row"> <div class="main-allies"> <div class="col-md-5"> <div class="allies-img"> <img src="{$product[1]['img']}" alt="" /> </div> </div> <div class="col-md-7"> <div class="allies-content sm-m-top-50"> <div class="head-title"> <h2 class="text-black">O{$product[1]['title']}</h2> <h5 class="text-black m-top-30">{$product[1]['content']}</h5> <div class="separator2"><span></span><span></span><span></span></div> </div> <p class="m-top-40">{$product[1]['specs']}</p> <a href="" class="btn btn-primary btn-round m-top-30">Portfolio</a> </div> </div> </div> </div><!-- End off row --> </div><!--End off container --> </section><!-- End off Allies Section-->
接着往下看:
该区域能够更改为文章的展现,建立已数据库表:
添加内容:
查看html代码:
<div class="col-md-4 col-sm-6"> <div class="studies-item"> <div class="studies-feature border"> <img class="img-rounded" src="/home/assets/images/studies-img-01.jpg" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> <div class="custom-hover"></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">Acme Corporation</a></h4> <p class="m-top-10">Objective: Build a larger twitter community</p> </div> </div> </div> <div class="col-md-4 col-sm-6"> <div class="studies-item xs-m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="/home/assets/images/studies-img-02.jpg" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">Soylent Corp </a></h4> <p class="m-top-10">Objective: Make tone & branding consistency</p> </div> </div> </div> <div class="col-md-4 col-sm-6"> <div class="studies-item sm-m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="/home/assets/images/studies-img-03.jpg" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">Umbrella Corporation</a></h4> <p class="m-top-10">Objective: Eliminate the residue of black-hat methods</p> </div> </div> </div> <div class="col-md-4 col-sm-6"> <div class="studies-item m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="/home/assets/images/studies-img-04.jpg" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">Initech</a></h4> <p class="m-top-10">Objective: Improve site load speed & functionality</p> </div> </div> </div> <div class="col-md-4 col-sm-6"> <div class="studies-item m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="/home/assets/images/studies-img-05.jpg" alt="" /> <div class="studies-overlay img-rounded"> <a href=""> <span class="icon icon-arrows-2 hvr-hang"></span> </a> </div> </div> <div class="studies-conten m-top-30"> <h4><a href="">Vehement Capital Partners </a></h4> <p class="m-top-10">Objective: Increase nationwide sales</p> </div> </div> </div> <div class="col-md-4 col-sm-6"> <div class="studies-item m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="/home/assets/images/studies-img-06.jpg" alt="" /> <div class="studies-overlay img-rounded"> <a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a> </div> </div> <div class="studies-conten m-top-30"> <h4><a href="">Massive Dynamic</a></h4> <p class="m-top-10">Objective: Increase qualified traffic</p> </div> </div> </div>
发现该html代码中前3个div的class有所变化,后3个div布局内容则无变化。咱们使用eq标签使前3个div照原样输出,后3个div遍历输出:
{volist name="article" id="vo" key="k" } {eq name="k" value="1"} <div class="col-md-4 col-sm-6"> <div class="studies-item"> <div class="studies-feature border"> <img class="img-rounded" src="{$vo.img}" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> <div class="custom-hover"></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">{$vo.title}</a></h4> <p class="m-top-10">{$vo.content}</p> </div> </div> </div> {/eq} {eq name="k" value="2"} <div class="col-md-4 col-sm-6"> <div class="studies-item xs-m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="{$vo.img}" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">{$vo.title}</a></h4> <p class="m-top-10">{$vo.content}</p> </div> </div> </div> {/eq} {eq name="k" value="3"} <div class="col-md-4 col-sm-6"> <div class="studies-item sm-m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="{$vo.img}" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">{$vo.title}</a></h4> <p class="m-top-10">{$vo.content}</p> </div> </div> </div> {/eq} {gt name="k" value="3"} <div class="col-md-4 col-sm-6"> <div class="studies-item m-top-35"> <div class="studies-feature border"> <img class="img-rounded" src="{$vo.img}" alt="" /> <div class="studies-overlay img-rounded"><a href=""><span class="icon icon-arrows-2 hvr-hang"></span></a></div> </div> <div class="studies-conten m-top-30"> <h4><a href="">{$vo.title}</a></h4> <p class="m-top-10">{$vo.content}</p> </div> </div> </div> {/gt} {/volist}
在index控制器的首页方法index中添加对article表数据的查询:
<?php namespace app\index\controller; use think\Controller; use think\Db; class Index extends Controller{ public function index() { $banner_res=Db::table('tp_home_banner') ->order('id', 'desc') ->limit(4) ->select(); $advantageous_res=Db::table('tp_home_advantageous') ->order('id', 'desc') ->limit(6) ->select(); $product_res=Db::table('tp_home_product') ->order('id', 'desc') ->limit(2) ->select(); $article_res=Db::table('tp_home_article') ->order('id', 'desc') ->limit(6) ->select(); $this->view->assign('article',$article_res); $this->view->assign('product',$product_res); $this->view->assign('advantageous',$advantageous_res); $this->view->assign('banner',$banner_res); return $this->view->fetch(); } }
其它的前端内容经过数据库更改再也不赘述。
建立控制器Contentmanger,添加方法bannerManger,bannerManger方法跳转到一页面用于显示banner数据,点击每条数据可进行编辑:
因为有数据的查询,在控制器中须要查询banner表数据,代码为:
<?php /** * |----------------------- * | 页面跳转 * |----------------------- */ namespace app\admin\controller; use think\Controller; use think\Db; use think\facade\Request; class Contentmanger extends Base{ //官网首页内容管理 public function bannerManger(){ $list=Db::table('tp_home_banner') ->order('id') ->select(); $this->view->assign('list',$list); return $this->view->fetch(); } }
此处可添加验证器检测传入值是否正确,为了节省篇幅接下来的代码中再也不过多的添加其它内容。
html代码前端的编辑修改按钮,使用了url方法传参,传参后获取该id的内容,方便进行修改:
点击编辑后将会能够随意修改banner的值:
点击choosefile可选择img文件,修改banner图片。
建立一控制器用来管理内容修改操做的逻辑,建立一php文件名为 Contentmangerpost ,添加 bannerEdit 方法:
<?php /** * |----------------------- * | 页面跳转 * |----------------------- */ namespace app\admin\controller; use think\Controller; use think\Db; use think\facade\Request; use \think\File; use app\admin\model\Goods; use app\admin\validate\IdValidate; use app\admin\code\ReturnCodeInfo; class Contentmangerpost extends Base{ //轮播图编辑 public function bannerEdit(){ $save_path='/public'; $save_path_='/uploads/imgs/'; $request_data = Request::post(); $con['id']=$request_data['id']; $data['title']=$request_data['title']; $data['title_2']=$request_data['title_2']; $data['content']=$request_data['content']; $data['content_2']=$request_data['content_2']; // 获取表单上传文件 例如上传了001.jpg $file = request()->file('file'); if($file){ // 移动到框架应用根目录/uploads/ 目录下 $info = $file->move('..'.$save_path.$save_path_); if($info){ // 成功上传后 获取上传信息 $data['img']=$save_path_.str_replace('\\','/',$info->getSaveName()); }else{ // 上传失败获取错误信息 echo $file->getError(); } }else{ $data['img']='/home/assets/images/slide-bg-01.jpg'; } $res=Db::name('home_banner') ->where($con) ->update($data); if($res){ return json((new ReturnCodeInfo())->actionSuccess()); }else{ return json((new ReturnCodeInfo())->actionError()); } } }
以上代码使用request()->file(‘file’);判断是否接收到file值,如接收到说明用户选择了新图片,那么使用move方法保存图片,经过getSaveName方法获取保存图片名。最终更新至数据库中。其它数据的更新方法与该步骤相似,再也不赘述。接下来经过拖拽实现web而且绑定数据。
拖拽页面在此提供一个思想,经过bootstrap的layoutit可视化布局能够完成简单页面拖拽生成,须要完成更多复杂的界面须要对layoutit进行二次开发。本篇内容为一个demo,经过可视化layoutit生成界面且进行代码替换完成对于thinkphp模板的制做,最后经过可视化数据绑定生成php代码。
首先为layoutit添加一个控制器并更改资源路径,此操做再也不赘述。部署完成后打开界面:
可拖拽布局实现界面编辑:
拖拽成以下界面:
点击下载可查看生成的html代码:
在点击下载时,我经过js保存了生成的代码:
var template=''; $("[data-target=#downloadModal]").click(function(e) { e.preventDefault(); downloadLayoutSrc(); }); function downloadLayoutSrc() { var e = ""; $("#download-layout").children().html($(".demo").html()); var t = $("#download-layout").children(); t.find(".preview, .configuration, .drag, .remove").remove(); t.find(".lyrow").addClass("removeClean"); t.find(".box-element").addClass("removeClean"); t.find(".lyrow .lyrow .lyrow .lyrow .lyrow .removeClean").each(function() { cleanHtml(this) }); t.find(".lyrow .lyrow .lyrow .lyrow .removeClean").each(function() { cleanHtml(this) }); t.find(".lyrow .lyrow .lyrow .removeClean").each(function() { cleanHtml(this) }); t.find(".lyrow .lyrow .removeClean").each(function() { cleanHtml(this) }); t.find(".lyrow .removeClean").each(function() { cleanHtml(this) }); t.find(".removeClean").each(function() { cleanHtml(this) }); t.find(".removeClean").remove(); $("#download-layout .column").removeClass("ui-sortable"); $("#download-layout .row-fluid").removeClass("clearfix").children().removeClass("column"); if ($("#download-layout .container").length > 0) { changeStructure("row-fluid", "row") } formatSrc = $.htmlClean($("#download-layout").html(), { format: true, allowedAttributes: [ ["id"], ["class"], ["data-toggle"], ["data-target"], ["data-parent"], ["role"], ["data-dismiss"], ["aria-labelledby"], ["aria-hidden"], ["data-slide-to"], ["data-slide"] ] }); $("#download-layout").html(formatSrc); $("#downloadModal textarea").empty(); $("#downloadModal textarea").val(formatSrc) console.log(formatSrc); template=formatSrc; }
此代码为layoutit的js代码,在此基础上我新建了已js全局变量保存数据,变量为template,在js代码清洗完成后把清洗后的值赋值给全局变量template。
随后点击保存:
<button class="btn btn-primary" onclick="bc()">保存</button>
保存按钮点击后对应的js代码为:
function bc(){ {literal} template='{$head|raw}'+'<body style="min-height: 816px; cursor: auto;" class="devpreview sourcepreview">'+template+'</body>'; {/literal} $.ajax({ type:'post', url:'/index.php?s=/admin/Autoviewpost/test/', data:{"template":template}, dataType:"json", success:function(data){ },error:function(jqXHR){ } }) }
在由于生成的代码须要必定的js文件引入,在此我添加了{$head|raw}为前端的模板代码,使用了{literal} 标签对thinkphp的模板代码进行修饰,表示不解析其中内容。head变量的内容为:
$head='<link href="/autoview/css/bootstrap-combined.min.css" rel="stylesheet"> <link href="/autoview/css/layoutit.css" rel="stylesheet"> <!-- Le styles --> <link href="/autoview/css/bootstrap-combined.min.css" rel="stylesheet"> <link href="/autoview/css/layoutit.css" rel="stylesheet"> <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> <!--[if lt IE 9]> <script src="/autoview/js/html5shiv.js"></script> <![endif]--> <!-- Fav and touch icons --> <link rel="shortcut icon" href="/autoview/img/favicon.png"> <script type="text/javascript" src="/autoview/js/jquery-2.0.0.min.js"></script> <!--[if lt IE 9]> <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> <![endif]--> <script type="text/javascript" src="/autoview/js/bootstrap.min.js"></script> <script type="text/javascript" src="/autoview/js/jquery-ui.js"></script> <script type="text/javascript" src="/autoview/js/jquery.ui.touch-punch.min.js"></script> <script type="text/javascript" src="/autoview/js/jquery.htmlClean.js"></script> <script type="text/javascript" src="/autoview/ckeditor/ckeditor.js"></script> <script type="text/javascript" src="/autoview/ckeditor/config.js"></script> <script type="text/javascript" src="/autoview/js/scripts.js"></script>';
点击保存后,生成的html代码文本将会传到Autoviewpost控制器下的test方法中,test方法代码以下:
public function test(){ $request_data = Request::post(); $template=$request_data['template']; /* $regex4="/<div class=\"media\".*?>.*?<\/div>.*?<\/div>/ism"; */ $template=preg_replace(getMediaReStr(),getMediaHtmlStr(),$template);//media 替换 $template=preg_replace(getCarouselReStr(),getCarouselHtmlStr(),$template);//轮播图 替换 $template=preg_replace(getThumbnailsReStr(),getThumbnailsHtmlStr(),$template);//缩略图 替换 $template=preg_replace(getUnitReStr(),getUnitHtmlStr(),$template);//概述 替换 $template=str_replace("{eq name="key" value="1"}active{/eq}",'{eq name="key" value="1"}active{/eq}',$template); file_put_contents(dirname(dirname(__FILE__)).'\view\templates\t1.html',$template); /* print_r($request_data['template']); */ }
该方法在接收值后对一部分进行替换。使用preg_replace对文本进行替换,在该对比中我使用了正则对数据进行匹配,该方法我编写在common公共函数的php文件中,地址为application\common.php,内容为:
<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: 流年 <liu21st@gmail.com> // +---------------------------------------------------------------------- // 应用公共文件 function arrunset(&$arr){ array_splice($arr,0,1); } //Media php code function getMediaHtmlStr(){ $str='{volist name="media" id="data" }'. '<div class="media">'. '<a href="{$data.src}" class="pull-left"><img src="{$data.img}" class="media-object" alt=\'\' /></a>'. '<div class="media-body">'. '<h4 class="media-heading">'. '{$data.title}'. '</h4> {$data.content}'. '</div>'. '</div>'. '{:arrunset($media)}'. '{/volist}'; return $str; } //Media regex str function getMediaReStr(){ $re="/<div class=\"media\".*?>.*?<\/div>.*?<\/div>/ism"; return $re; } //轮播图 php code function getCarouselHtmlStr(){ $str='<div class="carousel slide" id="carousel-998124"><div class="carousel-inner"> '. '{volist name="banner" id="data"}'. '<div class=\'item {eq name="key" value="1"}active{/eq} \'>'. '<img alt="" src="{$data.img}" />'. '<div class="carousel-caption">'. '<h4>'. '{$data.title}'. '</h4>'. '<p>'. '{$data.content}'. '</p>'. '</div>'. '</div>'. '{/volist} '. '</div> '. '<a data-slide="prev" href="#carousel-998124" class="left carousel-control">‹</a><a data-slide="next" href="#carousel-998124" class="right carousel-control">›</a></div>'; return $str; } //轮播图 regex str function getCarouselReStr(){ $re="/<div class=\"carousel slide\".*?>.*?class=\"right carousel-control\">.*?<\/div>/ism"; return $re; } //缩略图 php code function getThumbnailsHtmlStr(){ $str='<ul class="thumbnails">'. '{volist name="article" id="data"}'. '<li class="span4">'. '<div class="thumbnail"> <img alt="300x200" src="{$data.faceimg}">'. '<div class="caption" contenteditable="true">'. '<h3>{$data.title}</h3>'. '<p>{$data.content}</p>'. '<p><a class="btn btn-primary" href="#">浏览</a> <a class="btn" href="#">分享</a></p>'. '</div>'. '</div>'. '</li>'. '{/volist}'. '</ul>'; return $str; } //缩略图 regex str function getThumbnailsReStr(){ $re="/<ul class=\"thumbnails\".*?>.*?<\/ul>/ism"; return $re; } //概述 php code function getUnitHtmlStr(){ $str='{volist name="ad" id="data" offset="0" length=\'1\'}'. '<div class="hero-unit" contenteditable="true">'. '<h1>{$data.title}</h1>'. '<p>{$data.content} </p>'. '<p><a class="btn btn-primary btn-large" href="#">参看更多 »</a></p>'. '</div>'. '{:arrunset($ad)}'. '{/volist}' ; return $str; } //概述 regex str function getUnitReStr(){ $re="/<div class=\"hero-unit\".*?>.*?<\/div>/ism"; return $re; }
使用不一样的方法返回不一样组件、html代码的正则匹配,替换成所需的带有thinkphp框架语法的html代码,这些代码一样在common文件中。完成替换后因为发现某些字符须要进行替换,编写代码:
$template=str_replace("{eq name="key" value="1"}active{/eq}",'{eq name="key" value="1"}active{/eq}',$template);
完成清洗替换后生成html模板生成危机:
file_put_contents(dirname(dirname(__FILE__)).'\view\templates\t1.html',$template);
因为是demo,因此位置写死了。
随后访问Autoview控制器下的createcontrol方法(页面没写):
输入你想要生成的控制器名、方法名,该方法须要绑定数据表中哪些元素,以及绑定的页面路径:
输入完成后点击提交,数据将会传到Autoview控制器中的buildcontrol方法中,方法代码以下:
//控制器生成 方法名及数据库 public function buildcontrol(){ $request_data = Request::post(); $data['controll'] = $request_data['controll']; $data['function']=$request_data['function']; $data['datas']=$request_data['datas']; $data['templatepath']=$request_data['templatepath']; $controlcode='<?php namespace app\admin\controller; use think\Controller; use think\Db; class '.$data['controll'].' extends AutoviewBase{ public function '.$data['function'].'(){ return $this->view->fetch(dirname(dirname(__FILE__)).'.$data['templatepath'].'); } }'; file_put_contents(dirname(dirname(__FILE__)).'\controller\\'.$data['controll'].'.php',$controlcode); $res = Url_datas::create($data); if($res){ return json((new ReturnCodeInfo())->actionSuccess()); }else{ return json((new ReturnCodeInfo())->actionError()); } }
该方法controlcode变量为控制器模板变量,该模板文本能够得知该控制器名称为自定义名称,继承于AutoviewBase基类,方法名也是自定义,模板位置根据指定路径进行输出渲染。最后使用 file_put_contents 进行控制器生成。最后将数据存入到Url_datas模型中,也是Url_datas表中,数据表结构数据以下:
咱们从控制器生成路径中能够得知,是admin内的控制器,咱们访问生成的控制器方法查看效果:
数据页面获得显示,这些数据都是数据库中的数据。在建立控制器时,咱们在指定数据表及字段时使用的格式内容为以下:
{ "banner":"id,title,img,content", "article":"id,title,content,faceimg", "media":"id,src,img,title,content", "ad":"id,title,content,img" }
数据指定格式为“数据表”:“字段1,字段2…”,经过在AutoviewBase的前置方法中对该json数据进行解析,AutoviewBase基类以下:
<?php /** * |----------------------- * | 前端页面自定义 * |----------------------- */ namespace app\admin\controller; use think\Controller; use think\Db; class AutoviewBase extends Controller{ protected $beforeActionList = [ 'toview' ]; public function toview(){ $head='<link href="/autoview/css/bootstrap-combined.min.css" rel="stylesheet"> <link href="/autoview/css/layoutit.css" rel="stylesheet"> <!-- Le styles --> <link href="/autoview/css/bootstrap-combined.min.css" rel="stylesheet"> <link href="/autoview/css/layoutit.css" rel="stylesheet"> <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> <!--[if lt IE 9]> <script src="/autoview/js/html5shiv.js"></script> <![endif]--> <!-- Fav and touch icons --> <link rel="shortcut icon" href="/autoview/img/favicon.png"> <script type="text/javascript" src="/autoview/js/jquery-2.0.0.min.js"></script> <!--[if lt IE 9]> <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> <![endif]--> <script type="text/javascript" src="/autoview/js/bootstrap.min.js"></script> <script type="text/javascript" src="/autoview/js/jquery-ui.js"></script> <script type="text/javascript" src="/autoview/js/jquery.ui.touch-punch.min.js"></script> <script type="text/javascript" src="/autoview/js/jquery.htmlClean.js"></script> <script type="text/javascript" src="/autoview/ckeditor/ckeditor.js"></script> <script type="text/javascript" src="/autoview/ckeditor/config.js"></script> <script type="text/javascript" src="/autoview/js/scripts.js"></script>'; $con['controll']=strtolower(request()->controller()); $con['function']=strtolower(request()->action()); //by controll and action select rules $data_rules=Db::name('url_datas') ->where($con) ->order('id', 'desc') ->field('datas') ->find(); //get datas $datas=[]; $tables = json_decode($data_rules['datas'], true); foreach($tables as $key => $value){ $datas[$key]=Db::name($key) ->column($value); } //输出到前端 foreach($datas as $key => $value){ $this->view->assign($key,$value); } $this->view->assign('head',$head); return $this->view->fetch(dirname(dirname(__FILE__)).'\view\templates\t1.html'); } }
以上代码中定义了前置操做toview方法,在toview方法中定义了head为头部资源文件,以后使用以下代码获取当前控制器及方法名:
$con['controll']=strtolower(request()->controller()); $con['function']=strtolower(request()->action());
把控制器及方法名看成条件至url_datas数据表中查询所需的数据要求及格式:
//by controll and action select rules $data_rules=Db::name('url_datas') ->where($con) ->order('id', 'desc') ->field('datas') ->find();
获得了所需数据后,对该数据进行json解析,解析后遍历该数据做为对指定表与数据的查询:
$datas=[]; $tables = json_decode($data_rules['datas'], true); foreach($tables as $key => $value){ $datas[$key]=Db::name($key) ->column($value); }
以后使用遍历把获得的数据结果输出到前端:
//输出到前端 foreach($datas as $key => $value){ $this->view->assign($key,$value); }
最后把head传递值前端代码,渲染输出:
$this->view->assign('head',$head); return $this->view->fetch(dirname(dirname(__FILE__)).'\view\templates\t1.html');
以上内容准备过于匆忙,只讲解了实现中较为重要的地方,不少优化及细节没有说明,但愿下次将会编写一份彻底的教程给你们!若有错误欢迎指出,想要深刻学习能够关注博主,点赞博主、收藏博文,谢谢~
原创做品@CSDN 1_bit https://blog.csdn.net/A757291228