Category Archive CodeIgniter

IIS: การตั้ง Virtual Directory

iis เดิมมีเว็บที่เป็น c# อยู่แล้วและไม่สะดวกที่จะเพิ่ม domain name เข้าไปใหม่ จะตั้งเป็น site ให้ใช้อีก port ก็ติด firewall ที่ยอมให้ใช้ port 80 เท่านั้น และเพื่อความปลอดภัย framework อย่าง laravel ไม่ควรวาง folder ให้สามารถเรียกใช้ php ไฟล์อื่นได้นอกจาก /public/index.php จึงเลือกที่ใช้วิธี Virtual Directory คือ การสร้าง folder ใน url ให้ชี้ไปอีก folder ที่อยู่นอก C:\inetpub\wwwroot

การตั้ง Virtual Directory

  1. เปิดโปรแกรม Internet Information Services (IIS) Manager
  2. คลิกขวาที่ site ที่ต้องการ เช่น Default Web Site
  3. Add Virtual Directory
    Alias
    คือ folder จำลอง เช่น punpun_uat
    Physical path:
    คือ path จริง ที่ทำ shortcut ชี้ไปหา เช่น C:\www\punpun_v1_uat
  4. (ไม่จำเป็น) คลิกที่ site หรือ server แล้ว restart
  5. ทดสอบโดยเรียก url เช่น http://localhost/punpun_uat

วิธีนี้จะมีข้อดีคือ

  1. สามารถติดตั้งหลายเว็บ โดยใช้ domain และ port เดียวกันได้
  2. สามารถควบคุมการเข้าถึงไฟล์ที่ไม่ต้องการได้เช่น ถ้าวาง file ของ larave ไว้ใน C:\inetpub\wwwroot ตามปกติ ผู้ไม่หวังดีสามารถเรียกใช้ไฟล์อื่นๆ นอกจากไฟล์ /public/index.php ได้โดยตรงเช่น ไฟล์ php ที่สามารถ upload ไฟล์ได้โดยไม่มีการตรวจสอบสิทธิทำให้ไม่ปลอดภัย
  3. สามารถรัน code เดียวกันโดยใช้ php หลาย version หรือมี config คนละแบบได้โดยใช้วิธี การติดตั้ง PHP หลายเวอร์ชั่น บน IIS
  4. ซ่อน หรือย้าย folder ที่เก็บงานไปไว้ไดร์อื่น

ERR_CONTENT_DECODING_FAILED

ไปเปิด option บีบอัดข้อมูลใน CodeIgniter [code language=”php” title=”\application\config\config.php”]$config[‘compress_output’] = true;[/code]
หลังเปิดเว็บก็ดูปกติดี จนเกือบจะเลิกเทสไปละ จนมาเจอว่า javaScript ที่เคยใช้ได้ปกติกับมี error เปิดไฟล์ออกมาดูใน chrome มันขึ้น error

หน้าเว็บใน http://xxx อาจหยุดให้บริการชั่วคราวหรืออาจถูกย้ายไปยังที่อยู่เว็บใหม่อย่างถาวร
ERR_CONTENT_DECODING_FAILED

เจอว่าเกิดได้จากหลายสาเหตุมากแต่เพราะเพิ่งไปเปิด zlib มาเลยเน้นมันเป็นพิเศษ จนเจอว่าไปแก้ php.ini เปลี่ยน[code language=”php” title=”php.ini”]zlib.output_compression = On[/code]ก็แก้ได้แล้ว

เวลาเขียน program อะไรเล็กๆ น้อยๆ ก็ทำให้ระบบเปลี่ยนแปลงได้เสมอ

Fixing ERR_CONTENT_DECODING_FAILED in Apache+PHP

CodeIgniter: php-amqplib

ลองเขียน custom driver สำหรับติดต่อกับ RabbitMQ / AMQP ยังไม่สมบูรณ์แต่ก็ดีกว่าการที่ต้องมา connect ผ่าน php-amqplib ทุกครั้งที่จะใช้ตามปกติ

  1. config file ก่อนตามระเบียบ[code language=”php” title=”\application\config\RabbitMQ.php”]<?php
    defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

    /*
    | ——————————————————————-
    | RABBITMQ CONNECTIVITY SETTINGS
    | ——————————————————————-
    | This file will contain the settings needed to access your RabbitMQ.
    |
    | For complete instructions please consult the ‘RabbitMQ Connection’
    | page of the User Guide.
    |
    | ——————————————————————-
    | EXPLANATION OF VARIABLES
    | ——————————————————————-
    |
    | [‘connection_timeout’] => 3.0,
    | [‘context’] => null,
    | [‘heartbeat’] => 0
    | [‘host’],
    | [‘insist’] => false,
    | [‘keepalive’] => false,
    | [‘locale’] => ‘en_US’,
    | [‘login_method’] => ‘AMQPLAIN’,
    | [‘login_response’] => null,
    | [‘password’],
    | [‘port’],
    | [‘read_write_timeout’] => 3.0,
    | [‘user’],
    | [‘vhost’] => ‘/’,
    */
    $config[‘RabbitMQ’][‘connects’] = [
    ‘default’ => [
    ‘connection_timeout’ => 3.0,
    ‘context’ => null,
    ‘heartbeat’ => 0,
    ‘host’ => ‘127.0.0.1’,
    ‘insist’ => false,
    ‘keepalive’ => false,
    ‘locale’ => ‘en_US’,
    ‘login_method’ => ‘AMQPLAIN’,
    ‘login_response’ => null,
    ‘password’ => ‘guest’,
    ‘port’ => 5672,
    ‘read_write_timeout’ => 3.0,
    ‘user’ => ‘guest’,
    ‘vhost’ => ‘/’,
    ],
    ];

    /*
    | ——————————————————————-
    | RABBITMQ DEBUG SETTINGS
    | ——————————————————————-
    |
    */
    $config[‘RabbitMQ’][‘debug’] = false;
    [/code]

  2. ถึงคิวไฟล์ driver ที่จะเป็นตัวกลางระหว่าง codeigniter และ php-amqplib[code language=”php” title=”\application\libraries\RabbitMQ\RabbitMQ.php”]<?php
    use PhpAmqpLib\Connection\AMQPStreamConnection;
    use PhpAmqpLib\Message\AMQPMessage;

    class RabbitMQ extends AMQPStreamConnection
    {

    public function __construct($config = array())
    {
    $this->CI = &get_instance();

    $this->config = $config[‘RabbitMQ’];

    if ($this->config[‘debug’] == true) {
    define(‘AMQP_DEBUG’, true);
    }
    }

    public function connect($dsnId = ‘default’) {
    $dsn = $this->config[‘connects’][$dsnId];

    $this->connection = new AMQPStreamConnection($dsn[‘host’], $dsn[‘port’], $dsn[‘user’], $dsn[‘password’], $dsn[‘vhost’], $dsn[‘insist’], $dsn[‘login_method’], $dsn[‘login_response’], $dsn[‘locale’], $dsn[‘connection_timeout’], $dsn[‘read_write_timeout’], $dsn[‘context’], $dsn[‘keepalive’], $dsn[‘heartbeat’]);

    $this->channel = $this->connection->channel();
    }

    public function disconnect()
    {
    $this->channel->close();
    $this->connection->close();
    }

    public function setMessageJson($datas)
    {
    $msg_body = json_encode($datas);

    $properties = [
    ‘content_type’ => ‘application/json’,
    ‘delivery_mode’ => AMQPMessage::DELIVERY_MODE_PERSISTENT,
    ];

    return new AMQPMessage($msg_body, $properties);
    }

    }[/code]

  3. ที่นี้ก็ controller ที่จะทำงานให้[code language=”php” title=”\application\controllers\Messaging.php”]<?php
    defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

    class Messaging extends CI_Controller
    {
    public function __construct()
    {
    parent::__construct();

    $this->load->driver(‘RabbitMQ’);
    }

    public function receive()
    {
    set_time_limit(0);

    $this->rabbitmq->connect();

    $exchange_name = ‘customers’;
    $queue_name = ‘invoices’;

    /**
    * Declares exchange
    *
    * @param string $exchange_name
    * @param string $type
    * @param bool $passive
    * @param bool $durable
    * @param bool $auto_delete
    * @param bool $internal
    * @param bool $nowait
    * @param array $arguments
    * @param int $ticket
    * @return mixed|null
    */
    $this->rabbitmq->channel->exchange_declare($exchange_name, ‘fanout’, false, true, false);

    /**
    * Declares queue, creates if needed
    *
    * @param string $queue
    * @param bool $passive
    * @param bool $durable
    * @param bool $exclusive
    * @param bool $auto_delete
    * @param bool $nowait
    * @param array $arguments
    * @param int $ticket
    * @return mixed|null
    */
    list($queueName, $message_count, $consumer_count) = $this->rabbitmq->channel->queue_declare(”, false, false, true, false);
    $this->rabbitmq->channel->queue_bind($queue_name, $exchange_name);

    $callback = function ($msg) {

    $datas = json_decode($msg->body, true);
    fwrite(fopen(‘RabbitMQReceive.txt’, ‘a+’), print_r($datas, true));

    sleep(substr_count($msg->body, ‘.’));

    /* delete message */
    $msg->delivery_info[‘channel’]->basic_ack($msg->delivery_info[‘delivery_tag’]);
    };

    /**
    * Starts a queue consumer
    *
    * @param string $queue_name
    * @param string $consumer_tag
    * @param bool $no_local
    * @param bool $no_ack
    * @param bool $exclusive
    * @param bool $nowait
    * @param callback|null $callback
    * @param int|null $ticket
    * @param array $arguments
    * @return mixed|string
    */
    $this->rabbitmq->channel->basic_consume($queue_name, ”, false, false, false, false, $callback);

    while (count($this->rabbitmq->channel->callbacks)) {
    /**
    * Wait for some expected AMQP methods and dispatch to them.
    * Unexpected methods are queued up for later calls to this PHP
    * method.
    *
    * @param array $allowed_methods
    * @param bool $non_blocking
    * @param int $timeout
    * @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
    * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
    * @return mixed
    */
    $this->rabbitmq->channel->wait();
    }

    $this->rabbitmq->channel->close();
    $connection->close();
    }

    public function send()
    {
    $this->rabbitmq->connect();

    $exchange_name = ‘customers’;
    $queue_name = ‘invoices’;

    /**
    * Declares exchange
    *
    * @param string $exchange_name
    * @param string $type
    * @param bool $passive
    * @param bool $durable
    * @param bool $auto_delete
    * @param bool $internal
    * @param bool $nowait
    * @param array $arguments
    * @param int $ticket
    * @return mixed|null
    */
    $this->rabbitmq->channel->exchange_declare($exchange_name, ‘fanout’, false, true, false);

    /**
    * Declares queue, creates if needed
    *
    * @param string $queue
    * @param bool $passive
    * @param bool $durable
    * @param bool $exclusive
    * @param bool $auto_delete
    * @param bool $nowait
    * @param array $arguments
    * @param int $ticket
    * @return mixed|null
    */
    list($queueName, $message_count, $consumer_count) = $this->rabbitmq->channel->queue_declare($queue_name, false, true, false, false);

    $datas = [
    ‘rand’ => rand(0, 100),
    ‘time’ => date(‘Y-m-d H:m:s’),
    ];

    /**
    * Declares message
    *
    * @param array $datas
    * @return string
    */
    $msg = $this->rabbitmq->setMessageJson($datas);

    /**
    * Publishes a message
    *
    * @param AMQPMessage $msg
    * @param string $exchange
    * @param string $routing_key
    * @param bool $mandatory
    * @param bool $immediate
    * @param int $ticket
    */
    $this->rabbitmq->channel->basic_publish($msg, $exchange_name, $queue_name);

    echo ‘<br><pre>’ . print_r($datas, true), ‘</pre>’;

    $this->rabbitmq->disconnect();

    }

    }
    [/code]

  4. ทดสอบโดยเรียก http://localhost/CodeIgniter-3.1.3/messaging/send และ http://localhost/CodeIgniter-3.1.3/messaging/receive

CodeIgniter: Composer

โดยปกติ composer จะเก็บไฟล์โดยสร้าง folder vendor ใน folder เดียวกับไฟล์ composer.json

ใน codeinigter ว่างไฟล์ composer.json ไว้นอกสุดแต่ในไฟล์ \application\config\config.php กลับเขียน[code language=”php” title=”\application\config\config.php”]/*
|————————————————————————–
| Composer auto-loading
|————————————————————————–
|
| Enabling this setting will tell CodeIgniter to look for a Composer
| package auto-loader script in application/vendor/autoload.php.
|
| $config[‘composer_autoload’] = TRUE;
|
| Or if you have your vendor/ directory located somewhere else, you
| can opt to set a specific path as well:
|
| $config[‘composer_autoload’] = ‘/path/to/vendor/autoload.php’;
|
| For more information about Composer, please visit http://getcomposer.org/
|
| Note: This will NOT disable or override the CodeIgniter-specific
| autoloading (application/config/autoload.php)
*/[/code]คือ พี่แกให้เก็บไฟล์ไว้ที่ application/vendor ไม่ใช่ /vendor

ปัญหานี้แก้ได้โดยเพิ่ม[code language=”javascript” title=”composer.json”]{

"config": {
"vendor-dir": "application/vendor"
},

}[/code]หลังจากนั้นก็ไปเปิด auto-load โดยเปลี่ยน[code language=”php” title=”\application\config\config.php”]$config[‘composer_autoload’] = false;[/code]เป็น[code language=”php” title=”\application\config\config.php”]$config[‘composer_autoload’] = true;[/code]แล้วทดลองใช้ command[code language=”text” title=”command”]composer update[/code]จะเห็นว่าไฟล์เข้าไปที่ application/vendor แล้ว

ถ้า composer โหลดไฟล์มาไม่ได้เพราะติด proxy อ่าน

อ่านเพิ่มเติม

CodeIgniter: Cache

เพื่อให้ผู้ใช้เข้าหน้าเพจด้วยความรวดเร็วจึงต้องทำ caching เก็บส่วนที่ไม่ได้เปลี่ยนแปลงบ่อย และสามารถใช้ซ่้ำๆ ได้

เริ่มจากสร้างไฟล์เก็บ config ขึ้นมาก่อน[code language=”php” title=”\application\config\cache.php”]<?php
defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

$config[‘caching’][‘adapter’] = ‘memcached’;
$config[‘caching’][‘backup’] = ‘file’;[/code]โดย

adapter
คือ จะเลือกใช้ driver ตัวไหนในการทำ cache ระหว่าง

Drivers Value
Alternative PHP Cache (APC) Caching apc
Dummy Cache dummy
File-based Caching file
Memcached Caching memcached
Redis Caching redis
WinCache Caching wincache
backup
เป็นตัวเลือกสำรองถ้าตัวแรกไม่สามารถทำงานได้ เช่น เครื่องที่เราใช้ไม่ได้ลง Memcached ไว้ ก็จะใช้ file แทน ( cache file ถูกเก็บไว้ที่ \application\cache หรือ ตาม config $config[‘cache_path’])

โหลด Driver ได้ 2 วิธีคือ

Auto-loading
เหมาะกับงานที่ต้องใช้ cache บ่อยๆ ทำได้โดยเพิ่ม ‘cache’ เข้าไป[code language=”php” title=”\application\config\autoload.php”]$autoload[‘drivers’] = […, ‘cache’, …];[/code]
load driver
โหลด driver เฉพาะ controller ที่ต้องใช้ cache เท่านั้น[code language=”php” title=”\application\controllers\Welcome.php”] public function __construct()
{
parent::__construct();

$this->load->driver(‘cache’);
}[/code]

การใช้งาน[code language=”php” title=”\application\controllers\Welcome.php”]<?php
defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

class Welcome extends CI_Controller
{
/**
* Index Page for this controller.
*
* Maps to the following URL
* http://example.com/index.php/welcome
* – or –
* http://example.com/index.php/welcome/index
* – or –
* Since this controller is set as the default controller in
* config/routes.php, it’s displayed at http://example.com/
*
* So any other public methods not prefixed with an underscore will
* map to /index.php/welcome/<method_name>
* @see https://codeigniter.com/user_guide/general/urls.html
*/
public function index()
{
$cacheKey = ‘home’ . date(‘Y-m-d’); /* unque id */
$cacheKey = mb_ereg_replace(‘([^\w\s\d\-_~,;\[\]\(\).])’, ”, $cacheKey);

if (!$datas = $this->cache->get($cacheKey)) {

$datas = $this->load->view(‘welcome_message’, ”, true);

$this->cache->save($cacheKey, $datas, 300);
}

echo $datas;
}

}[/code] เพราะว่า file driver เป็นการเขียนไฟล์ลงบน hard disk จริงๆ จึงควรแน่ใจว่าไฟล์ถูกรูปแบบที่ os สามารถเขียนไฟล์ได้ โดนอาจจะใช้ md5 เข้ารหัส $cacheKey ก่อน[code language=”php”]$cacheKey = md5($cacheKey);[/code] หรือใช้ [code language=”php”]$cacheKey = mb_ereg_replace(‘([^\w\s\d\-_~,;\[\]\(\).])’, ”, $cacheKey);[/code]

อ่านเพิ่มเติม

CodeIgniter: แยก config

การดึงค่าคอนฟิกของโค้ทอินิกเตอร์สามารถทำได้โดย ใช้คำสั่ง[code language=”php”]$this->config->item(‘ ตัวแปร ‘);[/code] โดยจะดึงมาจากไฟล์ \application\config\config.php เป็นหลัก แต่ถ้าต้องการแยก config ออกมาเป็นส่วนๆ เพื่อความง่ายในการจัดการ สามารถสร้างไฟล์ ขึ้นมาใน application\config ได้[code language=”php” title=”\application\config\caching.php”]<?php
defined(‘BASEPATH’) OR exit(‘No direct script access allowed’);

$config[‘caching’][‘adapter’] = ‘memcached’;
$config[‘caching’][‘backup’] = ‘file’;[/code]

จากนั้นเราจะอ่าน configuration เข้ามาในระบบของเราสามารถทำได้ 2 วิธีคือ

  1. เป็นค่าที่เราต้องใช้บ่อยๆแทบทุกหน้า เพิ่มเข้าไปใน Auto-loading เปิดไฟล์ \application\config\autoload.php เพิ่มชื่อไฟล์ลงไปใน[code language=”php” title=”\application\config\autoload.php”]$autoload[‘config’][/code]เช่น[code language=”php” title=”\application\config\autoload.php”]…
    $autoload[‘config’] = [‘caching’];
    …[/code]
  2. ถ้าต้องการใช้เฉพาะบางไฟล์ ทำได้โดยการ load ใน controller ที่ต้องการโดยใช้คำสั่ง[code language=”php”]

    $this->config->load(‘caching’);
    …[/code]

ผมใช้หน้าแรกแสดงตัวอย่างการใช้[code language=”php” title=”\application\controllers\Welcome.php”]<?php
defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

class Welcome extends CI_Controller
{
public function __construct()
{
parent::__construct();

echo ‘charset = ‘ . $this->config->item(‘charset’);

//$this->config->load(‘caching’);

echo ‘<pre>’, print_r($this->config->item(‘caching’), true), ‘</pre>’;
}
…[/code]

อ่านเพิ่มเติม

Custom Rules Validation

ตัวอย่างการเขียน custom rule validation[code language=”php” title=”\application\controllers\FormValidationCustomRules.php”]<?php
class FormValidationCustomRules extends CI_Controller
{

public function __construct()
{
parent::__construct();
// Your own constructor code
}

public function uploadJPEG()
{
$this->form_validation->set_rules(‘cover’, ‘JPEG Upload’, ‘trim|xss_clean’);

if ($this->form_validation->run() == true) {
$fileDir = ‘assets/contents/’;
$fileName = date(‘U’) . ‘.jpeg’;

$config = [
‘allowed_types’ => ‘jfi|jfif|jif|jpe|jpeg|jpg’,
‘file_ext_tolower’ => true,
‘file_name’ => $fileName,
‘max_size’ => ‘10000’,
‘overwrite’ => false,
‘upload_path’ => $fileDir,
];

$this->load->library(‘upload’, $config);

if ($this->upload->do_upload(‘cover’, false)) {
$this->uploadFile = $fileDir . $fileName;
return true;
} else {
$this->form_validation->set_message(‘uploadJPEG’, $data[‘error’] = $this->upload->display_errors());

if ($_FILES[‘cover’][‘error’] != 4) {
return false;
}
}
}

return false;
}

public function index()
{
$this->load->library(‘form_validation’);

$formValidation = [
[
‘field’ => ‘cover’,
‘label’ => ‘JPEG Upload’,
‘rules’ => ‘callback_uploadJPEG’,
],
];
$this->form_validation->set_rules($formValidation);

if ($this->form_validation->run() == true) {
echo ‘upload file to ‘, $this->uploadFile, ‘<br><img alt="’, $this->uploadFile, ‘" class="img-thumbnail" src="../’, $this->uploadFile, ‘">’;
}

$this->load->view(‘FormValidation/CustomRules’);
}
}
[/code]

หลักการคือ

  • สร้าง function ขึ้นมา แล้วเรียกใช้โดยมี callback_ นำหน้า
  • ส่ง message กลับมาโดยใช้ $this->form_validation->set_message(‘function name’,’ข้อความ’)

config codeigniter by environments

ที่ทำงานมี process ในการพัฒนา development > test > production โดยแต่ละ server จะมี config และ database แยกออกจากกันเพื่อความปลอดภัย โดย codeigniter สามารถแยก config ได้โดยการเปลี่ยน Environments ซึ่งโดยปกติจะมี 3 สภาพแวดล้อมคือ development, production, testing และแล้วแต่ท่านจะตั้ง

การทำระบบ config สามารถทำได้โดย

  1. ได้โดยเปิดไฟล์ \.htaccess เพิ่ม [code language=”text” title=”.htaccess”]…
    SetEnv CI_ENV development
    …[/code]เข้าไป หรือเข้าไปแก้ไฟล์ \index.php แก้บรรทัด[code language=”php” title=”index.php”]define(‘ENVIRONMENT’, isset($_SERVER[‘CI_ENV’]) ? $_SERVER[‘CI_ENV’] : ‘development’);[/code]เป็น[code language=”php” title=”index.php”]define(‘ENVIRONMENT’, ‘development’);[/code]
  2. ไปที่ \application\config สร้าง folder ไว้เก็บ configuration ตามชื่อ Environments เช่น \application\config\development, \application\config\production, \application\config\testing
  3. copy config ไฟล์ มาจาก \application\config จากนั้นก็ลบข้อมูลเดิมออกแล้วใส่เฉพาะส่วนที่แตกต่างกันเช่น \application\config\development\constants.php[code language=”php” title=”pplicationconfigdevelopmentconstants.php”]<?php
    defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

    define(‘TITLE’, ‘development’);
    [/code]ทำแบบเดียวกันกับไฟล์อื่นๆ ถ้ามี config ที่ต่างกัน เช่น \application\config\development\database.php

  4. ทดสอบโดยเปิดไฟล์ \application\views\welcome_message.php แก้เป็น[code language=”php”]<?php
    defined(‘BASEPATH’) or exit(‘No direct script access allowed’);
    ?><!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title><?=TITLE;?></title>

    <style type="text/css">

    ::selection { background-color: #E13300; color: white; }
    ::-moz-selection { background-color: #E13300; color: white; }

    body {
    background-color: #fff;
    margin: 40px;
    font: 13px/20px normal Helvetica, Arial, sans-serif;
    color: #4F5155;
    }

    a {
    color: #003399;
    background-color: transparent;
    font-weight: normal;
    }

    h1 {
    color: #444;
    background-color: transparent;
    border-bottom: 1px solid #D0D0D0;
    font-size: 19px;
    font-weight: normal;
    margin: 0 0 14px 0;
    padding: 14px 15px 10px 15px;
    }

    code {
    font-family: Consolas, Monaco, Courier New, Courier, monospace;
    font-size: 12px;
    background-color: #f9f9f9;
    border: 1px solid #D0D0D0;
    color: #002166;
    display: block;
    margin: 14px 0 14px 0;
    padding: 12px 10px 12px 10px;
    }

    #body {
    margin: 0 15px 0 15px;
    }

    p.footer {
    text-align: right;
    font-size: 11px;
    border-top: 1px solid #D0D0D0;
    line-height: 32px;
    padding: 0 10px 0 10px;
    margin: 20px 0 0 0;
    }

    #container {
    margin: 10px;
    border: 1px solid #D0D0D0;
    box-shadow: 0 0 8px #D0D0D0;
    }
    </style>
    </head>
    <body>

    <div id="container">
    <h1>Welcome to CodeIgniter!</h1>

    <div id="body">
    <?php
    echo ‘<br>ENVIRONMENT = ‘ . ENVIRONMENT;
    echo ‘<br>FILE_READ_MODE = ‘ . FILE_READ_MODE;
    echo ‘<br>title = ‘ . TITLE;
    ?>
    </div>

    <p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo (ENVIRONMENT === ‘development’) ? ‘CodeIgniter Version <strong>’ . CI_VERSION . ‘</strong>’ : ”; ?></p>
    </div>

    </body>
    </html>[/code]

  5. เปิดหน้าแรกและสังเกตผลลัพธ์

ค่าในไฟล์ config จะถูกเพิ่มจากของเดิมโดยเลือกมาจากไฟล์ใน folder ที่ชื่อเดียวกับ environments ไม่ใช่แทนที่ของเดิมสังเกตได้จากค่า FILE_READ_MODE ที่ไม่มีในไฟล์ \application\config\development\constants.php ที่เราแยกออกมา

Codeigniter reused active record query

ในการเขียน query บางครั้งก็ต้องเขียนคิวรี่ที่ต่างกันเล็กๆ น้อยๆ เช่น ต้องแบ่ง list ข้อมูลออกเป็นหน้าๆ หรือที่เรียกกันว่า pagination จะมี 2 query ที่ต้องใช้คือ query ที่นับ ข้อมูลที่มีทั้งหมด[code language=”sql” title=”record count”]SELECT `d`.`district_id`,
`d`.`enable`,
`d`.`district_code`,
`d`.`district_name`,
`p`.`province_name`
FROM `district` AS `d`
LEFT JOIN `province` AS `p`
ON `d`.`province_id` = `p`.`province_id`[/code] และ query ที่ดึงข้อมูลเฉพาะช่วงที่กำหนด [code language=”sql” title=”results”]SELECT `d`.`district_id`,
`d`.`enable`,
`d`.`district_code`,
`d`.`district_name`,
`p`.`province_name`
FROM `district` AS `d`
LEFT JOIN `province` AS `p`
ON `d`.`province_id` = `p`.`province_id`
LIMIT 0, 10[/code]จะเห็นว่า ต่างกันแค่ LIMIT 0, 10 เท่านั้น แต่เพราะว่า ไม่ควร query ข้อมูล ที่ไม่ได้ใช้ จึงเปลี่ยน [code language=”sql” title=”record count”]SELECT COUNT(district_id) AS recordsTotal
FROM `district` AS `d`
LEFT JOIN `province` AS `p`
ON `d`.`province_id` = `p`.`province_id`[/code] แก้ select ให้เป็น SELECT COUNT(DISTRICT_ID) AS recordsTotal แทนที่จะเลือกหลายๆ column หลาย row มาเพื่อแค่จะนับว่ามีข้อมูลกี่ record แค่นั้นเอง

ยังยุ่งไม่พอ เมื่อทำเป็น data table หรือ grid จะมีเงื่อนไขอย่างเช่น filter ข้อมูล หรือ order ข้อมูลใหม่ตาม column ต่างๆ ที่ user ปลับเปลี่ยนเวลาใช้งาน ถ้าต้องเขียนเงื่อนใขเพิ่ม ถ้าเป็น query string อย่าง DISTRICT_ID = ‘xxx’ ก็พอจะเก็บในตัวแปรแล้วเอามาต่อกันได้ แต่ถ้าเขียนเป็น active record ก็ลำบากมากที่ต้องมาเขียนใหม่หลายๆจุด แต่ Codeigniter มีวิธี reused active record มาใช้ใหม่ได้ลดความซับซ้อนลงไปได้เยอะเลย

วิธีการคือเขียน query ส่วนที่ใช้ ร่วมกัน ระหว่าง ->start_cache(); และ ->stop_cache(); จากนั้นเขียน ส่วนอื่นเพิ่มเข้าไป เป็นวิธีที่ฉลาดมากๆ

[code language=”sql” title=”/application/models/DatabasecachingModel.php”]<?php

class DatabasecachingModel extends CI_Model
{

public function __construct()
{
// Call the CI_Model constructor
parent::__construct();

$this->db = $this->load->database(‘db’, true);
}

public function getPagination()
{
$post = $this->input->post();

// Turn caching on
$this->db->start_cache();

$this->db->from(‘district AS d’);
$this->db->join(‘province AS p’, ‘d.PROVINCE_ID = p.PROVINCE_ID’, ‘left’);

if (isset($post[‘DISTRICT_ID’])) {
$this->db->where(‘DISTRICT_ID’, $post[‘DISTRICT_ID’]);
}

if (isset($post[‘PROVINCE_ID’])) {
$this->db->where(‘PROVINCE_ID’, $post[‘PROVINCE_ID’]);
}

$this->db->stop_cache();

$this->db->select(‘COUNT(DISTRICT_ID) AS recordsTotal’);
$recordsTotal = $this->db->get()->row()->recordsTotal;

echo ‘<br><br> query string for count = ‘ . $this->db->last_query();
echo ‘<br> records total = ‘ . $recordsTotal;

$this->db->select(‘d.DISTRICT_ID, d.enable, d.DISTRICT_CODE, d.DISTRICT_NAME, p.province_NAME’);
$this->db->limit(10, 0);
$results = $this->db->get()->result_array();

echo ‘<br><br> query string for results = ‘ . $this->db->last_query();
echo ‘<pre>’, print_r($results, true), ‘<pre>’;

}

}[/code]

เก็บ CodeIgniter log ด้วย hook

วิธีที่จะพัฒนาและดูแลเว็บคือทำระบบ log ที่จะบันทึกข้อมูลการใช้งาน ซึ่งจะบันทึกสิ่งที่เกิดขึ้นไว้สำหรับเข้ามาดูเหตุการณ์ที่เกิดขึ้นย้อนหลังได้ มีประโยชน์ในการดูแลระบบโดยเฉพาะตัวที่เป็น API ให้ระบบอื่นๆเรียกใช้ สำหรับ ci เราสามารถเขียนได้โดยการใช้ hook

ระบบ Hook (ตะขอ) คือ เป็นตะขอที่เกี่ยวกับเหตุการณ์ซักอย่างแล้วจึงทำงาน เหมือนกับ tricker ในดาต้าเบส หรือจะ อธิบายการทำงานง่ายๆ ก็เหมือน คุณไปที่ร้านอาหารคิดไม่ออกว่าจะทานอะไร ใช้วิธีรอให้เพื่อนสั่งแล้วบอกว่า หมีทู่ (me too.) นั้นละครับ

  1. ก่อนอื่น ไปเปิดการใช้งาน hook ก่อน เปิดไฟล์ /application /config/config.php แก้ $config[‘enable_hooks’] = FALSE; เป็น TRUE;
  2. เปิดไฟล์ /application /config/ hooks.php เพิ่มบรรทัด
    [sourcecode language=”php”]
    $hook[‘post_system’][] = array(
    ‘class’ => ”,
    ‘function’ => ‘log_Profiling’,
    ‘filename’ => ‘log_Profiling.php’,
    ‘filepath’ => ‘hooks’
    );
    [/sourcecode]
  3. จากนั้นไปสร้างไฟล์ log_Profiling.php ใน /application/hooks เนื้อหาตามนี้ครับ
    [sourcecode language=”php”]
    <?php
    function log_Profiling(){
    global $CI ,$application_folder;
    $output = ‘<html><body>’;
    $output .= ‘<fieldset id="ci_profiler_benchmarks" style="border:1px solid #c00000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee">
    <legend style="color:#c00000;">OUTPUT</legend>’.$CI->output->get_output().'</fieldset>’;
    if( ! isset($_POST[‘profiler’])){
    $CI->load->library(‘profiler’);
    if ( ! empty($CI->_profiler_sections)){
    $CI->profiler->set_sections($this->_profiler_sections);
    }
    $output .= $CI->profiler->run();
    }
    $output .= ‘</body></html>’;
    $fp = fopen($application_folder.’/logs/Profiling/’.date(‘Y-m-d-H-i-s-U’).’_’.$CI->router->fetch_class().’_’.$CI->router->fetch_method().’.html’ ,’w’);
    flock($fp ,LOCK_EX);
    fwrite($fp ,$output);
    flock($fp ,LOCK_UN);
    fclose($fp);
    }
    [/sourcecode]
  4. สร้างโฟลเดอร์ application/logs/Profiling/