🍰 CakePHP5にTOPページを構築する方法

TOPページ(ホームページ)を設ける前に、MVC(Model-View-Controller)に関する基本設定が必要です。

基本設定
タイムゾーンの設定

% cd ~/www/config
% cp app.php ~/files
% vi app.php
% diff ~/files/app.php app.php
55,56c55,56
<         'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'),
<         'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'UTC'),
---
>         'defaultLocale' => env('APP_DEFAULT_LOCALE', 'ja_JP'),
>         'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'Asia/Tokyo'),
292c292
<             'timezone' => 'UTC',
---
>             'timezone' => 'timezone',
335c335
<             'timezone' => 'UTC',
---
>             'timezone' => 'timezone',
%
                
デバッグ用関数(applog)の用意

当資料ではDebugKitを無効にしているため、デバッグ用の広域関数を用意します。 ~/www/logs/appl.logファイルに記録されます。


% cd ~/www/config
% vi my_define.php
% cat my_define.php
<?php
define('APPLOG', LOGS . 'app.log');  // デバッグ用アプリケーションログ
% vi my_func.php
% cat my_func.php
<?php
function applog($val)
{
    $bktr = debug_backtrace();
    $file = $bktr[0]['file'];
    $line = $bktr[0]['line'];
    error_log(date('Y-m-d H:i:s') . ' ' . $file . ':' . $line . "\n" . var_export($val, true) . "\n", 3, APPLOG);
}
% cp bootstrap.php ~/files
% vi bootstrap.php
% diff ~/files/bootstrap.php bootstrap.php
250a251,256
>
> /*
>  * Global Function
>  */
> include 'my_define.php';
> include 'my_func.php';
%
                
データベース用定義ファイルとテーブルの作成。

DDL(Data Definition Language)ファイル(ddl.sql)を作成し、MySQLで実行します。


% cd ~/db
% vi ddl.sql
% cat ddl.sql
--
-- accountname01_schemename は [コントロールパネル]で作成したデータベース(スキーマ)です。
-- MySQLの場合、データベースとスキーマは同じ意味を持ちます。
--
use `accountname01_schemename`

--
-- クリーンアップ
--
DROP TABLE IF EXISTS `c_copyright`;

--
-- 版権情報
--
CREATE TABLE IF NOT EXISTS `c_copyright` (
    `begin_year`    INT  NOT NULL  COMMENT '開始年',
    `message`       TEXT NOT NULL  COMMENT '版権'
) ENGINE=InnoDB COMMENT '版権情報';

--
-- 初期値
--
INSERT INTO `c_copyright` (`begin_year`, `message`) VALUES
(2026, 'half-of-string.com. All Rights Reserved.');

-- End Of Line
%
                
Bootstrap5用のjsとcssファイルの用意

・~/www/webroot/jsにファイルを追加する。

  bootstrap.bundle.min.js
  bootstrap.esm.min.js
  bootstrap.min.js
  jquery-3.7.1.min.js

・~/www/webroot/cssにファイルを追加する。

  bootstrap-grid.min.css
  bootstrap-grid.rtl.min.css
  bootstrap-reboot.min.css
  bootstrap-reboot.rtl.min.css
  bootstrap-utilities.min.css
  bootstrap-utilities.rtl.min.css
  bootstrap.min.css
  bootstrap.rtl.min.css

・当サイト用cssを用意。

% cd ~/www/webroot/css
% vi my.css
                
MVCの基本ファイルの変更

CakePHPの制約を緩める変更を行います。

  • 性能向上のため、AppModelファイルを用意し、 ORM(Object-Relational Mapping)を使用しない環境を構築します。
  • POSTやGETパラメータを直接使用できるようにします。
  • text/plainのレスポンスも容易にできるメソッドを用意します。
  • 基本情報をセッションに保存します。
  • 当サイトの固有レイアウト(my_default.php)を用意します。

  Model:
    ~/www/src/Model/AppModel.php   // SQLを直接使用できるようにします。
    ~/www/src/Model/BasicModel.php // 当サイトの基本情報をDBから取得します。

  View:
    ~/www/templates/layout/my_default.php

  Controller:
    ~/www/src/Controller/AppController.php

% cd ~/www/src/Model
% vi AppModel.php
% cat AppModel.php
<?php
declare(strict_types=1);

namespace App\Model;

use Cake\Datasource\ConnectionManager;

/**
 * Application Model
 */
class AppModel
{
    private $_config;
    private $_mysqli;

    function __construct() {
	$this->_config = ConnectionManager::get('default')->config();
    }

    public function connect() {

        $this->_mysqli = new \mysqli($this->_config['host'], $this->_config['username'], $this->_config['password'], $this->_config['database']);

        if ($this->_mysqli->connect_error) {
            return $this->_mysqli->connect_error;
        } else {
            $this->_mysqli->set_charset('utf8');
            $this->_mysqli->autocommit(FALSE);
            mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
        }

        return '';
    }

    public function close() {
        $this->_mysqli->close();
    }

    public function begin() {
        $this->_mysqli->begin_transaction();
    }

    public function commit() {
        $this->_mysqli->commit();
    }

    public function rollback() {
        $this->_mysqli->rollback();
    }

    public function esc($val) {
        $str = (gettype($val) == 'string') ? $val : (string)$val;
        return $this->_mysqli->real_escape_string($str);
    }

    public function query($sql) {
        return $this->_mysqli->query($sql);
    }

    public function insert_id() {
        return $this->_mysqli->insert_id;
    }

    public function getRecord($sql) {

        $ans = [];

        if ($result = $this->_mysqli->query($sql)) {
            while ($row = $result->fetch_assoc()) {
                $ans[] = $row;
            }
            $result->close();
        }

        return $ans;
    }
}
%

% cd ~/www/src/Model
% vi BasicModel.php
% cat BasicModel.php
<?php
declare(strict_types=1);

namespace App\Model;

/**
 * Basic Model
 */
class BasicModel extends AppModel
{
    public function getBasicInfo() {

        $this->connect();
        $sql =  "SELECT `begin_year`, `message`"
             . " FROM `c_copyright`"
             ;
        $rec = $this->getRecord($sql);
        $this->close();

        return (empty($rec[0])) ? [] : $rec[0];
    }
}
%

% cd ~/www/templates/layout
% vi my_default.php
% cat my_default.php
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="">
        <title>halh-of-string</title>
        <link href="/css/bootstrap.min.css" rel="stylesheet">
        <link href="/css/my.css?<?=date('Ymdhis')?>" rel="stylesheet">
        <link href="/favicon.ico?<?=date('Ymdhis')?>" rel="shortcut icon">
    </head>
    <body>
        <div class="container text-center" id="my_main">
            <header class="navbar navbar-expand-lg navbar-light bg-light p-1">
                <div class="container-fluid" id="my_nav">
                    <div class="col"><p class="fw-bolder"><?= $header_message ?></p></div>
                </div>
            </header>

            <div style="height: 18px;"> </div>

            <div id="my_content">
                <?= $this->fetch('content') ?>
            </div>

            <div style="height: 18px;"> </div>

            <footer class="d-flex flex-wrap align-items-center justify-content-center" id="my_footer">
<?php
$beginYear  = $basic['begin_year'];
$message    = $basic['message'];
$year = (date('Y') == $beginYear) ? $beginYear : $beginYear . '–' . date('Y');
$msg  = 'Copyright © ' . $year . ' ' . $message;
?>
                <div class="col"><?= $msg ?></div>
            </footer>
        </div>
        <script src="/js/bootstrap.bundle.min.js"></script>
        <script src="/js/jquery-3.7.1.min.js"></script>
    </body>
</html>
%

% cd ~/www/src/Controller
% cp AppController.php ~/files
% vi AppController.php
% cat AppController.php
<?php
declare(strict_types=1);

/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link      https://cakephp.org CakePHP(tm) Project
 * @since     0.2.9
 * @license   https://opensource.org/licenses/mit-license.php MIT License
 */
namespace App\Controller;

use Cake\Controller\Controller;

use Cake\Http\Session;
use Cake\Http\Response;

use App\Model\BasicModel;

/**
 * Application Controller
 *
 * Add your application-wide methods in the class below, your controllers
 * will inherit them.
 *
 * @link https://book.cakephp.org/5/en/controllers.html#the-app-controller
 */
class AppController extends Controller
{
    protected $session;
    protected $get;
    protected $post;

    private $Basic;

    /**
     * Initialization hook method.
     *
     * Use this method to add common initialization code like loading components.
     *
     * e.g. `$this->loadComponent('FormProtection');`
     *
     * @return void
     */
    public function initialize(): void
    {
        parent::initialize();

        $this->Basic = new BasicModel();

        $this->loadComponent('Flash');

        /*
         * Enable the following component for recommended CakePHP form protection settings.
         * see https://book.cakephp.org/5/en/controllers/components/form-protection.html
         */
        //$this->loadComponent('FormProtection');

        $this->session = $this->getRequest()->getSession();

        if ($this->getRequest()->is('get')) {
            $this->get = $this->getRequest()->getQuery();
        } else {
            $this->get = [];
        }

        if ($this->getRequest()->is('post')) {
            $this->post = $this->getRequest()->getData();
        } else {
            $this->post = [];
        }

        $basic = $this->Basic->getBasicInfo();
        $this->session->write('basic', $basic);
        $this->set('basic', $basic);
    }

    public function response(string $str): object
    {
        $response = new Response();
        $response = $response->withType('text/plain');
        $response = $response->withStringBody($str);
        return $response;
    }
}
%
                
Topページ(ホームページ)の用意

以下のMVC3ファイルを作成し、ルーティング(~/www/config/routes.php)を設定する。

  1. Model: ~/www/src/Model/TopModel.php
  2. View: ~/www/templates/Top/index.php
  3. Controller: ~/www/src/Controller/TopController.php
Model: www/src/Model/TopModel.php

% cd ~/www/src/Model
% vi TopModel.php
% cat TopModel.php
<?php
declare(strict_types=1);

namespace App\Model;

/**
 * Top Model
 */
class TopModel extends AppModel
{
    // このメソッドは使っていながサンプルとして用意しています。
    public function getCopyrightInfo() {
        $this->connect();
        $sql =  "SELECT `begin_year`, `message`"
             . " FROM `c_copyright`"
             ;
        $rec = $this->getRecord($sql);
        $this->close();
        return (empty($rec[0])) ? [] : $rec[0];
    }
}
%
                
View: ~/www/templates/Top/index.php

% cd ~/www/templates
% mkdir Top
% cd Top
% vi index.php
% cat index.php
<div class="row">
    <div class="col"></div>
    <div class="col-auto">
        <div>
            <h5><a href="#" class="link-dark">half-of-string.com</a>はシステムエンジニア(System Engineer)の為になるサイトを目指しています。</h5>
        </div>
        <div style="height: 20px;"> </div>

..... 以下、省略 .....

%
                
Controller: ~/www/src/Controller/TopController.php

% cd ~/www/src/Controller
% vi TopController.php
% cat TopController.php
<?php
declare(strict_types=1);

namespace App\Controller;

use App\Model\TopModel;

/**
 * Top Controller
 */
class TopController extends AppController
{
    private $_top;

    public function initialize(): void
    {
        parent::initialize();

        $this->_top = new TopModel();
    }

    public function index()
    {
        $this->set('header_message', 'half-of-string.com は SE支援サイト です。');
        //$copyright = $this->_top->getCopyrightInfo();
        //applog($copyright);
        //$this->session->write('copyright', $copyright);
    }
}
%
                
ルーティング設定

% cd ~/www/config
% cp routes.php ~/files
% vi routes.php
% diff ~/files/routes.php routes.php
58c58
<         $builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
---
>         $builder->connect('/', ['controller' => 'Top', 'action' => 'index']);
%
                
Topページ(ホームページ)の確認

WEBブラウザで確認。https://half-of-string.com

CakephpApps_001.png
 
🔝