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

2020-10-25 14:00:42

學習目標

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

最開始,我們就以個人瀏覽網站的方式給大家說一下這一個過程是如何運作的。
在這裡插入圖片描述

我們存取網站,一般先開啟瀏覽器(不要槓),輸入一個網址,隨後瀏覽器開啟一個網頁。在你在請求這一個網址資料的時候,已經發生了一系列的操作。

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,錯誤 10002
    private $validate_success=10001;
    private $validate_error=10002;
    private $validate_info='驗證成功';
    
    //資料庫存
    private $action_success=10003;
    private $action_error=10004;
    private $action_sinfo='操作成功';
    private $action_einfo='操作失敗';

    //驗證器 Code
    public function validataSuccess(){
        return ['code'=>$this->validate_success,'msg'=>$this->validate_info];
    }
    public function validataError($msg){
        return ['code'=>$this->validate_error,'msg'=>$msg];
    }

    //規則 Code
    public function actionSuccess(){
        return ['code'=>$this->action_success,'msg'=>$this->action_sinfo];
    }
    public function actionError(){
        return ['code'=>$this->action_error,'msg'=>$this->action_einfo];
    }

}

以上類定義了操作失敗或成功的返回狀態,方便之後的操作呼叫該狀態碼。該php檔案我寫在controller同目錄下的code目錄中,名為ReturnCodeInfo的php檔案。則在Authpost程式碼中使用瞭如下程式碼引入:

use app\admin\code\ReturnCodeInfo;

接著我們返回到html中,在提交按鈕上繫結onclick事件,當然你使用別的方式也行,程式碼如下:

<button type="button" onclick="add()" class="btn btn-success btn-bordered waves-effect w-md waves-light m-b-5">提交</button>

隨後在頁面中填入內容:
在這裡插入圖片描述
點選提交:
在這裡插入圖片描述
操作成功。
我們按照如上方式建立角色組的建立,點選角色組管理:
在這裡插入圖片描述角色組實現邏輯與管理員實現邏輯類似,不再贅述。點選新增進入新增頁:
在這裡插入圖片描述填入組名後點選提交,操作成功:
在這裡插入圖片描述該邏輯實現一致,均是建立模型後進行資料插入。
隨後開始新增規則,進入規則新增頁:
在這裡插入圖片描述點選提交進行新增。

上述相同過程完成後開始實現許可權認證的邏輯。
接下來完全使用者對角色組的繫結,進入管理員管理頁,點選編輯:
在這裡插入圖片描述進入編輯頁後選擇超級管理員:
在這裡插入圖片描述
點選提交,完成繫結:
在這裡插入圖片描述
提交方法使用ajax,所存取的介面為Authpost下的groupBindUser方法:

//組繫結使用者
    public function groupBindUser(){
        $request_data = Request::post();

        $data['uid']=$request_data['uid'];
        $data['group_id']=$request_data['gid'];
        
        $data['create_time']=$data['update_time']=time();

        //儲存
        $res = AuthGroupAccess::create($data);
        
        if($res){
            return json((new ReturnCodeInfo())->actionSuccess());
        }else{
            return json((new ReturnCodeInfo())->actionError());
        }
    }

該方法主要使用AuthGroupAccess模型呼叫create方法進行資料插入。資料庫儲存如下:
在這裡插入圖片描述
為使用者id對應的組id。
隨後進入組頁面,進行組繫結規則的操作,點選編輯進入超級管理員編輯頁:
在這裡插入圖片描述
選擇需要的規則,點選提交完成規則與組的繫結:
在這裡插入圖片描述資料庫儲存如下:
在這裡插入圖片描述
以上表中,id為組id,rules則為規則的id。

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傳值,前端頁顯示如下:
在這裡插入圖片描述
隨後填入帳號及密碼通過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=&quot;key&quot; value=&quot;1&quot;}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=&quot;key&quot; value=&quot;1&quot;}active{/eq}",'{eq name="key" value="1"}active{/eq}',$template);

完成清洗替換後生成html模板生成危機:

file_put_contents(dirname(dirname(__FILE__)).'\view\templates\t1.html',$template);

由於是demo,所以位置寫死了。
隨後存取Autoview控制器下的createcontrol方法(頁面沒寫):
在這裡插入圖片描述
輸入你想要生成的控制器名、方法名,該方法需要繫結資料表中哪些元素,以及繫結的頁面路徑:
在這裡插入圖片描述
輸入完成後點選提交,資料將會傳到Autoview控制器中的buildcontrol方法中,方法程式碼如下:

//控制器生成 方法名及資料庫
    public function buildcontrol(){
        $request_data = Request::post();
        $data['controll'] = $request_data['controll'];
        $data['function']=$request_data['function'];
        $data['datas']=$request_data['datas'];
        $data['templatepath']=$request_data['templatepath'];

        $controlcode='<?php

        namespace app\admin\controller;
        use think\Controller;
        use think\Db;
        
        class '.$data['controll'].' extends AutoviewBase{
        
            public function '.$data['function'].'(){
        
                return $this->view->fetch(dirname(dirname(__FILE__)).'.$data['templatepath'].');
            }
        }';

        file_put_contents(dirname(dirname(__FILE__)).'\controller\\'.$data['controll'].'.php',$controlcode);

        $res = Url_datas::create($data);

        if($res){
            return json((new ReturnCodeInfo())->actionSuccess());
        }else{
            return json((new ReturnCodeInfo())->actionError());
        }
    }

該方法controlcode變數為控制器模板變數,該模板文字可以得知該控制器名稱為自定義名稱,繼承於AutoviewBase基礎類別,方法名也是自定義,模板位置根據指定路徑進行輸出渲染。最後使用 file_put_contents 進行控制器生成。最後將資料存入到Url_datas模型中,也是Url_datas表中,資料表結構資料如下:
在這裡插入圖片描述

在這裡插入圖片描述我們從控制器生成路徑中可以得知,是admin內的控制器,我們存取生成的控制器方法檢視效果:

在這裡插入圖片描述

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

{             
"banner":"id,title,img,content",            
 "article":"id,title,content,faceimg",             
 "media":"id,src,img,title,content",             
 "ad":"id,title,content,img"        
  }

資料指定格式為「資料表」:「欄位1,欄位2…」,通過在AutoviewBase的前置方法中對該json資料進行解析,AutoviewBase基礎類別如下:

<?php
/**
 * |-----------------------
 * | 前端頁面自定義
 * |-----------------------
 */
namespace app\admin\controller;
use think\Controller;
use think\Db;

class AutoviewBase extends Controller{
    protected $beforeActionList = [
        'toview'
    ];

    public function toview(){
        $head='<link href="/autoview/css/bootstrap-combined.min.css" rel="stylesheet">
        <link href="/autoview/css/layoutit.css" rel="stylesheet">
        <!-- Le styles -->
        <link href="/autoview/css/bootstrap-combined.min.css" rel="stylesheet">
        <link href="/autoview/css/layoutit.css" rel="stylesheet">
        
        <!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
        <!--[if lt IE 9]>
                <script src="/autoview/js/html5shiv.js"></script>
            <![endif]-->
        
            <!-- Fav and touch icons -->
            <link rel="shortcut icon" href="/autoview/img/favicon.png">
            
            <script type="text/javascript" src="/autoview/js/jquery-2.0.0.min.js"></script>
            <!--[if lt IE 9]>
            <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
            <![endif]-->
            <script type="text/javascript" src="/autoview/js/bootstrap.min.js"></script>
            <script type="text/javascript" src="/autoview/js/jquery-ui.js"></script>
            <script type="text/javascript" src="/autoview/js/jquery.ui.touch-punch.min.js"></script>
        <script type="text/javascript" src="/autoview/js/jquery.htmlClean.js"></script>
        <script type="text/javascript" src="/autoview/ckeditor/ckeditor.js"></script>
        <script type="text/javascript" src="/autoview/ckeditor/config.js"></script>
        <script type="text/javascript" src="/autoview/js/scripts.js"></script>';
        
        $con['controll']=strtolower(request()->controller());
        $con['function']=strtolower(request()->action());

        //by controll and action select rules
        $data_rules=Db::name('url_datas')
        ->where($con)
        ->order('id', 'desc')
        ->field('datas')
        ->find();
        
        //get datas
        $datas=[];
        $tables = json_decode($data_rules['datas'], true);
        foreach($tables as $key => $value){
            $datas[$key]=Db::name($key)
                        ->column($value);
        }

        //輸出到前端
        foreach($datas as $key => $value){
            $this->view->assign($key,$value);
        }


        $this->view->assign('head',$head);

        return $this->view->fetch(dirname(dirname(__FILE__)).'\view\templates\t1.html');
    }

}

以上程式碼中定義了前置操作toview方法,在toview方法中定義了head為頭部資原始檔,之後使用如下程式碼獲取當前控制器及方法名:

$con['controll']=strtolower(request()->controller());
$con['function']=strtolower(request()->action());

把控制器及方法名當作條件至url_datas資料表中查詢所需的資料要求及格式:

//by controll and action select rules
        $data_rules=Db::name('url_datas')
        ->where($con)
        ->order('id', 'desc')
        ->field('datas')
        ->find();

得到了所需資料後,對該資料進行json解析,解析後遍歷該資料作為對指定表與資料的查詢:

		$datas=[];
        $tables = json_decode($data_rules['datas'], true);
        foreach($tables as $key => $value){
            $datas[$key]=Db::name($key)
                        ->column($value);
        }

之後使用遍歷把得到的資料結果輸出到前端:

 //輸出到前端
    foreach($datas as $key => $value){
        $this->view->assign($key,$value);
    }

最後把head傳遞值前端程式碼,渲染輸出:

 $this->view->assign('head',$head);
return $this->view->fetch(dirname(dirname(__FILE__)).'\view\templates\t1.html');

以上內容準備過於匆忙,只講解了實現中較為重要的地方,很多優化及細節沒有說明,希望下次將會編寫一份完全的教學給大家!如有錯誤歡迎指出,想要深入學習可以關注博主,點贊博主、收藏博文,謝謝~
原創作品@CSDN 1_bit https://blog.csdn.net/A757291228