1. 簡介
Joomla 是一款PHP語言開發(fā)的CMS系統(tǒng)迹淌,采用完全面向?qū)ο蟮腗VC模式開發(fā)沟堡。支持多種開發(fā)方式油吭。
Joomla開發(fā)擴展的方式:
- Component : 組件模式雕憔,開發(fā)一套完整的組件應(yīng)用的視圖逾雄、數(shù)據(jù)庫含懊、邏輯结借;
- Module : 模塊模式筐摘,能夠在網(wǎng)頁中顯示的組件,例如登錄模塊等船老;
- Plugin : 插件模式咖熟,用于整個系統(tǒng)中,進行系統(tǒng)各個事件的處理柳畔,無界面馍管;
- Template : 模版模式,用于自定義各種組件Component的外觀薪韩;
通常實現(xiàn)一個應(yīng)用系統(tǒng)确沸,會使用Component進行開發(fā),利用Component可以開發(fā)前臺展示端和后臺管理端俘陷,因此本文使用Component作為實例說明罗捎。
1.1 REST 風(fēng)格
REST風(fēng)格是將HTTP網(wǎng)址、方法進行統(tǒng)一規(guī)范的方式拉盾,提供了一套通用的定義桨菜。例如:POST 方法用于創(chuàng)建資源,PUT方法用于更新資源,GET單純獲取資源倒得;網(wǎng)址例如 /books 獲取圖書列表, /books/3.json 則是獲取 id為 3 的圖書信息泻红。
1.2 實例定義
1. GET /api/books.json 返回圖書列表,并且支持傳遞 page, size 參數(shù);
2. GET /api/books/{id}.json 返回特定圖書的信息
3. POST /api/books 創(chuàng)建一個圖書數(shù)據(jù)
4. DELETE /api/books/{id}.json 刪除一個圖書數(shù)據(jù)
2. 實現(xiàn)方式
2.1 Joomla Component 開發(fā)簡介
Joomla的擴展開發(fā)需要一個關(guān)鍵的xml文件霞掺,這個文件定義了當(dāng)前擴展的類型谊路、基本信息、代碼文件根悼、配置信息和資源信息凶异。通常都是在一個空目錄中創(chuàng)建xml文件,并且增加對應(yīng)的目錄和代碼挤巡,之后打包成zip/tar.gz 即可安裝給Joomla剩彬。
2.2 REST 風(fēng)格的關(guān)鍵技術(shù)
Joomla 中提供了一個 JRoute
類,這個類的方法會對傳入的網(wǎng)址參數(shù)進行處理矿卑,映射成特定的 REST網(wǎng)址喉恋,可以進行網(wǎng)站的SEO優(yōu)化。
對于客戶端等請求 REST網(wǎng)址的方式來說母廷,需要反向?qū)EST網(wǎng)址解析為實際的組件中的頁面轻黑、處理器。
實現(xiàn)上述方式都需要用到一個特定接口的實現(xiàn): JComponentRouterInterface
Component只要實現(xiàn)了這個接口琴昆,就可以實現(xiàn)REST風(fēng)格網(wǎng)址的生成和解析氓鄙。
2.3 Joomla 網(wǎng)址請求規(guī)則
Joomla中的所有的網(wǎng)址都是使用如下的格式來定義:
index.php?option=com_xxx&view=vvv&layout=lll&task=ttt&format=fff
其中 task 和 view不會并存。
- option : 代表請求哪一個組件业舍,我們的例子為 com_restdemo
- view : 代表請求組件的哪一個 view 視圖
- layout : 代表請求 view 的哪一個顯示模版
- task : 代表調(diào)用 請求哪一個控制器的哪一個任務(wù) 通常是 controller.xxx 的方式
- format : 代表要求返回哪種數(shù)據(jù)格式
2.4 關(guān)鍵術(shù)語
- MVC : Joomla 使用MVC模式進行開發(fā)抖拦,每一個部分用于處理不同的需求
- Task : Joomla 組件通過網(wǎng)址訪問,網(wǎng)址中使用 task 參數(shù)可以直接調(diào)用Controller 的方法舷暮,可以不需要網(wǎng)頁顯示
- 文件名稱特性:PHP文件中可以包含 format 類型态罪,這樣可以讓Joomla自動查找對應(yīng)的文件,例如 view.html.php 代表輸出 HTML格式的代碼下面;view.json.php 則代表輸出 json 格式的內(nèi)容复颈。
2.5 初始工程代碼段
- 初始化步驟
創(chuàng)建一個空的目錄,例如 D:\restdemo 目錄(Windows)沥割,或者 ~/restdemo 目錄(Linux)耗啦,在restdemo目錄中創(chuàng)建任意名稱的 xml 文件,例如 restdemo.xml机杜。
<?xml version="1.0" encoding="utf-8" ?>
<extension type="component" version="3.8" method="upgrade">
<name>COM_RESTDEMO</name>
<creationDate>2020/2/8</creationDate>
<author>vhly</author>
<authorEmail>your@email.com</authorEmail>
<authorUrl>http://your.url.com</authorUrl>
<copyright>A copyright</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<version>1.0</version>
<description>COM_RESTDEMO_XML_DESCRIPTION</description>
<!-- Front-end files -->
<files folder="com_restdemo">
<filename>restdemo.php</filename>
<filename>controller.php</filename>
<folder>controllers</folder>
<folder>models</folder>
<folder>views</folder>
</files>
<administration>
<!-- 暫時不加后臺處理 -->
</administration>
</extension>
在restdemo目錄中創(chuàng)建 com_restdemo 目錄芹彬,并且在這個目錄中,繼續(xù)創(chuàng)建restdemo.php文件叉庐,controller.php 文件舒帮,controllers 子目錄,models 子目錄,和views 子目錄玩郊。
組件被執(zhí)行的時候肢执,會自動查找和加載 restdemo.php 文件,通過這個文件加載組件的處理器译红,這個處理器是通過 controller.php 定義的预茄,也是自動加載的,處理器會根據(jù)請求的 view, format, layout, task 等參數(shù)侦厚,來自動的查找 controllers, views 子目錄中的類耻陕,進行處理。
- restdemo.php 主入口
主入口會創(chuàng)建主Controller刨沦,執(zhí)行相關(guān)操作诗宣,也就是 controller.php 會被自動加載。對應(yīng)的代碼如下:
<?php
// Joomla 代碼必須在第一行加入這句話
defined('_JEXEC') or die;
// 創(chuàng)建 Controller對象想诅,名稱前綴為 RestDemo的對象
// Joomla 代碼會自動將所有類的 名稱轉(zhuǎn)換為小寫再加載文件
// 本代碼會自動加載 controller.php 文件召庞,并且創(chuàng)建對象
$controller = JControllerLegacy::getInstance('RestDemo');
// 處理器處理網(wǎng)址參數(shù) task 并進行邏輯處理,
// 如果沒有設(shè)置,默認處理 task=display
$controller->execute(JFactory::getApplication()->input->get('task'));
$controller->redirect();
- 主控制器 controller.php
主控制器名稱是 RestDemoController 来破,其中 RestDemo 就是 restdemo.php中指定的前綴篮灼。所有的 代碼都是以 RestDemo 開頭。主控制器默認的任務(wù)為 display徘禁,這個任務(wù)會自動加載view進行顯示诅诱,默認名稱為 restdemo 的view,可以設(shè)置 default_view 來修改送朱。
<?php
defined('_JEXEC') or die;
/**
* 主控制器娘荡,主控制器默認會處理 display 任務(wù)
* display 會自動加載 views/restdemo 目錄的 view
*/
class RestDemoController extends JControllerLegacy
{
/**
* 設(shè)置 display 任務(wù)時 默認顯示為 views/index/view.html.php,
* 如果不設(shè)置骤菠,則會要求加載 views/restdemo/view.html.php
*/
protected $default_view = 'index';
}
- 制作index視圖
在 views 目錄中,創(chuàng)建一個新的目錄疤孕,名稱為 "index", 并且在 index中創(chuàng)建view.html.php 的文件商乎。
最終的路徑為com_restdemo/views/index/view.html.php
。
<?php
defined('_JEXEC') or die;
// RestDemo 為前綴祭阀,View 代表是View類型鹉戚,Index 就是對應(yīng)的 "index" 頁面
class RestDemoViewIndex extends JViewLegacy
{
public function display($tpl = null)
{
return parent::display($tpl);
}
}
默認的代碼 display 會在當(dāng)前 view.html.php 文件下查找 tmpl/default.php 的文件,作為實際的輸出內(nèi)容专控。完整路徑為
com_restdemo/views/index/tmpl/default.php
<?php
defined('_JEXEC') or die;
echo 'Hello World by vhly.';
- 打包安裝
默認的Component 已經(jīng)完成抹凳,先進行安裝測試,打包文件:
- restdemo.xml
- com_restdemo 目錄
將這兩個部分壓縮成一個壓縮包伦腐,可以采用 zip 方式赢底,打包之后,在Joomla后臺管理“擴展安裝”菜單進行安裝即可。
- 測試頁面
http://xxx.xxx.xxx.xxx/index.php?option=com_restdemo
這個地址會默認查找 com_restdemo 組件的前臺頁面幸冻,會找到 restdemo.php 并且最終顯示 index的內(nèi)容粹庞。
2.6 增加 REST 地址映射
由于Component組件包含前臺代碼和后臺管理代碼,相當(dāng)于兩套代碼洽损,通過 xml文件進行描述庞溜,本文沒有編寫后臺管理代碼,因此只處理前臺代碼的REST映射碑定。
只要在 com_restdemo 文件夾增加 router.php 文件流码,并且實現(xiàn)代碼,就可以增加REST映射了延刘。
具體修改如下:
- 修改 restdemo.xml 文件
<?xml version="1.0" encoding="utf-8" ?>
<extension type="component" version="3.8" method="upgrade">
<name>COM_RESTDEMO</name>
<creationDate>2020/2/8</creationDate>
<author>vhly</author>
<authorEmail>your@email.com</authorEmail>
<authorUrl>http://your.url.com</authorUrl>
<copyright>A copyright</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<version>1.0</version>
<description>COM_RESTDEMO_XML_DESCRIPTION</description>
<!-- Front-end files -->
<files folder="com_restdemo">
<filename>restdemo.php</filename>
<filename>controller.php</filename>
<!-- 此處增加一個文件B浴!访娶! -->
<filename>router.php</filename>
<folder>controllers</folder>
<folder>models</folder>
<folder>views</folder>
</files>
<administration>
</administration>
</extension>
- 增加 com_restdemo/router.php 文件
創(chuàng)建 RestDemoRouter 類商虐,并且實現(xiàn) JComponentRouteInterface
需要實現(xiàn)三個方法,方法并不是必須要有實際的代碼崖疤,根據(jù)需求來實現(xiàn)秘车。
- preprocess($query) 方法:在生成地址之前處理請求參數(shù)
- build(&$query) 方法:自己實現(xiàn)生成 rest/seo 風(fēng)格的地址
- parse(&$segments) 方法:解析REST風(fēng)格的網(wǎng)址 !劫哼!
<?php
defined('_JEXEC') or die;
class RestDemoRouter implements JComponentRouterInterface
{
/**
* 預(yù)處理 query 請求叮趴,這個是網(wǎng)址中的 各個請求參數(shù)
*/
public function preprocess($query)
{
return $query;
}
/**
* 這個方法生成 rest 風(fēng)格的網(wǎng)址,或者是 SEO友好的網(wǎng)址
* 將類似 index.php?option=com_restdemo&view=index 轉(zhuǎn)化
* 成類似這樣的網(wǎng)址 index.php/com_restdemo/index.html
*/
public function build(&$query)
{
// TODO: Implement build() method.
}
/**
* 此方法將 REST 風(fēng)格網(wǎng)址解析成實際的請求地址
* 這個方法是重要的
*/
public function parse(&$segments)
{
// TODO: Implement parse() method.
}
}
3. 實現(xiàn)REST風(fēng)格API
3.1 測試生成 /api/books.json API
這個API請求需要注意幾個關(guān)鍵特征:
- 請求返回的格式必須是 json权烧,也就是 application/json 類型
- 請求的方法是 GET
設(shè)計思路:
- 請求的內(nèi)容是 JSON眯亦,不需要使用頁面,直接使用Controller即可般码;
- 使用Controller妻率,需要定義 task 來對應(yīng)請求;
- Controller 必須是支持JSON的板祝,不能夠是默認HTML的宫静,需要單獨創(chuàng)建;
- JSON 響應(yīng)使用 JResponseJSON 來封裝券时;
- Router 中只要實現(xiàn) parse 即可孤里,其余兩個方法不是必須的;
實現(xiàn)步驟:
- 在 router.php 的 parse 方法橘洞,增加 api/books.json 的支持捌袜,映射到controller
每當(dāng)請求為 api/books.json 那么調(diào)用實際的地址變成:
index.php?option=com_restdemo&task=api.books&format=json
/**
* 此方法將 REST 風(fēng)格網(wǎng)址解析成實際的請求地址
* 這個方法是重要的
*/
public function parse(&$segments)
{
$query = array(); // 返回查詢字段,拼接成 key1=value1&key2=value2
$slen = count($segments);
if ($slen > 0) {
// api 開頭
if ('api' === $segments[0]) {
if ($slen > 1) {
// 請求為 api/books.json
if ('books.json' === $segments[1]) {
// 開始設(shè)置地址請求參數(shù)
// 1. 設(shè)置 task = api.books
$query['task'] = 'api.books';
// 2. 設(shè)置 format = 'json'
$query['format'] = 'json';
}
}
}
}
return $query;
}
- 創(chuàng)建 API Controller炸枣,路徑為 com_restdemo/controllers/api.json.php
名稱 api.json.php 只有到 format=json時虏等,回調(diào)用這個PHP文件
<?php
defined('_JEXEC') or die;
class RestDemoControllerApi extends JControllerLegacy
{
/**
* task = api.books 會自動映射到這個方法
*/
public function books(){
// 所有其余的請求都會通過input來獲取
$input = JFactory::getApplication()->input;
$books = array();
$book = new stdClass();
$book->title = 'Book 001';
$books[] = $book;
$book = new stdClass();
$book->title = 'Book 002';
$books[] = $book;
echo new JResponseJson($books, 'Books', false);
}
}
- 打包測試
請求地址:http://XXX.XXX.XXX.XXX/index.php?option=com_restdemo&task=api.books&format=json
返回JSON數(shù)據(jù):
- 獲取實際的REST地址
實現(xiàn) router.php 中的 build 方法弄唧,看一下最終生成的地址是什么樣子的。
規(guī)則如下:
- build 方法檢查 task 是否是 api.books
- 檢查 format 是否是 json
- 通過檢查博其,拼接地址
public function build(&$query)
{
$segments = array(); // 地址拼接
if (isset($query['task'])) {
$task = $query['task'];
if (isset($query['format'])) {
$format = $query['format'];
if ('json' === $format) {
if ('api.books' === $task) {
// 將 task=api.books&format=json
// 轉(zhuǎn)換為 api/books.json
$segments[] = 'api';
$segments[] = 'books.json';
// 清除參數(shù)套才,注意保留業(yè)務(wù)參數(shù)
unset($query['task']);
unset($query['format']);
}
}
}
}
return $segments;
}
- 測試REST地址
Joomla中不需要直接使用 Router的代碼,而是通過 JRoute::_() 方法來調(diào)用慕淡。
參數(shù)就是請求地址背伴,會自動傳遞給 build 方法。
在 com_restdemo/views/index/tmpl/default.php 中調(diào)用輸出峰髓,進行測試:
<?php
defined('_JEXEC') or die;
$booksUrl = JRoute::_('index.php?option=com_restdemo&task=api.books&format=json');
echo 'Books URL: '.$booksUrl;
輸出結(jié)果:
最終輸出地址為:
/index.php/component/restdemo/api/books.json
- 生成短地址
如果對于地址有更嚴格的限制傻寂,要求生成 http://IP/api/books.json 的方式,那么
需要將 com_restdemo 的內(nèi)容設(shè)置為網(wǎng)站默認首頁携兵,就會隱藏掉 index.php 和 component 信息疾掰。
1. 在 com_restdemo/views/index/tmpl/ 目錄,創(chuàng)建 default.xml 文件定義菜單鏈接
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="COM_RESTDEMO_RESTDEMO">
<message>
<![CDATA[COM_RESTDEMO_RESTDEMO_DESC_MENU]]>
</message>
</layout>
</metadata>
重新打包安裝徐紧。
進入后臺菜單項管理静檬,創(chuàng)建新的菜單項
選擇菜單項類型為組件頁面:
設(shè)置為默認首頁
即可生成: index.php/api/books.json 地址,直接訪問 http://IP/api/books.json 同樣可以返回數(shù)據(jù)