PHP : CodeIgniter Framework - Template Parser Class
สำหรับผลลัพธ์เมื่อสั่ง
$this->parser->parse('test_parser_view', $data);
ก็จะเกิดการแทรกข้อมูลในอาร์เรย์ $data ลงไปยังไฟล์ View ที่ระบุ และแสดงผลแบบ Loop แถวของตารางตามจำนวนอาร์เรย์ blog_entries และ data_list โดยอัตโนมัติ
จะเห็นว่าการใช้ Template Engine ก็ช่วยให้การเขียนโค้ดง่ายขึ้นได้เช่นกัน (แต่ก็จะต้องจัดรูปแบบข้อมูลให้เรียบร้อยใน Controller ก่อนจะนำมาแทรกลงใน Template ซึ่งใน PHP CI MANIA จะมีการสร้างฟังก์ชั่นเหล่านี้โดยอัตโนมัติ เช่น จัดรูปแบบวันที่ภาษาไทย จัดรูปแบบตัวเลข เป็นต้น)
ทุกอย่างก็ดูสะดวกสบายใช้ง่ายดีนี่นา แล้วทำไมถึงอยากจะปรับแต่งฟังก์ชั่นให้กับ Template Parser Class ด้วยล่ะ??
ซึ่งถ้าได้ติดตามบล็อกของผมมาโดยตลอด จะเห็นว่าก่อนหน้านี้ก็มีการปรับแต่งไปแล้วครั้งหนึ่งในบทความ PHP Codeigniter กับการใช้งาน Template Parser Class แล้วไม่สามารถเรียกใช้ตัวแปรด้านนอก Variable Pairs ที่กำหนดไว้ได้ ซึ่งมีปัญหาว่าในลูปไม่สามารถดึงค่าตัวแปร url ของหน้าเว็บจากอารเรย์ชั้นข้างนอกได้
หลังจากปรับแต่งก็ใช้วิธีใส่วงเล็บปีกกาซ้อนกัน 2 ชั้นเพื่อเรียกใช้ตัวแปรด้านนอกแทน
จะเห็นว่าเวลาในโหมด Preview เพื่อดูไฟล์ View ในตัวอย่างโค้ดด้านบนนั้น จะมีข้อความส่วนที่ผมไม่อยากให้แสดงออกมาโผล่ออกมาด้วย
ซึ่งในส่วนของการรับค่าอาร์เรย์ data_list ยังพอทำใจได้เพราะมันครอบ <li> เอาไว้
แต่ในส่วนของตาราง <table> จะมีวงเล็บ {blog_entries} ครอบแถวเอาไว้ พอมันครอบ <tr> แต่มันไม่ใช่แท็ก HTML ของ <table> มันก็เลยถูกเขี่ยกระเด็นออกไปด้านบนซะงั้น
นอกจากจะรกเกะกะสายตาแล้ว ยังทำให้ตำแหน่งตารางผิดไปจากตำแหน่งจริงอีกด้วย
และในภาพด้านบนนี้ ก็คือตัวอย่างการแสดงผลในโหมด Preview ที่กระผมต้องการนั่นเอง จะเห็นว่าส่วนของบล็อกที่ใช้รับค่า {data_list} และ {blog_entries} ได้หายไปแล้ว เพราะผมได้เอาไปซ่อนไว้ใน Attribute ของอีเลเมนต์ที่ต้องการวนลูปไปแล้วนั่นเอง
จากภาพด้านบนนี้ ผมได้สร้างแอตทริบิวต์สำหรับรับค่าอาร์เรย์จาก Controller ไว้ชื่อว่า parser-repeat ซึ่งจะมีด้วยกัน 2 จุด คือ
1) <li parser-repeat="data_list">{title}</li>
จะสร้างแท็ก <li> ที่นำข้อมูลจากอาร์เรย์ title ใน data_list มาแสดง จนครบทุกตัว
2) <tr parser-repeat="blog_entries">
จะสร้างแท็ก <tr> ที่นำข้อมูลจากอาร์เรย์ title และ body ใน blog_entries มาแสดง จนครบทุกตัว
และเมื่อใช้คำสั่ง
$this->parser->parse_repeat('test_parser_repeat_view', $data);
ไฟล์ Controller สำหรับทดสอบ
application/controllers/Template_engine.php
ไฟล์ View สำหรับทดสอบ
1. สำหรับแสดงตัวอย่างแบบเดิม
application/views/test_parser_view.php
2. สำหรับแสดงตัวอย่างแบบใหม่ (แทรกในแท็กของอีเลเมนต์)
application/views/test_parser_repeat_view.php
แหล่งข้อมูลอ้างอิง
CodeIgniter - Template Parser Class
https://www.codeigniter.com/userguide3/libraries/parser.html
15 PHP regular expressions for web developers
https://www.catswhocode.com/blog/15-php-regular-expressions-for-web-developers
จะเห็นว่าการใช้ Template Engine ก็ช่วยให้การเขียนโค้ดง่ายขึ้นได้เช่นกัน (แต่ก็จะต้องจัดรูปแบบข้อมูลให้เรียบร้อยใน Controller ก่อนจะนำมาแทรกลงใน Template ซึ่งใน PHP CI MANIA จะมีการสร้างฟังก์ชั่นเหล่านี้โดยอัตโนมัติ เช่น จัดรูปแบบวันที่ภาษาไทย จัดรูปแบบตัวเลข เป็นต้น)
ทุกอย่างก็ดูสะดวกสบายใช้ง่ายดีนี่นา แล้วทำไมถึงอยากจะปรับแต่งฟังก์ชั่นให้กับ Template Parser Class ด้วยล่ะ??
ซึ่งถ้าได้ติดตามบล็อกของผมมาโดยตลอด จะเห็นว่าก่อนหน้านี้ก็มีการปรับแต่งไปแล้วครั้งหนึ่งในบทความ PHP Codeigniter กับการใช้งาน Template Parser Class แล้วไม่สามารถเรียกใช้ตัวแปรด้านนอก Variable Pairs ที่กำหนดไว้ได้ ซึ่งมีปัญหาว่าในลูปไม่สามารถดึงค่าตัวแปร url ของหน้าเว็บจากอารเรย์ชั้นข้างนอกได้
หลังจากปรับแต่งก็ใช้วิธีใส่วงเล็บปีกกาซ้อนกัน 2 ชั้นเพื่อเรียกใช้ตัวแปรด้านนอกแทน
มาที่เรื่องของบทความนี้กันต่อ
เนื่องจากในโปรแกรม PHP CI MANIA ที่ผมกำลังพัฒนาอยู่นั้น จะมีส่วนของการแสดงตัวอย่างของ VIEW แต่ละตัวในขั้นตอนสร้างโค้ดได้ด้วย จึงทำให้เกิดบทความนี้ขึ้นมาจะเห็นว่าเวลาในโหมด Preview เพื่อดูไฟล์ View ในตัวอย่างโค้ดด้านบนนั้น จะมีข้อความส่วนที่ผมไม่อยากให้แสดงออกมาโผล่ออกมาด้วย
ซึ่งในส่วนของการรับค่าอาร์เรย์ data_list ยังพอทำใจได้เพราะมันครอบ <li> เอาไว้
แต่ในส่วนของตาราง <table> จะมีวงเล็บ {blog_entries} ครอบแถวเอาไว้ พอมันครอบ <tr> แต่มันไม่ใช่แท็ก HTML ของ <table> มันก็เลยถูกเขี่ยกระเด็นออกไปด้านบนซะงั้น
นอกจากจะรกเกะกะสายตาแล้ว ยังทำให้ตำแหน่งตารางผิดไปจากตำแหน่งจริงอีกด้วย
และในภาพด้านบนนี้ ก็คือตัวอย่างการแสดงผลในโหมด Preview ที่กระผมต้องการนั่นเอง จะเห็นว่าส่วนของบล็อกที่ใช้รับค่า {data_list} และ {blog_entries} ได้หายไปแล้ว เพราะผมได้เอาไปซ่อนไว้ใน Attribute ของอีเลเมนต์ที่ต้องการวนลูปไปแล้วนั่นเอง
จากภาพด้านบนนี้ ผมได้สร้างแอตทริบิวต์สำหรับรับค่าอาร์เรย์จาก Controller ไว้ชื่อว่า parser-repeat ซึ่งจะมีด้วยกัน 2 จุด คือ
1) <li parser-repeat="data_list">{title}</li>
จะสร้างแท็ก <li> ที่นำข้อมูลจากอาร์เรย์ title ใน data_list มาแสดง จนครบทุกตัว
2) <tr parser-repeat="blog_entries">
จะสร้างแท็ก <tr> ที่นำข้อมูลจากอาร์เรย์ title และ body ใน blog_entries มาแสดง จนครบทุกตัว
และเมื่อใช้คำสั่ง
$this->parser->parse_repeat('test_parser_repeat_view', $data);
ก็จะเรียกฟังก์ชั่นใหม่ที่ผมได้เพิ่มเข้าไป นั่นคือ parse_repeat() ซึ่งจะทำหน้าที่ค้นหา HTML ทุกตัวที่มี Attribute กำหนดไว้ว่า parser-repeat="{ชื่อ Key ของอาร์เรย์}" มาแทนที่ด้วยข้อมูลอาร์เรย์ในคีย์ชุดนั้นๆ
สำหรับซอร์สโค้ดหลังปรับแต่งเรียบร้อยมีดังนี้
application/libraries/MY_parser.php<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Overrides the CI Template Parser to allow for multiple occurrences of the
* same variable pair
*
*/
class MY_Parser extends CI_Parser {
/**
* Parse a template
*
* Parses pseudo-variables contained in the specified template view,
* replacing them with the data in the second param
*
* @param string
* @param array
* @param bool
* @return string
*/
public function parse($template, $data, $return = FALSE)
{
$template = $this->CI->load->view($template, $data, TRUE);
$template = $this->_parse_double($template, $data); return $this->_parse($template, $data, $return);
}
public function parse_repeat($template, $data, $return = FALSE)
{
$template = $this->CI->load->view($template, $data, TRUE);
$template = $this->_parse_double($template, $data);
$template = $this->_parse_attribute($template, $data, $return);
return $this->_parse($template, $data, $return);
}
/**
* Parse a double key/value
*
* @param string
* @param array
* @return string
*/
protected function _parse_double($template, $data)
{
$replace = array();
preg_match_all("/\{\{(.*?)\}\}/si", $template, $matches);
foreach ($matches[1] as $match)
{
$key = '{{'.$match.'}}';
$replace[$key] = isset($data[$match]) ? $data[$match] : $key;
}
unset($data);
return strtr($template, $replace);
}
/**
* Parse list with attribute
*
* @param string
* @param array
* @return string
*/
protected function _parse_attribute($template, $data, $return = FALSE)
{
if ($template === '')
{
return FALSE;
}
$replace = array();
foreach ($data as $key => $val)
{
$replace = array_merge(
$replace,
is_array($val)
? $this->_parse_attribute_pair($key, $val, $template)
: $this->_parse_single($key, (string) $val, $template)
);
}
unset($data);
$template = strtr($template, $replace);
/* return only
if ($return === FALSE)
{
$this->CI->output->append_output($template);
}
*/
return $template;
}
protected function _parse_attribute_pair( $attr_value, $data, $string)
{
$replace = array();
$matches = $this->get_tag('parser-repeat', $attr_value, $string);
foreach ($matches as $match)
//if(!empty($matches))
{
$str = '';
foreach ($data as $row)
{
$temp = array();
foreach ($row as $key => $val)
{
if (is_array($val))
{
$pair = $this->_parse_attribute_pair($key, $val, $match[0]);
if ( ! empty($pair))
{
$temp = array_merge($temp, $pair);
}
continue;
}
$temp[$this->l_delim.$key.$this->r_delim] = $val;
}
$temp[' parser-repeat="'.$attr_value.'"'] = '';//Clear repeat attribute
$str .= strtr($match[0], $temp);
}
$replace[$match[0]] = $str;
}
return $replace;
}
private function get_tag( $attr, $value, $xml, $tag=null ) {
if( is_null($tag) ){
$tag = '\w+';
}else{
$tag = preg_quote($tag);
}
$attr = preg_quote($attr);
$value = preg_quote($value);
$tag_regex = "/<(".$tag.")[^>]*$attr\s*=\s*".
"(['\"])$value\\2[^>]*>(.*?)<\/\\1>/s";
preg_match_all($tag_regex,
$xml,
$matches,
PREG_PATTERN_ORDER);
$target = array();
if(!empty($matches)){
$match1 = isset($matches[0][0]) ? $matches[0][0] : '';
$match2 = isset($matches[3][0]) ? $matches[3][0] : '';
if($match1 != ''){
$target = array(array($match1, $match2));
}
}
return $target;
}
}
// END Parser Class
/* End of file MY_Parser.php */
/* Location: ./application/libraries/MY_Parser.php */
ไฟล์ Controller สำหรับทดสอบ
application/controllers/Template_engine.php
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Template_engine extends CI_Controller
{
public function __construct()
{
parent::__construct();
}
public function index()
{
$this->parser->parse('labs/test_parser_view', array());
}
public function parser_view()
{
$this->parser->parse('labs/test_parser_repeat_view', array());
}
public function test_repeat()
{
$data = array(
'blog_title' => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
'blog_entries' => array(
array('title' => 'Title 1', 'body' => 'Body 1'),
array('title' => 'Title 2', 'body' => 'Body 2'),
array('title' => 'Title 3', 'body' => 'Body 3'),
array('title' => 'Title 4', 'body' => 'Body 4'),
array('title' => 'Title 5', 'body' => 'Body 5')
),'data_list' => array(
array('title' => 'Title 1', 'body' => 'Body 1'),
array('title' => 'Title 2', 'body' => 'Body 2'),
array('title' => 'Title 3', 'body' => 'Body 3'),
),
);
$this->parser->parse_repeat('labs/test_parser_repeat_view', $data);
}
}
// ---------------------------- END Controller Class --------------------------------
ไฟล์ View สำหรับทดสอบ
1. สำหรับแสดงตัวอย่างแบบเดิม
application/views/test_parser_view.php
<html>
<head>
<title>การแสดงผลแบบ Parser ปกติ</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<h3>{blog_heading}</h3> <ul>
{data_list}
<li>{title}</li>
{/data_list}
</ul> <table class="table table-bordered" border="1" style="width:500px">
<thead>
<tr bgcolor="#aaaaaa">
<td>Title</td>
<td>body</td>
</tr>
</thead>
<tbody>
{blog_entries}
<tr parser-repeat="blog_entries">
<td>{title}</td>
<td>{body}</td>
</tr>
{/blog_entries}
</tbody>
</table>
</body>
</html>
2. สำหรับแสดงตัวอย่างแบบใหม่ (แทรกในแท็กของอีเลเมนต์)
application/views/test_parser_repeat_view.php
<html>
<head>
<title>การแสดงผลแบบใช้ Parser Repeat</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<h3>{blog_heading}</h3>
<ul>
<li parser-repeat="data_list">{title}</li>
</ul>
<table class="table table-bordered" border="1" style="width:500px">
<thead>
<tr bgcolor="#aaaaaa">
<td>Title</td>
<td>body</td>
</tr>
</thead>
<tbody>
<tr parser-repeat="blog_entries">
<td>{title}</td>
<td>{body}</td>
</tr>
</table>
</table>
</body>
</html>
แหล่งข้อมูลอ้างอิง
CodeIgniter - Template Parser Class
https://www.codeigniter.com/userguide3/libraries/parser.html
15 PHP regular expressions for web developers
https://www.catswhocode.com/blog/15-php-regular-expressions-for-web-developers
ความคิดเห็น
แสดงความคิดเห็น