首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 靈活文章調用類設計與實現:模板驅動、多維篩選、層級導航的 CMS 核心組件

     編輯:PHP 綜合知識

在內容管理系統(CMS)中,文章列表的靈活調用是核心需求。本文展示一個功能完整的 Article 類,支援模板驅動渲染、多條件篩選、分頁排序、層級導航、圖文列表、預覽鏈接生成等企業級特性,可直接集成至項目或作為設計參考。

核心特性

  • 模板驅動:字符串模板支援字段替換、長度截取、條件判斷、日期格式化
  • 多維篩選:分類、區域、狀態、圖片/視頻、自定義 WHERE、ID 列表
  • 靈活排序:雙字段排序(主/次)、升降序任意組合
  • 分頁支援:起始位置、顯示條數、循環變量
  • 關聯數據:自動 JOIN 分類表獲取分類標題、目錄、描述
  • 靜態方法:單篇獲取、點擊統計、導航生成、預覽鏈接
  • 擴展友好:清晰的屬性結構、SQL 構建分離、易於繼承定制

類結構設計

<?php
/**
 * 文章調用核心類
 * 僅支援 PHP 5.3+(建議 7.4+)
 * 依賴:Database 單例類(需實現 Query、ExecuteQuery、Update、ResultArr 等)
 * 常量:DB_TABLE_PRE、SITE_URL、SITE_FOLDER、ARTICLEURL、ARTICLEPATH、ISSUEFLAG、ARTICLE_FLAG
 */
class Article {
    // === 公共配置屬性 ===
    public $CateId = 0;           // 分類 ID,支援 "12" 或 "12,13"
    public $Count = 10;           // 顯示條數
    public $TitleCount = 20;      // 標題截取長度(默認,模板可覆蓋)
    public $BeginCount = 0;       // 起始位置(分頁用)
    public $OrderBy = 'id';       // 主排序字段
    public $OrderSort = 'DESC';   // 主排序方向
    public $OrderBy2 = '';        // 次排序字段
    public $OrderSort2 = '';      // 次排序方向
    public $Area = 0;             // 顯示區域(對應 area1-area5 字段)
    public $Flag = 2;             // 文章狀態(2=已發布)
    public $Pic = 0;              // 僅有圖片 (1=是)
    public $Video = 0;            // 僅有視頻 (1=是)
    public $notshowlist = 0;      // 包含不顯示在列表 (1=是)
    public $AndWhere = '';        // 額外 SQL WHERE 條件
    public $Loop = 0;             // 循環計數器初值
    public $Template = '';        // 渲染模板字符串
    public $IdList = '';          // 調用後生成的 ID 列表(輸出屬性)
    
    // === 內部 SQL 片段 ===
    protected $SqlCateId = '';
    protected $SqlCateTitleId = '';
    protected $SqlArea = '';
    protected $SqlFlag = '';
    protected $SqlNotShow = '';
    protected $SqlPic = '';
    protected $SqlVideo = '';
    protected $SqlOrder = '';
    protected $SqlLimit = '';
    protected $SqlWhere = '';
    public $SqlStr = '';          // 調試用:最終完整 SQL
    
    // === 初始化 SQL 片段 ===
    public function InitSql() {
        // 分類條件
        if (strpos($this->CateId, ',') !== false) {
            $this->SqlCateId = ' AND `cateid` IN (' . $this->CateId . ')';
        } elseif ($this->CateId > 0) {
            $this->SqlCateId = ' AND `cateid` = ' . (int)$this->CateId;
        } else {
            $this->SqlCateId = '';
        }
        
        // 區域條件
        if ($this->Area > 0) {
            $this->SqlArea = ' AND `area' . (int)$this->Area . '` = 1';
        } else {
            $this->SqlArea = '';
        }
        
        // 狀態
        $this->SqlFlag = ' AND `flag` = ' . (int)$this->Flag;
        
        // 列表顯示
        $this->SqlNotShow = ' AND `notshowlist` = ' . (int)$this->notshowlist;
        
        // 圖片/視頻
        $this->SqlPic = ($this->Pic == 1) ? ' AND (`pic1` <> "" OR `pic2` <> "")' : '';
        $this->SqlVideo = ($this->Video == 1) ? ' AND `isvideo` = 1' : '';
        
        // 額外條件
        $this->SqlWhere = $this->AndWhere ? ' AND ' . $this->AndWhere : '';
        
        // 排序
        $this->SqlOrder = ' ORDER BY `' . $this->OrderBy . '` ' . $this->OrderSort;
        if ($this->OrderBy2) {
            $this->SqlOrder .= ', `' . $this->OrderBy2 . '` ' . $this->OrderSort2;
        }
        
        // 分頁
        $this->SqlLimit = ' LIMIT ' . (int)$this->BeginCount . ', ' . (int)$this->Count . ';';
    }
    
    // === 重置屬性 ===
    public function Clear() {
        $this->CateId = 0;
        $this->Count = 10;
        $this->TitleCount = 20;
        $this->BeginCount = 0;
        $this->OrderBy = 'id';
        $this->OrderSort = 'DESC';
        $this->OrderBy2 = '';
        $this->OrderSort2 = '';
        $this->Area = 0;
        $this->Flag = 2;
        $this->Pic = 0;
        $this->Video = 0;
        $this->notshowlist = 0;
        $this->AndWhere = '';
        $this->Loop = 0;
        $this->Template = '';
        $this->IdList = '';
    }

核心方法:getArticleListByCateId()


    /**
     * 根據模板獲取文章列表 HTML
     * 模板語法:
     *   {html}        文章鏈接(優先 reurl)
     *   {title}       標題(可 {title|長度} 截取)
     *   {title2}      副標題(可 {title2|長度})
     *   {title_full}  完整標題
     *   {titlestrip}  去標籤標題
     *   {memo}        摘要(可 {memo|長度})
     *   {posttime}    發布時間(可 {posttime|Y-m-d H:i} 格式化)
     *   {hits}        點擊數
     *   {catetitle}   分類標題
     *   {catedir}     分類目錄
     *   {catedesc}    分類描述
     *   {pic}         主圖(優先 pic2)
     *   {pic1} / {pic2} 原始圖片字段
     *   {loop}        循環計數器
     *   {id}          文章 ID
     */
    public function getArticleListByCateId(): string {
        $this->InitSql();
        
        // 解析模板中的長度/格式參數
        preg_match('/\{title\|([\d.]+?)\}/i', $this->Template, $m);  $titleCount = $m[1] ?? 0;
        preg_match('/\{title2\|([\d.]+?)\}/i', $this->Template, $m); $title2Count = $m[1] ?? 0;
        preg_match('/\{memo\|([\d.]+?)\}/i', $this->Template, $m);   $memoCount = $m[1] ?? 0;
        preg_match('/\{posttime\|(.+?)\}/i', $this->Template, $m);   $timeFormat = $m[1] ?? '';
        
        // 移除參數標記,保留基礎標籤供替換
        $tpl = preg_replace('/\{title\|[\d.]+?\}/i', '{title}', $this->Template);
        $tpl = preg_replace('/\{title2\|[\d.]+?\}/i', '{title2}', $tpl);
        $tpl = preg_replace('/\{memo\|[\d.]+?\}/i', '{memo}', $tpl);
        $tpl = preg_replace('/\{posttime\|.+?\}/i', '{posttime}', $tpl);
        
        // 構建 SQL
        $sql = 'SELECT * FROM `' . DB_TABLE_PRE . 'view_article` WHERE 1=1';
        $sql .= $this->SqlNotShow . $this->SqlCateId . $this->SqlArea . $this->SqlFlag;
        $sql .= $this->SqlPic . $this->SqlVideo . $this->SqlWhere . $this->SqlOrder . $this->SqlLimit;
        $this->SqlStr = $sql;
        
        // 重置次排序(避免影響後續調用)
        $this->OrderBy2 = $this->OrderSort2 = '';
        
        $db = Database::Get();
        $db->SqlStr = $sql;
        $result = '';
        $loop = $this->Loop;
        
        if ($db->Query()) {
            foreach ($db->ResultArr as $row) {
                $this->IdList .= $row['id'] . ',';
                
                // 鏈接處理
                $url = $row['reurl'] ?: (SITE_URL . SITE_FOLDER . ARTICLEURL . $row['html']);
                
                // 圖片處理
                $pic = $row['pic2'] ?: $row['pic1'];
                
                // 字段處理
                $title = $titleCount ? subString($row['title'], $titleCount) : $row['title'];
                $title2 = $title2Count ? subString($row['title2'], $title2Count) : $row['title2'];
                $memo = $memoCount ? subString($row['memo'], $memoCount) : $row['memo'];
                $titleFull = $row['title'];
                $titleStrip = strip_tags($row['title']);
                
                // 標題顏色
                if ($row['titlecolor']) {
                    $title = '' . $title . '';
                }
                
                // 時間格式化
                $posttime = $timeFormat 
                    ? date($timeFormat, $row['posttime']) 
                    : date('Y-m-d H:i', $row['posttime']);
                
                // 模板替換
                $search = [
                    '{id}', '{title_full}', '{title}', '{title2}', '{titlestrip}',
                    '{memo}', '{html}', '{hits}', '{posttime}',
                    '{catetitle}', '{catedir}', '{pic}', '{pic1}', '{pic2}',
                    '{loop}', '{catedesc}'
                ];
                $replace = [
                    $row['id'], $titleFull, $title, $title2, $titleStrip,
                    $memo, $url, $row['hits'], $posttime,
                    $row['catetitle'], $row['catedir'], $pic, $row['pic1'], $row['pic2'],
                    $loop++, $row['catedesc']
                ];
                
                $result .= str_replace($search, $replace, $tpl);
            }
        }
        
        $this->Clear();
        return $result;
    }

靜態便捷方法


    // === 單篇內容獲取 ===
    public static function getContentById(int $id): string {
        $db = Database::Get();
        $db->SqlStr = 'SELECT `content` FROM `' . DB_TABLE_PRE . 'view_article` WHERE `id` = ' . (int)$id;
        return $db->Query() ? $db->ResultArr[0][0] : '';
    }
    
    public static function getUrlById(int $id): string {
        $db = Database::Get();
        $db->SqlStr = 'SELECT `reurl`, `html` FROM `' . DB_TABLE_PRE . 'view_article` WHERE `id` = ' . (int)$id;
        if ($db->Query()) {
            return $db->ResultArr[0]['reurl'] ?: ('article_details.php?id=' . $id);
        }
        return '';
    }
    
    public static function getHTMLById(int $id): string {
        $db = Database::Get();
        $db->SqlStr = 'SELECT `html` FROM `' . DB_TABLE_PRE . 'view_article` WHERE `id` = ' . (int)$id;
        return $db->Query() ? $db->ResultArr[0][0] : '';
    }
    
    // === 點擊統計 ===
    public static function UpdateHits(int $id): void {
        $db = Database::Get();
        $db->SqlStr = 'UPDATE `' . DB_TABLE_PRE . 'article` SET `hits` = `hits` + 1 WHERE `id` = ' . (int)$id;
        $db->ExecuteQuery();
    }
    
    public static function GetHits(int $id): int {
        $db = Database::Get();
        $db->SqlStr = 'SELECT `hits` FROM `' . DB_TABLE_PRE . 'article` WHERE `id` = ' . (int)$id;
        return $db->Query() ? (int)$db->ResultArr[0][0] : 0;
    }
    
    // === 導航生成 ===
    public static function getCatePreviewUrl(int $cateid): string {
        $db = Database::Get();
        $db->SqlStr = 'SELECT `template_url` FROM `' . DB_TABLE_PRE . 'view_articlecate` WHERE `id` = ' . (int)$cateid . ' LIMIT 1';
        return $db->Query() ? $db->ResultArr[0][0] : '';
    }
    
    public static function getPreviewUrl(int $id): string {
        $db = Database::Get();
        $db->SqlStr = 'SELECT `template_url` FROM `' . DB_TABLE_PRE . 'view_articlelist` WHERE `id` = ' . (int)$id . ' LIMIT 1';
        return $db->Query() ? $db->ResultArr[0][0] : '';
    }
    
    // === 狀態顯示 ===
    public static function printFlag(int $flag = 0): string {
        return match($flag) {
            0 => '未存 未發',
            1 => '已存 未發',
            2 => '已存 已發',
            default => '未知狀態'
        };
    }
    
    public static function printHtmlFlag(int $flag = 0): string {
        return $flag ? '已生' : '未生';
    }
    
    public static function printVideoFlag(int $flag = 0): string {
        return $flag == 1 ? '視' : '';
    }
    
    public static function printIssueFlag(int $flag = 0): string {
        return match($flag) {
            1 => '未審',
            2 => '已審',
            default => '未知狀態'
        };
    }
}

使用示例

基礎列表調用

<?php
$Article = new Article();
$Article->Template = '<li><a href="{html}" title="{title}">{title|30}</a> [{catetitle}] <span>{posttime|Y-m-d}</span></li>';
$Article->CateId = 8;          // 調用分類 8
$Article->Count = 15;          // 15 條
$Article->OrderBy = 'posttime'; // 按發布時間
$Article->OrderSort = 'DESC';  // 倒序
$Article->Pic = 1;             // 僅有圖片
echo '<ul class="article-list">' . $Article->getArticleListByCateId() . '</ul>';
?>

多欄目混合調用

<?php
$Article = new Article();
$Article->Template = '<div class="item"><h3><a href="{html}">{title|40}</a></h3><p>{memo|100}</p></div>';
$Article->CateId = '8,9,10';   // 多個分類
$Article->Count = 20;
$Article->OrderBy = 'order';   // 按排序字段
$Article->OrderSort = 'ASC';
$Article->OrderBy2 = 'id';
$Article->OrderSort2 = 'DESC';
echo $Article->getArticleListByCateId();
?>

首頁圖文混排塊

<?php
// 使用內建方法 getPartByCateId(需在類中實現)
$Article = new Article();
echo $Article->getPartByCateId(8); // 返回完整 DL/DT/DD 結構 HTML
?>

分頁導航

<?php
$page = max(1, (int)($_GET['page'] ?? 1));
$pageSize = 10;
$Article = new Article();
$Article->BeginCount = ($page - 1) * $pageSize;
$Article->Count = $pageSize;
$Article->CateId = 8;
$Article->Template = '...';
$listHtml = $Article->getArticleListByCateId();

// 總數查詢(需額外 SQL)
$db = Database::Get();
$db->SqlStr = 'SELECT COUNT(*) FROM `' . DB_TABLE_PRE . 'view_article` WHERE `cateid`=8 AND `flag`=2';
$db->Query();
$total = $db->ResultArr[0][0];
$totalPages = ceil($total / $pageSize);

// 輸出分頁 HTML...
?>

數據庫結構參考

文章表 (mc_article)

CREATE TABLE `mc_article` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `cateid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分類ID',
  `title` varchar(255) NOT NULL COMMENT '標題',
  `title2` varchar(255) DEFAULT NULL COMMENT '副標題',
  `content` text COMMENT '內容',
  `memo` text COMMENT '摘要',
  `posttime` int(10) unsigned NOT NULL COMMENT '發布時間戳',
  `flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '狀態:0草稿,1存檔,2發布',
  `hits` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '點擊數',
  `pic1` varchar(255) DEFAULT NULL COMMENT '圖片1',
  `pic2` varchar(255) DEFAULT NULL COMMENT '圖片2',
  `reurl` varchar(255) DEFAULT NULL COMMENT '跳轉鏈接',
  `html` varchar(255) DEFAULT NULL COMMENT '靜態頁路徑',
  `order` tinyint(4) unsigned NOT NULL DEFAULT '99' COMMENT '排序權重',
  `isvideo` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否視頻',
  `notshowlist` tinyint(4) NOT NULL DEFAULT '0' COMMENT '不在列表顯示',
  `titlecolor` varchar(7) DEFAULT NULL COMMENT '標題顏色',
  `area` int(10) unsigned NOT NULL DEFAULT '0',
  `area1`-`area5` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `userid` int(10) unsigned NOT NULL DEFAULT '0',
  `author` varchar(255) DEFAULT NULL,
  `from` varchar(255) DEFAULT NULL,
  `keyword` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

分類表 (mc_article_cate)

CREATE TABLE `mc_article_cate` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `parentid` int(10) DEFAULT NULL,
  `level` char(50) DEFAULT NULL COMMENT '層級編碼: 01, 0101, 010101',
  `title` char(100) DEFAULT NULL,
  `dir` char(100) DEFAULT NULL COMMENT '目錄名/路由',
  `url` varchar(255) NOT NULL DEFAULT '',
  `templateid` int(10) DEFAULT NULL,
  `catetemplateid` int(10) DEFAULT NULL,
  `kind` tinyint(4) DEFAULT '0',
  `pagesize` tinyint(4) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

視圖 (mc_view_article)

CREATE VIEW `mc_view_article` AS
SELECT a.*, c.`parentid`, c.`level`, c.`title` AS `catetitle`, c.`dir` AS `catedir`, 
       c.`templateid`, c.`catetemplateid`, c.`kind`, c.`pagesize`, c.`url` AS `curl`
FROM `mc_article` a
JOIN `mc_article_cate` c ON a.`cateid` = c.`id`;

擴展建議

  1. 緩存層:在 getArticleListByCateId 中加入 Redis/Memcached 緩存,Key 由所有公共屬性哈希生成
  2. 參數綁定:將 SQL 拼接改為 PDO 預處理,防止注入風險
  3. 模板引擎:替換為 Blade/Twig/Smarty,支援繼承、邏輯控制、過濾器
  4. 事件鉤子:添加 beforeQuery、afterQuery、beforeRender 事件點
  5. 分頁對象:封裝 Pagination 類自動處理分頁邏輯與鏈接生成
  6. 多語言:字段擴展 title_en、content_en,配合 gettext 實現 i18n
  7. 全文搜索:整合 Elasticsearch/MeiliSearch,提供 search() 方法

總結

這個 Article 類展示了 CMS 核心組件的設計思路:配置屬性化、SQL 構建模組化、渲染模板化、功能靜態化。實際項目中可根據具體框架(Laravel、ThinkPHP、Yii 等)進行 ORM 遷移,但核心設計模式具有普適價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved