天天看點

一篇文帶你從0到1了解建站及完成CMS系統編寫

學習目标

  1. 了解搭建一般網站的簡便方式
  2. 了解最原始一般站點搭建
  3. 了解内容管理站點搭建
  4. 了解權限設計及完成
  5. 了解使用設計模式減少代碼備援
  6. 了解前端拖拽頁面生成及生成
  7. 了解自定義資料的建立
  8. 了解動态生成的前端頁如何綁定自定義資料

開發環境

  • 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開發的流程,好讓各位萌新知道學習的時候學習了什麼知識點,這個知識點能夠幹哈。

最開始,我們就以個人浏覽網站的方式給大家說一下這一個過程是如何運作的。

一篇文帶你從0到1了解建站及完成CMS系統編寫

我們通路網站,一般先打開浏覽器(不要杠),輸入一個網址,随後浏覽器打開一個網頁。在你在請求這一個網址資料的時候,已經發生了一系列的操作。

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??!!​

一篇文帶你從0到1了解建站及完成CMS系統編寫

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了,一般别亂改,不然很難過的,有時候浏覽器打不開網址,就是這個原因。

一篇文帶你從0到1了解建站及完成CMS系統編寫

記住,網絡IP沖突可能會導緻上不了網,這種情況在學校的機房裡很常見,隻要改成自動擷取IP就ok了,會自動配置設定閑置的IP位址。

1.5 了解資料請求

一篇文帶你從0到1了解建站及完成CMS系統編寫

當找到了IP位址,這個時候就會向該IP位址的裝置去請求資料,請求資料的意思就是,這個裝置或者說伺服器就像一個大型的分發機構,就是送情報的一個部門,一共有65535個視窗,每個視窗送不同的情報;例如我們需要請求網站之類的資料,就通過第80個視窗請求,這個時候浏覽器派來的小弟來到這個80号視窗,可能會排一下隊,拿到資料後,回到浏覽器,浏覽器把拿到的資料顯示給你看。

1.6 了解“ 渲染”

一篇文帶你從0到1了解建站及完成CMS系統編寫

其實在這個時候,浏覽器顯示的資料會根據一些标記,進行排版,這些标記就稱是HTML,HTML是 Hyper Text Markup Language 的縮寫,中文名是超級文本标記語言,其實說那麼深奧還不友善了解;簡單來說就是通過特定的标簽,把一段文本資訊标記起來,表示這段文本資訊要怎麼樣去進行顯示,或者是這個文本資訊是啥東西;例如 ​

​<title>CSDN-專業IT技術社群</title>​

​是CSDN官網首頁的标題,用了title這個标簽把文本資訊标記,标記好後,浏覽器就知道這個文本要顯示在哪裡,要怎麼進行顯示,最終浏覽器把這一段資訊顯示在了浏覽器标題頭位置:

一篇文帶你從0到1了解建站及完成CMS系統編寫

我們再看看另外的一個例子:

一篇文帶你從0到1了解建站及完成CMS系統編寫

這一段HTML語言所标記了一個部落格的文本,整個标記的情況為了清晰的看清楚,我在這裡列出:​

​<a href="//blog.csdn.net/" class="toolbar_to_feed" title="部落格">部落格</a>​

​,标記語言HTML那一些标記并不會進行顯示,隻顯示了部落格這個這個文本在網頁上:

一篇文帶你從0到1了解建站及完成CMS系統編寫

那是因為浏覽器是通過标記語言的内容去進行顯示,标記語言的作用就是告訴浏覽器這裡你要怎麼顯示這個内容,或者說這個内容有什麼功能。這裡是部落格的一個跳轉,使用的是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 了解前端

一篇文帶你從0到1了解建站及完成CMS系統編寫

通過以上描述就很清楚的知道,如果我們做web開發的話,做html相關的就是給頁面制作布局,怎麼樣好看,甚至可以做特效,讓頁面顯示多姿多彩;一般我們稱做HTML這種,是為了資料的顯示的排版工作,或者說是為了包裝資料工作的這類職位叫做前端;不過前端是個相對概念,在web上可以這樣了解是沒問題的,不過現在的前端,如果不去大廠,基本上要做的不止是包裝資料的排版那麼簡單,可能還會做得更多。如果我們去做前端工作的話,還要掌握跟伺服器互動的一些操作,打個比方,使用者點選了一個按鈕,這個按鈕的功能是擷取到你們的使用者人數,這個時候你需要編寫一個邏輯,去伺服器擷取到這個使用者想要的資料。不過這點隻是作為一個提醒,當真正接觸前端的話會了解的。

1.8 了解後端

一篇文帶你從0到1了解建站及完成CMS系統編寫

有很多小問号的朋友可能會記得剛剛說的,前端可能要向伺服器請求資料,那麼這個資料,是不是就是傳說中的後端做的?(聽沒聽過後端某問題,反正就是後端)

後端可以了解為一些業務邏輯的代碼編寫實作,就是需要後端,什麼是業務邏輯?簡單的舉個例子,就像你淘寶買東西,你點了這個物品,下單了,我要在代碼上怎麼實作這個下單這個背後的操作;因為下單後你還需要交易,交易要收錢,收錢你還要把這個記錄記載到你存放資料的地方,我們可以叫做資料庫,存進去後,使用者檢視自己的下單記錄,你還需要把這個記錄取出來,用代碼實作這個取出來這個過程給使用者看到,不然沒有記錄那就很尴尬了,隻收錢不賣貨!流批!是以一般是指的是資料庫(因為要存儲資料,例如你網站的使用者資料,肯定要用東西來存儲,這個東西就是資料庫)進行互動以處理相應的業務邏輯。雖然後端要考慮很多東西,但是一般來說這樣舉例子就比較友善了解,就不過多的談論其它東西了。

現在整個邏輯基本上就通了,簡單的了解,後端就是實作一些資料操作,業務邏輯的實作(其實可能會運維),前端呢就是負責使用者的頁面資料的展示排版;嗯,大體這樣了解問題不大。

1.9 了解建站

一篇文帶你從0到1了解建站及完成CMS系統編寫

既然了解通了,我們就來說說一個網站搭建的流程是什麼吧!

首先我們需要租一個伺服器,嗯…這個萌新不了解,那我們降一個檔次,那就是我們在我們自己的本地電腦進行試驗,這樣就問題不大了,友善快捷。

搭建一個簡單企業門戶網站其實賊簡單,不吹不黑,幾年前的時候,做這個還是挺得錢的,接接外包,舒舒服服,現在就不行了,畢竟技術在更新,過時的技術也變得更加廉價了,但是依舊是基本。

以下我使用一個靜态網站作為例子示範一個網站的搭建;“啥是靜态網站?”。靜态網站就是沒有後端,好吧,簡單來說就是這樣,由于後端需要一些其它語言,本篇博文針對于普遍人群,為了友善了解就不用後端了,直接靜态網站作為示範,列出html的代碼,到時候萌新們可以直接複制代碼拿去自己試驗,舒舒服服,美滋滋。

1.10 了解內建環境

一篇文帶你從0到1了解建站及完成CMS系統編寫

首先我們下載下傳一個內建環境。​“啥是內建環境?”。

內建環境打個比方,就像你做菜、需要火源、鍋、鍋鏟,這種就是環境;我做網站也要一個環境,這個環境有人給你做好了,你直接拿過來用就好,就不需要自己搭建,有些初學者就喜歡自己搭建,然後發現一堆問題,搞着搞着發現太難就不學了,簡直嘤嘤嘤!初學者我個人建議先别增加自己的難度,先學,不然沒搞懂就上會一臉懵圈的。現在我們下載下傳一個叫做phpstudy的軟體,下載下傳​​點這裡​​

去官網。然後進行傻瓜式安裝。

一篇文帶你從0到1了解建站及完成CMS系統編寫

安裝完後打開服務:

一篇文帶你從0到1了解建站及完成CMS系統編寫

Apache可能會有人問是什麼,Apache是伺服器軟體,它就是你做菜需要的必要工具之一,開啟了就對了,可能你隻開啟Apache隻能做湯,那也沒事,畢竟我現在示範的是靜态網站。

首先我們把我們的資源檔案帶到網站根目錄下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

根目錄不會找?沒關系,我們打開網站,點選管理找到根目錄就ok:

一篇文帶你從0到1了解建站及完成CMS系統編寫

找到後把資源檔案放到根目錄下,删除以前的根目錄下的内容即可。

然後在浏覽器輸入:​​​​​​ 或者輸入 ​​​​​​ 就可以通路我們本地電腦上的網站了!

二、給所搭建的靜态網站添加後端

在以上第一節内容中,我們已經做好了一個靜态的網站,但該網站并沒有一些背景功能。例如背景設定網頁的所展示的内容,那為什麼要背景設定網頁展示的内容呢?當我們的網站成功架設後,假設該網站是雙十一的推廣網站,圖檔這些全部都是标有雙十一字樣,當雙十一過後該網站難道就不能繼續使用了嗎?答案當然是不,隻需要編寫一管理背景,使用者在背景中可自由設定圖檔要顯示哪一張。該功能完成後,使用者可根據自己的需要更改對應的圖檔;既然圖檔都可以更改了,那麼文章也同樣可以更改,這時網站的自由度将會更高。

更改網站圖檔的顯示與更改文字内容的顯示都需要使用資料庫,當然其它方式也可以,但我們在這裡使用一種較為正常與成熟的資料庫方式進行存儲,并且使用一個php的開發架構thinkphp來友善我們的搭建。thinkphp的版本是5.1版本。可能有些小夥伴們問為什麼要使用架構?這不是增加學習成本嗎?其實使用架構并不會增加你的開發時長,并且會增加你的開發效率;架構就像搭建房子時的地基,直接使用一個地基比你自己再去做一個地基更加簡單友善,而且更為标準;如果你是一個新手,自己去搭建一個地基,往往會做到一半就“塌”了,這種情況也不是不可能。

2.1 了解thinkphp5.1 的使用

首先我們下載下傳thinkphp5.1,解壓後目錄如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

目錄參考可以根據​​thinkpp5.1手冊​​:

一篇文帶你從0到1了解建站及完成CMS系統編寫

thinkphp5.1的目錄結構在本文并不需要了解過多,本文将會說明需要了解的目錄。

我們複制解壓出來的檔案至網站根目錄下,并且删除原有網站根目錄下的内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

由于thinkphp架構的入口在public目錄下,我們打開public目錄進行檢視:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在public目錄下找到了index.php檔案。由于該架構的入口檔案是index.php,需要更改網站的根目錄為public。打開phpstudy,依次點選其它菜單選項->軟體設定->端口正常設定:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在彈出來的根目錄設定中,選擇public作為根目錄:

一篇文帶你從0到1了解建站及完成CMS系統編寫

此時輸入localhost進行通路:

一篇文帶你從0到1了解建站及完成CMS系統編寫

出現如上示例則表示目前thinkphp部署成功。接下來就可以進行相應的代碼編寫了。

2.2 完成第一節靜态網站的移植部署

在第一節中,我們實作了一個靜态網站的搭建,現在将第一節編寫好的靜态網站index.html檔案複制到如下路徑中:

一篇文帶你從0到1了解建站及完成CMS系統編寫

我的目錄是 E:\devlop\phpstuy\PHPTutorial\WWW\application\index\view\index,如果沒有該目錄可以自己建立。我們浏覽器再次輸入localhost檢視,發現依舊出現之前的web頁提示,這是什麼回事呢?因為我們需要在thinkphp的控制器中,添加一行跳轉到該html檔案的代碼。控制器檔案在 E:\devlop\phpstuy\PHPTutorial\WWW\application\index\controller 下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

該目錄是存放目前子產品下所有控制器的地方(當然你可以不這樣),控制器在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手冊:

一篇文帶你從0到1了解建站及完成CMS系統編寫

使用 fetch 方法将會自動定位到模闆檔案。thinkphp已經幫我們寫好了一定的規則,自動定位到預設view目錄下對應控制器名下的index檔案。在此注意,是自動定位到view目錄下與控制器同名的目錄下的檔案,不加參數自動定位到index.html,也就是view/控制器名/index.html,由于控制器名是index,則是view/index/index.html;view目錄下的index目錄則是之前複制靜态網站html檔案的目錄。

儲存php檔案,通路localhost:

一篇文帶你從0到1了解建站及完成CMS系統編寫

這時發現整個web頁錯亂,這時因為所有css檔案、js檔案、img檔案的路徑都有所改變,這時需要更改到正确的資源加載目錄。為了友善加載,在網站根目錄public目錄下建立一個home目錄,複制該頁面所需的資源檔案到該目錄下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

網站根目錄資源的通路路徑是“/”表示網站根目錄下,由于在根目錄下建立了一個home目錄,則進一步可以寫為“/home/”,在home目錄下有一個asset,則可以寫為“/home/assets/”,assets下的檔案通路則可以根據目錄進行具體通路,例如asset下的目錄img有一個圖檔叫做1.png,那麼通路則可以寫成“/home/assets/img/1.png”。

了解了通路的規則後,修改index.html檔案,将所有 assets/ 都更換為 /home/assets/,我使用的編輯器是 vscode,快捷鍵 ctrl+h 即可調出一鍵替換:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選如上圖中的一鍵替換即可完成資源内容的目錄修改,随後儲存,再次通路:

一篇文帶你從0到1了解建站及完成CMS系統編寫

完美呈現,是不是賊爽?那麼接下來就實作這些圖檔資源的可背景更換。

三、完成背景子產品的編寫

3.1 完成管理背景子產品搭建

首先複制application目錄下的index目錄:

一篇文帶你從0到1了解建站及完成CMS系統編寫

更改index-副本名為admin:

一篇文帶你從0到1了解建站及完成CMS系統編寫

随後更改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類型,均是修改頁面的資源路徑即可),通路效果如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

注:本節項目代碼将會打包分享給大家。

3.2 完成資料庫的導入

完成背景管理頁的搭建後,發現該背景所有使用者均可通路,這對于一個網站是不好的權限行為;必須實作可控的權限管理,使得網站内容不得随意更改。

首先打開sqlyog,輸入資料庫的帳号密碼,一般帳号為root密碼為root或空:

一篇文帶你從0到1了解建站及完成CMS系統編寫

連接配接成功後,郵件你本地資料庫點選建立資料:

一篇文帶你從0到1了解建站及完成CMS系統編寫

輸入資料庫名,我建立資料庫名為minimalism_cms,并且選擇字元集,字元集為utf8即可,點選建立:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在出現的建立資料庫中,選擇建立表:

一篇文帶你從0到1了解建站及完成CMS系統編寫

輸入表資訊如以下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

以上所有所需的資料庫表我将會導出sql檔案,同學們使用時在資料庫導入即可,導入步驟如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在對應資料庫中右鍵選擇導入點選執行sql腳本即可。

導入完将會出現如下的資料庫表:

一篇文帶你從0到1了解建站及完成CMS系統編寫

以上資料庫表考慮排錯等操作并沒有過多限制。

3.3 完成權限内容添加功能編寫

權限管理首先需要有賬戶,賬戶屬于什麼角色,該角色又有什麼權限,這是實作權限管理的思想。例如有個賬戶名為admin,admin屬于超級管理者這個角色,該角色擁有所有的權限。接下來首先建立管理者使用者。

在權限管理下拉清單中選擇管理者管理進入頁面:

一篇文帶你從0到1了解建站及完成CMS系統編寫

我們檢視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代碼将會打包下載下傳即用。

點選添加,添加管理者進入頁面:

一篇文帶你從0到1了解建站及完成CMS系統編寫

該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());
        }
    }
    }      

先不看以上代碼,我們檢視需要存儲值的資料庫字段有哪些:

一篇文帶你從0到1了解建站及完成CMS系統編寫

通過表得知,資料庫字段包括 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 <[email protected]>
// +----------------------------------------------------------------------

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>      

随後在頁面中填入内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選送出:

一篇文帶你從0到1了解建站及完成CMS系統編寫

操作成功。

我們按照如上方式建立角色組的建立,點選角色組管理:

一篇文帶你從0到1了解建站及完成CMS系統編寫

角色組實作邏輯與管理者實作邏輯類似,不再贅述。點選添加進入添加頁:

一篇文帶你從0到1了解建站及完成CMS系統編寫

填入組名後點選送出,操作成功:

一篇文帶你從0到1了解建站及完成CMS系統編寫

該邏輯實作一緻,均是建立模型後進行資料插入。

随後開始添加規則,進入規則添加頁:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選送出進行添加。

上述相同過程完成後開始實作權限認證的邏輯。

接下來完全使用者對角色組的綁定,進入管理者管理頁,點選編輯:

一篇文帶你從0到1了解建站及完成CMS系統編寫

進入編輯頁後選擇超級管理者:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選送出,完成綁定:

一篇文帶你從0到1了解建站及完成CMS系統編寫

送出方法使用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方法進行資料插入。資料庫存儲如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

為使用者id對應的組id。

随後進入組頁面,進行組綁定規則的操作,點選編輯進入超級管理者編輯頁:

一篇文帶你從0到1了解建站及完成CMS系統編寫

選擇需要的規則,點選送出完成規則與組的綁定:

一篇文帶你從0到1了解建站及完成CMS系統編寫

資料庫存儲如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

以上表中,id為組id,rules則為規則的id。

3.4 完成權限管理邏輯編寫

為了使驗證層能夠靈活的使用,在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{      

3.5 完成登入功能編寫

在admin子產品中,index控制器添加方法login,内容為:

public function login(){
        return $this->view->fetch();
    }      

前端代碼使用ajax傳值,前端頁顯示如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

随後填入帳号及密碼通過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 完成管理背景子產品搭建

我們首先實作檢視輪播圖區域元素:

一篇文帶你從0到1了解建站及完成CMS系統編寫

發現元素包含輪播圖示題、簡介,以及輪播圖示題1、簡介1以及背景圖。資料庫設計如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫

我們通過sqlyog的可視化操作添加輪播圖所需要資源的資料,可以通過郵件檢查直接擷取資源路徑及内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

首先得到輪播圖第一張圖檔的資料:

一篇文帶你從0到1了解建站及完成CMS系統編寫

複制内容填入sqlyog表中:

一篇文帶你從0到1了解建站及完成CMS系統編寫

同理擷取所有的内容填入至表:

一篇文帶你從0到1了解建站及完成CMS系統編寫

所有内容填入資料庫:

一篇文帶你從0到1了解建站及完成CMS系統編寫

回到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成功獲得資料:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在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:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在代碼中找到id為homev1的元素,檢視代碼,每個輪播圖示簽類似,隻有預設選項多了個class修飾:

一篇文帶你從0到1了解建站及完成CMS系統編寫

但是有些小夥伴覺得很麻煩,那我們換一種方式,使用tp架構前端的模闆文法,類似if判斷,進而輸出内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

首先使用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 -->      

接着我們往下檢視首頁内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

個人覺得該區域可以放一個“有利于”之類的宣傳語,那麼建一表存放标題、圖檔、内容資訊:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在該表中填入網頁中原有的資料:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在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>      

運作結果:

一篇文帶你從0到1了解建站及完成CMS系統編寫

接着往下,檢視頁面區域:

一篇文帶你從0到1了解建站及完成CMS系統編寫

我們将該頁面編寫成産品展示區域。建立一資料庫表:

一篇文帶你從0到1了解建站及完成CMS系統編寫

填入内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在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-->      

接着往下看:

一篇文帶你從0到1了解建站及完成CMS系統編寫

該區域可以更改成文章的展示,建立已資料庫表:

一篇文帶你從0到1了解建站及完成CMS系統編寫

添加内容:

一篇文帶你從0到1了解建站及完成CMS系統編寫

檢視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資料,點選每條資料可進行編輯:

一篇文帶你從0到1了解建站及完成CMS系統編寫

由于有資料的查詢,在控制器中需要查詢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的内容,友善進行修改:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選編輯後将會可以随意修改banner的值:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選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添加一個控制器并更改資源路徑,此操作不再贅述。部署完成後打開界面:

一篇文帶你從0到1了解建站及完成CMS系統編寫

可拖拽布局實作界面編輯:

一篇文帶你從0到1了解建站及完成CMS系統編寫

拖拽成如下界面:

一篇文帶你從0到1了解建站及完成CMS系統編寫

點選下載下傳可檢視生成的html代碼:

一篇文帶你從0到1了解建站及完成CMS系統編寫

在點選下載下傳時,我通過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: 流年 <[email protected]>
// +----------------------------------------------------------------------

// 應用公共檔案
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方法(頁面沒寫):

一篇文帶你從0到1了解建站及完成CMS系統編寫

輸入你想要生成的控制器名、方法名,該方法需要綁定資料表中哪些元素,以及綁定的頁面路徑:

一篇文帶你從0到1了解建站及完成CMS系統編寫

輸入完成後點選送出,資料将會傳到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表中,資料表結構資料如下:

一篇文帶你從0到1了解建站及完成CMS系統編寫
一篇文帶你從0到1了解建站及完成CMS系統編寫

我們從控制器生成路徑中可以得知,是admin内的控制器,我們通路生成的控制器方法檢視效果:

一篇文帶你從0到1了解建站及完成CMS系統編寫

資料頁面得到顯示,這些資料都是資料庫中的資料。在建立控制器時,我們在指定資料表及字段時使用的格式内容為如下:

{             
"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);
    }      
$this->view->assign('head',$head);
return $this->view->fetch(dirname(dirname(__FILE__)).'\view\templates\t1.html');