新聞?wù)故竟δ?a class="headerlink" href="#id1" title="永久鏈接至標(biāo)題">?

在上一個(gè)章節(jié)里,我們寫(xiě)了一個(gè)用于展示靜態(tài)頁(yè)面的類(lèi)文件,通過(guò)這個(gè)簡(jiǎn)單的例子我們把CI4框架里的一些基本的概念講解了一下。 我們還簡(jiǎn)單的使用了CI4里面的路由功能,通過(guò)自定義路由規(guī)則實(shí)現(xiàn)了頁(yè)面的鏈接地址凈化,使頁(yè)面的訪問(wèn)地址看起來(lái)更整潔和搜索引擎友好。 現(xiàn)在,我們要開(kāi)始進(jìn)行一些基于數(shù)據(jù)庫(kù)的動(dòng)態(tài)內(nèi)容的開(kāi)發(fā)了。

建立教程所需的數(shù)據(jù)庫(kù)?

我們假設(shè)你已經(jīng)安裝和 配置 好了用于 CodeIgniter4 運(yùn)行所需的數(shù)據(jù)庫(kù)軟件。 我們也假設(shè)你會(huì)使用數(shù)據(jù)庫(kù)管理的客戶(hù)端工具(mysql,MySQL Workbench或者phpMyAdmin等)來(lái)運(yùn)行稍后教程里提供的創(chuàng)建數(shù)據(jù)庫(kù)表和插入測(cè)試數(shù)據(jù)所需的SQL代碼。

下面我們就來(lái)教你如何為本教程創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),并且正確配置CodeIgniter來(lái)使用這個(gè)數(shù)據(jù)庫(kù)。

用你安裝好的數(shù)據(jù)庫(kù)客戶(hù)端工具打開(kāi)數(shù)據(jù)庫(kù),然后運(yùn)行下面的兩段SQL代碼(MySQL適用)來(lái)創(chuàng)建一個(gè)數(shù)據(jù)表和插入一些測(cè)試數(shù)據(jù)。 隨著你對(duì) CodeIgniter 的熟悉程度越來(lái)越高,這些數(shù)據(jù)庫(kù)相關(guān)的操作也可以通過(guò)程序代碼在CodeIgniter框架下完成,你可以閱讀 數(shù)據(jù)遷移數(shù)據(jù)填充 這兩個(gè)章節(jié)來(lái)了解相關(guān)內(nèi)容,以便掌握用程序代碼操作數(shù)據(jù)庫(kù)的相關(guān)技能。

CREATE TABLE news (
                      id int(11) NOT NULL AUTO_INCREMENT,
                      title varchar(128) NOT NULL,
                      slug varchar(128) NOT NULL,
                      body text NOT NULL,
                      PRIMARY KEY (id),
                      KEY slug (slug)
);

Note: 數(shù)據(jù)表里的 slug 字段在基于互聯(lián)網(wǎng)訪問(wèn)的網(wǎng)站上是個(gè)非常有用的字段。 一般在這個(gè)字段里存放簡(jiǎn)短的詞語(yǔ)來(lái)概括性描述數(shù)據(jù)內(nèi)容,這將提升用戶(hù)打開(kāi)網(wǎng)址時(shí)候的訪問(wèn)體驗(yàn),并且這種網(wǎng)址也是搜索引擎友好的網(wǎng)址,有利于網(wǎng)站內(nèi)容的SEO優(yōu)化。

然后我們?cè)跀?shù)據(jù)表里插入如下測(cè)試數(shù)據(jù):

INSERT INTO news VALUES
(1,'Elvis sighted','elvis-sighted','Elvis was sighted at the Podunk internet cafe. It looked like he was writing a CodeIgniter app.'),
(2,'Say it isn\'t so!','say-it-isnt-so','Scientists conclude that some programmers have a sense of humor.'),
(3,'Caffeination, Yes!','caffeination-yes','World\'s largest coffee shop open onsite nested coffee shop for staff only.');

連接到你的數(shù)據(jù)庫(kù)?

CodeIgniter 安裝時(shí)會(huì)自動(dòng)生成一個(gè) .env 文件,確保里面的配置信息沒(méi)有被注釋掉,并且和你本地的數(shù)據(jù)庫(kù)實(shí)際情況相吻合:

database.default.hostname = localhost
database.default.database = ci4tutorial
database.default.username = root
database.default.password = root
database.default.DBDriver = MySQLi

創(chuàng)建你的數(shù)據(jù)模型文件?

我們要求你將數(shù)據(jù)庫(kù)的操作代碼寫(xiě)在模型(Model)文件里面,以便以后代碼重用,不要將這些代碼寫(xiě)在控制器(Controller)里。 你的模型文件們應(yīng)該成為你處理數(shù)據(jù)庫(kù)相關(guān)的增、刪、改、查操作的默認(rèn)地方。交由模型文件來(lái)操作數(shù)據(jù)庫(kù)或者其他格數(shù)的數(shù)據(jù)文件。

打開(kāi) app/Models/ 目錄,在這個(gè)目錄下面創(chuàng)建一個(gè)名字為 NewsModel.php 的文件,并在文件里加入如下代碼。 為了保證代碼運(yùn)行順利,你需要確認(rèn)一下已經(jīng)正確的完成了數(shù)據(jù)庫(kù)的相關(guān)配置:doc:這里 <../database/configuration>

<?php

    namespace App\Models;

    class NewsModel extends \CodeIgniter\Model
    {
            protected $table = 'news';
    }

這段代碼和我們上一個(gè)章節(jié)里創(chuàng)建的控制器里的代碼類(lèi)似。 我們通過(guò)繼承 CodeIgniter\Model 創(chuàng)建了一個(gè)新的模型文件,并加載了CI4內(nèi)置的數(shù)據(jù)庫(kù)操作類(lèi)庫(kù)。 后面我們可以在代碼里通過(guò) $this->db 來(lái)調(diào)用數(shù)據(jù)庫(kù)的相關(guān)操作類(lèi)庫(kù)。

現(xiàn)在我們的數(shù)據(jù)庫(kù)和數(shù)據(jù)模型文件已經(jīng)建立好了。 我們首先寫(xiě)一個(gè)方法從數(shù)據(jù)庫(kù)中獲取所有的新聞文章。 為實(shí)現(xiàn)這點(diǎn),我們將使用 CodeIgniter 的數(shù)據(jù)庫(kù)抽象層工具 查詢(xún)構(gòu)建器,通過(guò)它你可以編寫(xiě)你的查詢(xún)代碼,并在 所有支持的數(shù)據(jù)庫(kù)平臺(tái) 上運(yùn)行。 數(shù)據(jù)模型文件可以方便的和 查詢(xún)構(gòu)建器 一起工作,并且提供了一些方法讓你操作數(shù)據(jù)的時(shí)候更加簡(jiǎn)單?,F(xiàn)在向你的模型中添加如下代碼。

public function getNews($slug = false)
{
        if ($slug === false)
        {
                return $this->findAll();
        }

        return $this->asArray()
                     ->where(['slug' => $slug])
                     ->first();
}

通過(guò)這段代碼,你可以執(zhí)行兩種不同的查詢(xún),一種是獲取所有的新聞條目,另一種是根據(jù)特定的 slug 來(lái)獲取指定的新聞條目。 你可能注意到了,我們直接的進(jìn)行了基于``$slug`` 變量的數(shù)據(jù)對(duì)比命令,并不需要預(yù)先執(zhí)行相應(yīng)字段的查詢(xún)操作,因?yàn)?doc:查詢(xún)構(gòu)建器 <../database/query_builder> 自動(dòng)幫我們完成了這個(gè)工作。

我們?cè)谶@里用到的 findAll()first() 都是 CodeIgniter4 的數(shù)據(jù)模型(Model)基礎(chǔ)類(lèi)里面內(nèi)置的方法。 他們根據(jù)我們?cè)跀?shù)據(jù)模型文件里(本例中是 NewsModel 文件)聲明的 $table 變量而知道該對(duì)哪個(gè)數(shù)據(jù)表進(jìn)行操作。 這些方法通過(guò) 查詢(xún)構(gòu)建器 運(yùn)行指令操作當(dāng)前數(shù)據(jù)表,并且會(huì)以數(shù)組的形式返回?cái)?shù)據(jù)查詢(xún)結(jié)果。在這個(gè)例子里面,findAll() 的返回值是包含了指定數(shù)據(jù)表中的所有數(shù)據(jù)對(duì)象的一個(gè)數(shù)組。

顯示新聞?

現(xiàn)在,查詢(xún)已經(jīng)在數(shù)據(jù)模型文件里寫(xiě)好了,接下來(lái)我們需要將數(shù)據(jù)模型綁定到視圖上,向用戶(hù)顯示新聞條目了。 這可以在之前寫(xiě)的 Pages 控制器里來(lái)做,但為了更清楚的闡述,我們定義了一個(gè)新的 News 控制器,創(chuàng)建在 app/controllers/News.php 文件中。

<?php namespace App\Controllers;

use App\Models\NewsModel;

class News extends \CodeIgniter\Controller
{
        public function index()
        {
                $model = new NewsModel();

                $data['news'] = $model->getNews();
        }

        public function view($slug = null)
        {
                $model = new NewsModel();

                $data['news'] = $model->getNews($slug);
        }
}

閱讀上面的代碼你會(huì)發(fā)現(xiàn),這和之前寫(xiě)的代碼有些相似之處。 首先,它繼承了*CodeIgniter*的一個(gè)核心類(lèi),Controller,這個(gè)核心類(lèi)提供了很多非常有用的方法,它確保你可以操作當(dāng)前的 RequestResponse 對(duì)象,也可以操作``Logger`` 類(lèi), 方便你把日志文件寫(xiě)到磁盤(pán)里。

其次,有兩個(gè)方法用來(lái)顯示新聞條目,一個(gè)顯示所有的,另一個(gè)顯示特定的。 你可以看到第二個(gè)方法中調(diào)用模型方法時(shí)傳入了 $slug 參數(shù),模型根據(jù)這個(gè) slug 返回特定的新聞條目。

現(xiàn)在,通過(guò)模型,控制器已經(jīng)獲取到數(shù)據(jù)了,但還沒(méi)有顯示出來(lái)。 下一步要做的就是將數(shù)據(jù)傳遞給視圖。 我們修改 index() 方法成下面的樣子::

public function index()
{
        $model = new NewsModel();

        $data = [
                'news'  => $model->getNews(),
                'title' => 'News archive',
        ];

        echo view('templates/header', $data);
        echo view('news/index', $data);
        echo view('templates/footer');
}

上面的代碼從模型中獲取所有的新聞條目,并賦值給一個(gè)變量(news)。 另外頁(yè)面的標(biāo)題賦值給了 $data['title'] 元素,然后所有的數(shù)據(jù)被傳遞給視圖。 現(xiàn)在你需要?jiǎng)?chuàng)建一個(gè)視圖文件來(lái)顯示新聞條目了,新建 app/Views/news/index.php 文件并添加如下代碼。

<h2><?= $title ?></h2>

<?php if (! empty($news) && is_array($news)) : ?>

        <?php foreach ($news as $news_item): ?>

                <h3><?= $news_item['title'] ?></h3>

                <div class="main">
                        <?= $news_item['text'] ?>
                </div>
                <p><a href="<?= '/news/'.$news_item['slug'] ?>">View article</a></p>

        <?php endforeach; ?>

<?php else : ?>

        <h3>No News</h3>

        <p>Unable to find any news for you.</p>

<?php endif ?>

這里,我們通過(guò)一個(gè)循環(huán)將所有的新聞條目顯示給用戶(hù),你可以看到我們直接采用了 HTMLPHP 混用的寫(xiě)法創(chuàng)建了一個(gè)視圖頁(yè)面。 如果你希望使用一種模板語(yǔ)言,你可以使用 CodeIgniter 的 視圖模版解析類(lèi) <../outgoing/view_parser> ,或其他的第三方解析器。

新聞的列表頁(yè)就做好了,但是我們還缺少一個(gè)顯示特定新聞條目的頁(yè)面。 我們可以調(diào)用之前創(chuàng)建的模型里的數(shù)據(jù)來(lái)實(shí)現(xiàn)這個(gè)功能,你只需要向控制器中添加一些代碼,然后再新建一個(gè)視圖就可以了。 回到 News 控制器,使用下面的代碼替換掉 view() 方法:

public function view($slug = NULL)
{
        $model = new NewsModel();

        $data['news'] = $model->getNews($slug);

        if (empty($data['news']))
        {
                throw new \CodeIgniter\PageNotFoundException('Cannot find the page: '. $slug);
        }

        $data['title'] = $data['news']['title'];

        echo view('templates/header', $data);
        echo view('news/view', $data);
        echo view('templates/footer');
}

我們并沒(méi)有直接調(diào)用 getNews() 方法,而是傳入了一個(gè) $slug 參數(shù),所以它會(huì)返回相應(yīng)的新聞條目。 最后剩下的事是創(chuàng)建視圖文件 app/Views/news/view.php 并添加如下代碼 。

<?php echo ‘<h2>’.$news[‘title’].’</h2>’; echo $news[‘body’];

路由?

由于之前我們創(chuàng)建了基于通配符的路由規(guī)則,所以現(xiàn)在需要新增一條路由以便能訪問(wèn)到你剛剛創(chuàng)建的控制器。 修改路由配置文件(app/config/routes.php)添加類(lèi)似下面的代碼。 該規(guī)則可以讓地址中帶*news*的請(qǐng)求訪問(wèn) News 控制器而不是去訪問(wèn)之前默認(rèn)的 Pages 控制器。 第一行代碼可以讓訪問(wèn) news/slug 地址的 URI 重定向到 News 控制器的 view() 方法。

$routes->get('news/(:segment)', 'News::view/$1');
$routes->get('news', 'News::index');
$routes->get('(:any)', 'Pages::view/$1');

在地址欄里輸入 localhost:8080/news 來(lái)訪問(wèn)你創(chuàng)建好的新聞列表頁(yè)面吧。 你將會(huì)看到如下圖一樣的一個(gè)展示新聞列表的網(wǎng)頁(yè),列表里的每個(gè)文章都帶一個(gè)可以打開(kāi)該條新聞詳情頁(yè)面的超級(jí)鏈接。

../_images/tutorial2.png