2019/09/05
2024/12/09 (更新內容)
當系統越來越複雜的時候,就會需要利用一些現有的架構來協助我們開發,簡單的講,就是套用現成的開發方式來開發。如果利用Java進行web開發,常用的是Spring Framework,如果使用.net,常用的是.net MVC,而PHP也有一些架構,如Laravel及CodeIgniter。這邊介紹比較簡單容易安裝及上手的CodeIgniter,讓大家對架構有個初步的認識,未來,大家學其他架構(如:Laravel)時,就會覺得比較容易上手了。
CodeIgniter 繁體中文 / CodeIgniter
使用者指南 (官方教學) / User Guide
到CodeIgniter 繁體中文 / CodeIgniter (官方網站)下載,目前最新版本是4.4.6 (2024/12/08)。
過去可以直接下載檔案,現在官方文件都不再提供直接下載檔案的說明。
建議以Composer安裝CodeIgniter。
首先,先安裝Composer。(請參考安裝說明 )
以Windows為例,先下載Composer-Setup.exe。安裝後,在終端機(terminal)執行composer,可確認是否安裝成功。目前Composer的版本是2.8.3 (2024/12/8)。
composer -V
Composer version 2.8.3 2024-11-17 13:13:04
PHP version 8.2.12 (c:\xampp\php\php.exe)
Run the "diagnose" command to get more detailed diagnostics output.
appstarter需要解壓縮的程式,可以下載7-zip。如果沒有安裝,會有錯誤。
Failed to download codeigniter4/appstarter from dist: The zip extension and unzip/7z commands are both missing, skipping.
先到你要新增專案的資料夾 (app starter會建立資料夾,所以,不用先新增資料夾。假如你要新增的資料夾是CodeIgniter,在終端機中輸入:
composer create-project codeigniter4/appstarter CodeIgniter
接下來可能會看到一些問題,例如,沒有啟動intl的extension。
Problem 1
- Root composer.json requires codeigniter4/framework ^4.0 -> satisfiable by codeigniter4/framework[4.0.0, ..., v4.5.5].
- codeigniter4/framework[4.0.0, ..., v4.5.5] require ext-intl * -> it is missing from your system. Install or enable PHP's intl extension.
To enable extensions, verify that they are enabled in your .ini files:
- C:\xampp\php\php.ini
You can also run `php --ini` in a terminal to see which files are used by PHP in CLI mode.
Alternatively, you can run Composer with `--ignore-platform-req=ext-intl` to temporarily ignore these required extensions.
找到php.ini,把extension前的「;」拿掉:
extension=intl
改完之後,重啟Apache,也要把剛剛新增的資料夾刪除。再重新執行:
composer create-project codeigniter4/appstarter CodeIgniter
CodeIgniter內建伺服器,可以直接執行:
php spark serve
如果無法執行php,就記得要設定path變數,才能執行php。
Spark預設的port是8080,如果8080已經在使用,就必須改別的port,例如:8081。
php spark serve --port 8081
在瀏覽器輸入網址:
http://localhost:8080
就可以看到預設的畫面了。也就是已經安裝好了!
我們先來了解一下資料夾結構。
首先,我們在CodeIgniter資料夾底下,找不到index.php,因為通常這些框架都不是以傳統php的方式去讀取檔案。所以,如果要用原本的Apache來執行,就需要一些設定。所以,建議採用CodeIgniter內建伺服器。
所有的程式碼都在CodeIgniter資料夾下的app資料夾。所有,我們的程式碼就放在app資料夾之下。
CodeIgniter採用MVC架構,所以,html的部分會在app/Views,網頁的控制會在app/Controllers。
先介紹app/Controllers裡的檔案,目前裡面只有兩個檔案,一個是Home.php,另一個是BaseController.php。
Home.php的內容是:
<?php
namespace App\Controllers;
class Home extends BaseController
{
public function index(): string
{
return view('welcome_message');
}
}
大家第一個反應是: 這個程式只有「<?php」,沒有「?>」,這不是漏打了,而是在CodeIgnitor裡,除非底下有非php的內容,是可以不用"?>"。
這個類別會去繼承BaseController,而不要隨便去改變BaseController.php的內容,免得程式就無法執行了。CodeIgnitor有個小缺點,就是系統產生的必要檔案跟我們開發的檔案會混在一起,如果不小心動到那些檔案,會使得系統無法順利執行。
而這個類別只有一個function: index(),這個function會回傳一個view,view裡的字串就是view的名字。
如果我們把Home.php內容改成:
<?php
namespace App\Controllers;
class Home extends BaseController
{
public function index(): string
{
// return view('welcome_message');
return '<h1>Hello CodeIgnitor</h1>';
}
}
就會看到「Hello CodeIgnitor」。
我們來試試看,如果我們在app/Views新增一個hello.php
<h1>Hi!!</h1>
如果我們把Home.php內容改成:
<?php
namespace App\Controllers;
class Home extends BaseController
{
public function index(): string
{
return view('hello');
// return view('welcome_message');
// return '<h1>Hello CodeIgnitor</h1>';
}
}
在畫面上看到的就是hello的內容了。重要的是,使用者目前沒有任何辦法去執行welcome_message.php。
所以,在CodeIgnitor裡,是靠Controller去執行一個View的程式,而不能直接執行Views底下的任何一個程式。這雖然讓我們不能寫個php程式就可以用,還要寫對應的Controller,也就是把程式的控制權交給Controller,而不是透過檔案名稱去取得,這也大大的減少了一些困擾。我們可以把登入身分的檢查放在Controller裡,有權限才能開啟某些檔案,這樣,我們就不用在每個檔案裡去檢查session,只要session是空的,就轉到登入畫面。
那我們要如何看到不同的頁面呢?
剛剛在介紹Controller的時候,其實跳過了一個很重要的角色: app/Config/Routes.php
<?php
use CodeIgniter\Router\RouteCollection;
/**
* @var RouteCollection $routes
*/
$routes->get('/', 'Home::index');
其實,打開伺服器之所以知道要去執行Home.php裡的index(),是因為我們建了一個route,我們設定當我們以http get呼叫"/"時,會去執行Home類別裡的index。
如果我們新增了一個controller:
app/Controllers/Hi.php
<?php
namespace App\Controllers;
class Hi extends BaseController{
public function hello() {
echo view('welcome_message');
}
}
app/Config/Routes.php
<?php
use CodeIgniter\Router\RouteCollection;
/**
* @var RouteCollection $routes
*/
$routes->get('/', 'Home::index');
$routes->get('/hi', 'Hi::hello');
也就是伺服器會收到url,在Routes.php裡面去找到對應的Controller,並呼叫Controller。
這跟陽春的php很不一樣,陽春的php是靠資料夾的路徑來直接呼叫php,但是,為了簡化url,也避免使用存取沒必要的檔案,很多框架也都可以設定route。
我們來參考官方教學 (靜態頁面 / Static pages)來設定靜態頁面。
route的寫法,除了以字串的方式來寫,也可以用陣列的方式,第一個參數是類別,第二個參數是方法名稱,但是,因為利用類別名稱,所以,需要先use類別的名稱:
<?php
use CodeIgniter\Router\RouteCollection;
use App\Controllers\Hi;
/**
* @var RouteCollection $routes
*/
$routes->get('/', 'Home::index');
//$routes->get('/hi', 'Hi::hello');
$routes->get('/hi', [Hi::class, 'index']);
跟傳統的php不一樣的是,呼叫的時候,也可以設定路徑參數。
<?php
use CodeIgniter\Router\RouteCollection;
use App\Controllers\Pages;
use App\Controllers\Hi;
/**
* @var RouteCollection $routes
*/
$routes->get('/', 'Home::index');
// $routes->get('/hi', 'Hi::index');
$routes->get('hi', [Hi::class, 'index']);
// $routes->get('pages', 'Pages::index');
$routes->get('pages', [Pages::class, 'index']);
$routes->get('pages/(:segment)', [Pages::class, 'view']);
也就是如果我們寫了,想把1當成參數傳給pages:
http://localhost:8080/pages/1
在Controller裡,「1」就會成為view()裡的參數,也就是$page裡的值。$page可以有預設值,但是,因為前面有:
$routes->get('pages', [Pages::class, 'index']);
所以,預設值就沒有用。可以試試看把前面這一行改成。
$routes->get('pages', [Pages::class, 'view']);
/app/Controllers/Pages.php
<?php
namespace App\Controllers;
class Pages extends BaseController
{
public function index()
{
return view('welcome_message');
}
public function view(string $page = 'home')
{
return '<h1>'.$page.'</h1>';
}
}
當然也以參數開啟其他頁面:
/app/Controllers/Pages.php
<?php
namespace App\Controllers;
class Pages extends BaseController
{
public function index()
{
return view('welcome_message');
}
public function view(string $page = 'home')
{
return view('pages/' . $page);
}
}
這樣,就會打開在/app/Views/pages裡的對應頁面。
/app/Views/pages/home.php
<h1>this is home</h1>
來試試看:
http://localhost:8080/pages
其實等同:
http://localhost:8080/pages/home
我們來試試看,再新增一個view
/app/Views/pages/1.php
<h1>1</h1>
來試試看:
http://localhost:8080/pages/1
因為html裡有很多內容會一直重複 (如:引用bootstrap),官方教學 (靜態頁面 / Static pages)裡建議我們利用header.php及footer.php,還記得我們原本必須每一頁都加上header.php及footer.php嗎? 這裡我們就可以利用Controller套用header.php及footer.php了~
app/Views/templates/header.php
<!doctype html>
<html>
<head>
<title>CodeIgniter Tutorial</title>
</head>
<body>
<h1><?= esc($title) ?></h1>
app/Views/templates/footer.php
<em>© 2022</em>
</body>
</html>
有些架構可以利用Layout套用header及footer,在CodeIgniter裡,可以利用Controller來套用,也可以設定檢視配置 / View Layouts。
我們先利用Controller來套用header及footer:
public function view($page = 'home') {
return view('templates/header')
. view('pages/' . $page)
. view('templates/footer');
}
另外,除了呼叫view之外,也可以傳參數給view,例如,傳一個$data給view。
public function view($page = 'home') {
$data['title'] = ucfirst($page); // Capitalize the first letter
return view('templates/header', $data)
. view('pages/' . $page)
. view('templates/footer');
}
app/Views/templates/header.php
<!doctype html>
<html>
<head>
<title>CodeIgniter Tutorial</title>
</head>
<body>
<h1><?= esc($title) ?></h1>
也要處理一下參數內容(檔案)找不到的問題
完整的程式碼 app/Controllers/Pages.php
<?php
namespace App\Controllers;
class Pages extends BaseController
{
public function index()
{
return view('welcome_message');
}
public function view(string $page = 'home')
{
if (! is_file(APPPATH . 'Views/pages/' . $page . '.php')) {
// Whoops, we don't have a page for that!
throw new PageNotFoundException($page);
}
$data['title'] = ucfirst($page); // Capitalize the first letter
return view('templates/header', $data)
. view('pages/' . $page)
. view('templates/footer');
}
}
這樣就會顯示預設的錯誤頁面。
另外,所有的錯誤都記錄到Log裡面,可以到/writable/logs裡面看log。
我們把bootstrap所需要的style及js加到header.php
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?=esc($title)?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
footer.php
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
改一下
app/Views/pages/home.php
<div class="container">
<h2>Table</h2>
<table class="table table-bordered table-striped">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
</table>
</div>
這樣pages裡的頁面就套用bootstrap了~
資料庫的部分可參考文件裡的範例,這個範例介紹如何利用MVC框架建立一個新聞網站。不過,範例使用的是查詢建構器類別 / Query Builder Class,這個類別讓我們可以不需要寫SQL就可以處理資料庫的CRUD。
其實,也可以使用SQL取得資料,使用方式跟mysqli很類似。