學習目標
- 了解搭建一般網站的簡便方式
- 了解最原始一般站點搭建
- 了解內容管理站點搭建
- 了解權限設計及完成
- 了解使用設計模式減少代碼冗余
- 了解前端拖拽頁面生成及生成
- 了解自定義數據的創建
- 了解動態生成的前端頁如何綁定自定義數據
開發環境
- Windows7 *64 SP1
- php5.6
- apache/nginx
- thinkphp5.1
- mysql
- phpstudy2018
- sqlyog
- layoutit
聲明
文章為從0到1了解內容管理系統搭建與編寫,由于一篇文章內容篇幅過長,文章內容經過壓縮,該項目中相同邏輯的實現只以一個實例作為描述,主要以核心關鍵功能的開發作為主要的講解步驟。如有想學習完整內容系統編寫可在留言區留言,我會盡快完成完整版的實戰教程發布。謝謝。本篇不涉及vue、nodejs的前端框架。
知識門檻
以下內容有過一些了解即可:
- html
- sql
- php
- tp框架
面向人群
- 剛學了php不懂怎么用的同學
- 會一點點建站但是又不清楚流程的同學
- 學習完了一些框架不懂怎么使用的同學
- 有過一些web開發經驗的同學等
- 希望本篇文章對每一個閱讀完的同學都有幫助
注意:本篇文章部分細節由于篇幅關系并不會去深入完善,并且相同邏輯的實現只以一個實例作為描述,主要以核心功能的開發作為主要的講解步驟。本篇不涉及vue、nodejs的前端框架。
一、 了解一些專業術語及概念
在了解搭建網站前,需要普及一些基本的知識概念,防止某些同學在一方面有概念性的錯誤,并且我個人認為在學習一方面知識前需要對這一方面的知識有一個廣度的了解,這里所指的廣度為這東西是用來干什么的、作用是什么、為什么要這樣寫;所以在正式開始介紹如何編寫CMS前將會介紹這一部分內容。為了方便閱讀第一點內容引入我另外一篇原創文章。
1.1了解瀏覽一個網頁的基本流程方式
在學習一門技術的時候,往往是了解整體體系架構才能更好的學習,不然在學習的過程中會出現不知道為什么這樣做,做出這一部分是該整體部分的哪個區域,只會跟著做,但是并不了解這是在干啥。可能一些萌新體會頗深,就照著打,老師教怎么寫,我就怎么寫,反正做出來了。
本篇博文,就來用最接地氣的方式對基本的web開發做一個整體的講解,帶各個萌新過一遍web開發的流程,好讓各位萌新知道學習的時候學習了什么知識點,這個知識點能夠干哈。
最開始,我們就以個人瀏覽網站的方式給大家說一下這一個過程是如何運作的。
我們訪問網站,一般先打開瀏覽器(不要杠),輸入一個網址,隨后瀏覽器打開一個網頁。在你在請求這一個網址數據的時候,已經發生了一系列的操作。
1.2了解IP地址
假設你輸入的是“csdn.net”,瀏覽器想要去訪問你這個網站,首先需要的是獲得你這個網站的IP地址。可能就有萌新問了**“什么是IP地址?”。IP地址就是“指互聯網協議地址,或者說網際協議地址”。又有萌新說了“你這么說我怎么懂?”**,好了現在容我慢慢道來。
IP地址就是在網絡中,定位你這臺電腦,或者說是設備的一個標記,這個標記是人們指定好的標準協議而產生的(協議就是你和我說好了一件事,拉鉤了,以后要這樣做)。就像你家的門牌號例如叫做“CSDN市,CSDN區,CSDN街道的CSDN小區第CSDN棟的第CSDN號”…這是由有關機構制定的一套規范名稱,不允許隨意更改;我們換個例子,例如你家是“深圳市南山區深南大道某某小區第八棟808”,你寫快遞的收件地址肯定是寫這個,難道你寫“宇宙第一星球第一市第一棟第一號”?地址是由專門組織規范且制定的一套定位規范,遵循這個規范可以使遵循該規范的設備或者人之間相互通信,這個通信指可以傳達交互,能夠定位、找到。綜上所述,IP地址就不要糾結為什么要這樣寫,只要知道這個IP地址是你要用的就行。
1.3了解DNS
現在IP地址知道是什么了,那么怎么獲得IP地址?這個時候就需要用到DNS了,啥是DNS??!!
DNS的英文全稱是 Domain Name System,翻譯過來就是域名系統。好了,這個時候問題又來了。
1.4了解域名
啥是域名?域名就是用來標識IP地址的一個標記,或者說是昵稱。“為什么不直接用IP地址?”這個問題問得好,如果我們人不用名稱,就用身份證號,我叫你的時候就會叫“450333333333333333…”。。。我覺得這樣不是很好。。。當人們覺得使用IP地址不方便記憶后,就產生了域名地址,就像CSDN,我們就知道是CSDN就好了,難道還要去記她的IP地址嗎?例如CSDN的地址是192.168.1.1,難不難受…以后可能你記網站名稱就在記數字了,又不方便又崩潰。好了,回歸正題,我們輸入了網址后,按下Enter鍵后,瀏覽器將會去DNS請求這個域名對應的IP是什么,如果找到了,就返回一個IP地址。可能又有萌新問了,“瀏覽器會自動去找DNS?”,會是會,但是我們也會給它一個目標,在我們的網絡連接里面,本地連接右鍵屬性,里面有個IPV4,雙擊進去就可以查看自己配置的DNS了,一般別亂改,不然很難過的,有時候瀏覽器打不開網址,就是這個原因。
記住,網絡IP沖突可能會導致上不了網,這種情況在學校的機房里很常見,只要改成自動獲取IP就ok了,會自動分配閑置的IP地址。
1.5 了解數據請求
當找到了IP地址,這個時候就會向該IP地址的設備去請求數據,請求數據的意思就是,這個設備或者說服務器就像一個大型的分發機構,就是送情報的一個部門,一共有65535個窗口,每個窗口送不同的情報;例如我們需要請求網站之類的數據,就通過第80個窗口請求,這個時候瀏覽器派來的小弟來到這個80號窗口,可能會排一下隊,拿到數據后,回到瀏覽器,瀏覽器把拿到的數據顯示給你看。
1.6 了解“ 渲染”
其實在這個時候,瀏覽器顯示的數據會根據一些標記,進行排版,這些標記就稱是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了。其實還有些動態的數據,但是在這里并不會講解,基本的理解這樣就沒問題了。專業點的說法就是構件編排用戶界面。
1.7 了解前端
通過以上描述就很清楚的知道,如果我們做web開發的話,做html相關的就是給頁面制作布局,怎么樣好看,甚至可以做特效,讓頁面顯示多姿多彩;一般我們稱做HTML這種,是為了數據的顯示的排版工作,或者說是為了包裝數據工作的這類職位叫做前端;不過前端是個相對概念,在web上可以這樣理解是沒問題的,不過現在的前端,如果不去大廠,基本上要做的不止是包裝數據的排版那么簡單,可能還會做得更多。如果我們去做前端工作的話,還要掌握跟服務器交互的一些操作,打個比方,用戶點擊了一個按鈕,這個按鈕的功能是獲取到你們的用戶人數,這個時候你需要編寫一個邏輯,去服務器獲取到這個用戶想要的數據。不過這點只是作為一個提醒,當真正接觸前端的話會了解的。
1.8 了解后端
有很多小問號的朋友可能會記得剛剛說的,前端可能要向服務器請求數據,那么這個數據,是不是就是傳說中的后端做的?(聽沒聽過后端某問題,反正就是后端)
后端可以理解為一些業務邏輯的代碼編寫實現,就是需要后端,什么是業務邏輯?簡單的舉個例子,就像你淘寶買東西,你點了這個物品,下單了,我要在代碼上怎么實現這個下單這個背后的操作;因為下單后你還需要交易,交易要收錢,收錢你還要把這個記錄記載到你存放數據的地方,我們可以叫做數據庫,存進去后,用戶查看自己的下單記錄,你還需要把這個記錄取出來,用代碼實現這個取出來這個過程給用戶看到,不然沒有記錄那就很尷尬了,只收錢不賣貨!流批!所以一般是指的是數據庫(因為要存儲數據,例如你網站的用戶數據,肯定要用東西來存儲,這個東西就是數據庫)進行交互以處理相應的業務邏輯。雖然后端要考慮很多東西,但是一般來說這樣舉例子就比較方便理解,就不過多的談論其它東西了。
現在整個邏輯基本上就通了,簡單的理解,后端就是實現一些數據操作,業務邏輯的實現(其實可能會運維),前端呢就是負責用戶的頁面數據的展示排版;嗯,大體這樣理解問題不大。
1.9 了解建站
既然理解通了,我們就來說說一個網站搭建的流程是什么吧!
首先我們需要租一個服務器,嗯…這個萌新不理解,那我們降一個檔次,那就是我們在我們自己的本地電腦進行試驗,這樣就問題不大了,方便快捷。
搭建一個簡單企業門戶網站其實賊簡單,不吹不黑,幾年前的時候,做這個還是挺得錢的,接接外包,舒舒服服,現在就不行了,畢竟技術在更新,過時的技術也變得更加廉價了,但是依舊是基本。
以下我使用一個靜態網站作為例子演示一個網站的搭建;“啥是靜態網站?”。靜態網站就是沒有后端,好吧,簡單來說就是這樣,由于后端需要一些其它語言,本篇博文針對于普遍人群,為了方便理解就不用后端了,直接靜態網站作為演示,列出html的代碼,到時候萌新們可以直接復制代碼拿去自己試驗,舒舒服服,美滋滋。
1.10 了解集成環境
首先我們下載一個集成環境。“啥是集成環境?”。
集成環境打個比方,就像你做菜、需要火源、鍋、鍋鏟,這種就是環境;我做網站也要一個環境,這個環境有人給你做好了,你直接拿過來用就好,就不需要自己搭建,有些初學者就喜歡自己搭建,然后發現一堆問題,搞著搞著發現太難就不學了,簡直嚶嚶嚶!初學者我個人建議先別增加自己的難度,先學,不然沒搞懂就上會一臉懵圈的。現在我們下載一個叫做phpstudy的軟件,下載點這里
去官網。然后進行傻瓜式安裝。
安裝完后打開服務:
Apache可能會有人問是什么,Apache是服務器軟件,它就是你做菜需要的必要工具之一,開啟了就對了,可能你只開啟Apache只能做湯,那也沒事,畢竟我現在演示的是靜態網站。
首先我們把我們的資源文件帶到網站根目錄下:
根目錄不會找?沒關系,我們打開網站,點擊管理找到根目錄就ok:
找到后把資源文件放到根目錄下,刪除以前的根目錄下的內容即可。
然后在瀏覽器輸入:http://127.0.0.1/ 或者輸入 http://localhost/ 就可以訪問我們本地電腦上的網站了!
二、給所搭建的靜態網站添加后端
在以上第一節內容中,我們已經做好了一個靜態的網站,但該網站并沒有一些后臺功能。例如后臺設置網頁的所展示的內容,那為什么要后臺設置網頁展示的內容呢?當我們的網站成功架設后,假設該網站是雙十一的推廣網站,圖片這些全部都是標有雙十一字樣,當雙十一過后該網站難道就不能繼續使用了嗎?答案當然是不,只需要編寫一管理后臺,用戶在后臺中可自由設置圖片要顯示哪一張。該功能完成后,用戶可根據自己的需要更改對應的圖片;既然圖片都可以更改了,那么文章也同樣可以更改,這時網站的自由度將會更高。
更改網站圖片的顯示與更改文字內容的顯示都需要使用數據庫,當然其它方式也可以,但我們在這里使用一種較為常規與成熟的數據庫方式進行存儲,并且使用一個php的開發框架thinkphp來方便我們的搭建。thinkphp的版本是5.1版本。可能有些小伙伴們問為什么要使用框架?這不是增加學習成本嗎?其實使用框架并不會增加你的開發時長,并且會增加你的開發效率;框架就像搭建房子時的地基,直接使用一個地基比你自己再去做一個地基更加簡單方便,而且更為標準;如果你是一個新手,自己去搭建一個地基,往往會做到一半就“塌”了,這種情況也不是不可能。
2.1 了解thinkphp5.1 的使用
首先我們下載thinkphp5.1,解壓后目錄如下:
目錄參考可以根據thinkpp5.1手冊:
thinkphp5.1的目錄結構在本文并不需要了解過多,本文將會說明需要了解的目錄。
我們復制解壓出來的文件至網站根目錄下,并且刪除原有網站根目錄下的內容:
由于thinkphp框架的入口在public目錄下,我們打開public目錄進行查看:
在public目錄下找到了index.php文件。由于該框架的入口文件是index.php,需要更改網站的根目錄為public。打開phpstudy,依次點擊其它菜單選項->軟件設置->端口常規設置:
在彈出來的根目錄設置中,選擇public作為根目錄:
此時輸入localhost進行訪問:
出現如上示例則表示當前thinkphp部署成功。接下來就可以進行相應的代碼編寫了。
2.2 完成第一節靜態網站的移植部署
在第一節中,我們實現了一個靜態網站的搭建,現在將第一節編寫好的靜態網站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 即可調出一鍵替換:
點擊如上圖中的一鍵替換即可完成資源內容的目錄修改,隨后保存,再次訪問:
完美呈現,是不是賊爽?那么接下來就實現這些圖片資源的可后臺更換。
三、完成后臺模塊的編寫
3.1 完成管理后臺模塊搭建
首先復制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類型,均是修改頁面的資源路徑即可),訪問效果如下:
注:本節項目代碼將會打包分享給大家。
3.2 完成數據庫的導入
完成后臺管理頁的搭建后,發現該后臺所有用戶均可訪問,這對于一個網站是不好的權限行為;必須實現可控的權限管理,使得網站內容不得隨意更改。
首先打開sqlyog,輸入數據庫的帳號密碼,一般帳號為root密碼為root或空:
連接成功后,郵件你本地數據庫點擊創建數據:
輸入數據庫名,我創建數據庫名為minimalism_cms,并且選擇字符集,字符集為utf8即可,點擊創建:
在出現的新建數據庫中,選擇創建表:
輸入表信息如以下:
以上所有所需的數據庫表我將會導出sql文件,同學們使用時在數據庫導入即可,導入步驟如下:
在對應數據庫中右鍵選擇導入點擊執行sql腳本即可。
導入完將會出現如下的數據庫表:
以上數據庫表考慮排錯等操作并沒有過多約束。
3.3 完成權限內容添加功能編寫
權限管理首先需要有賬戶,賬戶屬于什么角色,該角色又有什么權限,這是實現權限管理的思想。例如有個賬戶名為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,錯誤 10002private $validate_success=10001;private $validate_error=10002;private $validate_info='驗證成功';//數據庫存private $action_success=10003;private $action_error=10004;private $action_sinfo='操作成功';private $action_einfo='操作失敗';//驗證器 Codepublic function validataSuccess(){return ['code'=>$this->validate_success,'msg'=>$this->validate_info];}public function validataError($msg){return ['code'=>$this->validate_error,'msg'=>$msg];}//規則 Codepublic 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。
3.4 完成權限管理邏輯編寫
為了使驗證層能夠靈活的使用,在admin目錄下創建一個AuthRuleValidate目錄,新建一php文件名為AuthRuleValidateBase,內容如下:
<?phpnamespace 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{
3.5 完成登錄功能編寫
在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否則將提示帳號密碼錯誤。
3.6 完成傳入值的判斷
在基本權限實現完成后,使用驗證器對傳入值進行判斷,畢竟外部值都是不可靠的值。
在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;
四、完成內容管理功能的編寫
4.1 完成管理后臺模塊搭建
我們首先實現查看輪播圖區域元素:
發現元素包含輪播圖標題、簡介,以及輪播圖標題1、簡介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();}}
其它的前端內容通過數據庫更改不再贅述。
4.2 完成通過后臺設置更改與添加前端內容
創建控制器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代碼。
5.1 完成拖拽界面的前端搭建
首先為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='<?phpnamespace 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