CODEIGNITER FRAMEWORK: Sử dụng với Smarty

Smarty là gì?

  • Smarty là một hệ thống template. Nó được biết đến như là một công cụ cho việc chia nhỏ các thiết kế web. Nó tạo ra các nội dung từ các vị trí khác nhau và được gọi là smarty tag. Chúng được tạo ra bởi tag mở và tag khóa.
  • Thông thường lập trình PHP bạn thường hay viết cả mã HTML và PHP vào cùng một trang. Điều này sẽ dẫn đến rất rối cho người lập trình nhất là khi bảo trì hệ thống. Vì vậy Smarty ra đời để giúp bạn giải quyết được vấn đề đó. Nó giúp bạn code được rõ ràng hơn, tách biệt được phần code và templates.

Tích hợp Smarty và Codeigniter

  1. Download smarty : http://www.smarty.net/
  2. Bạn giải nén và copy thư mục Smarty vào application/third_party
  3. Trong thư mục application/libraries bạn tạo một file có tên là : Smarty.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CI Smarty
 *
 * Smarty templating for Codeigniter
 *
 * @package   CI Smarty
 * @author    Dwayne Charrington
 * @copyright 2015 Dwayne Charrington and Github contributors
 * @link      http://ilikekillnerds.com
 * @license   MIT
 * @version   3.0
 */
require_once APPPATH."third_party/Smarty/Smarty.class.php";
class CI_Smarty extends Smarty {

        public $template_ext = '.php';

        public function __construct()
        {
            parent::__construct();

            // Store the Codeigniter super global instance... whatever
            $CI = get_instance();

            // Load the Smarty config file
            $CI->load->config('smarty');

            // Turn on/off debug
            $this->debugging = $CI->config->item('smarty.smarty_debug');

            // Set some pretty standard Smarty directories
            $this->setCompileDir($CI->config->item('smarty.compile_directory'));
            $this->setCacheDir($CI->config->item('smarty.cache_directory'));
            $this->setConfigDir($CI->config->item('smarty.config_directory'));

            // Default template extension
            $this->template_ext = $CI->config->item('smarty.template_ext');

            // How long to cache templates for
            $this->cache_lifetime = $CI->config->item('smarty.cache_lifetime');

            // Disable Smarty security policy
            $this->disableSecurity();

            // If caching is enabled, then disable force compile and enable cache
            if ( $CI->config->item('smarty.cache_status') === TRUE )
            {
                $this->enable_caching();
            }
            else
            {
                $this->disable_caching();
            }

            // Set the error reporting level
            $this->error_reporting   = $CI->config->item('smarty.template_error_reporting');

            // This will fix various issues like filemtime errors that some people experience
            // The cause of this is most likely setting the error_reporting value above
            // This is a static function in the main Smarty class
            Smarty::muteExpectedErrors();

            // Should let us access Codeigniter stuff in views
            // This means we can go for example {$this->session->userdata('item')}
            // just like we normally would in standard CI views
            $this->assign("this", $CI);
    	}

        /**
         * Enable Caching
         *
         * Allows you to enable caching on a page by page basis
         * @example $this->smarty->enable_caching(); then do your parse call
         */
        public function enable_caching()
        {
            $this->caching = 1;
        }

        /**
         * Disable Caching
         *
         * Allows you to disable caching on a page by page basis
         * @example $this->smarty->disable_caching(); then do your parse call
         */
        public function disable_caching()
        {
            $this->caching = 0;
        }
	}
  1. Bạn tạo thêm file MY_Parser.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CI Smarty
 *
 * Smarty templating for Codeigniter
 *
 * @package   CI Smarty
 * @author    Dwayne Charrington
 * @copyright 2015 Dwayne Charrington and Github contributors
 * @link      http://ilikekillnerds.com
 * @license   MIT
 * @version   3.0
 */
class MY_Parser extends CI_Parser {

        protected $CI;

        protected $_module = '';
        protected $_template_locations = array();

        // Current theme location
        protected $_current_path = NULL;

        // The name of the theme in use
        protected $_theme_name = '';

        public function __construct()
        {
            // Codeigniter instance and other required libraries/files
            $this->CI = get_instance();
            $this->CI->load->library('ci_smarty');
            $this->CI->load->helper('parser');

            // Detect if we have a current module
            $this->_module = $this->current_module();

            // What controllers or methods are in use
            $this->_controller  = $this->CI->router->fetch_class();
            $this->_method     = $this->CI->router->fetch_method();

            // If we don't have a theme name stored
            if ($this->_theme_name == '')
            {
                $this->set_theme($this->CI->config->item('smarty.theme_name'));
            }

            // Update theme paths
            $this->_update_theme_paths();
        }

        /**
        * Call
        * able to call native Smarty methods
        * @returns mixed
        */
        public function __call($method, $params=array())
        {
            if ( ! method_exists($this, $method) )
            {
                return call_user_func_array(array($this->CI->smarty, $method), $params);
            }
        }

        /**
         * Set Theme
         *
         * Set the theme to use
         *
         * @access public
         * @param $name
         * @return string
         */
        public function set_theme($name)
        {
            // Store the theme name
            $this->_theme_name = trim($name);

            // Our themes can have a functions.php file just like Wordpress
            $functions_file  = $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/functions.php';

            // Incase we have a theme in the application directory
            $functions_file2 = APPPATH."themes/" . $this->_theme_name . '/functions.php';

            // If we have a functions file, include it
            if ( file_exists($functions_file) )
            {
                include_once($functions_file);
            }
            elseif ( file_exists($functions_file2) )
            {
                include_once($functions_file2);
            }

            // Update theme paths
            $this->_update_theme_paths();
        }

        /**
         * Get Theme
         *
         * Does what the function name implies: gets the name of
         * the currently in use theme.
         *
         * @return string
         */
        public function get_theme()
        {
            return (isset($this->_theme_name)) ? $this->_theme_name : '';
        }

        /**
         * Current Module
         *
         * Just a fancier way of getting the current module
         * if we have support for modules
         *
         * @access public
         * @return string
         */
        public function current_module()
        {
            // Modular Separation / Modular Extensions has been detected
            if ( method_exists( $this->CI->router, 'fetch_module' ) )
            {
                $module = $this->CI->router->fetch_module();
                return (!empty($module)) ? $module : '';
            }
            else
            {
                return '';
            }
        }

        /**
         * Parse
         *
         * Parses a template using Smarty 3 engine
         *
         * @access public
         * @param $template
         * @param $data
         * @param $return
         * @param $caching
         * @param $theme
         * @return string
         */
        public function parse($template, $data = array(), $return = FALSE, $caching = TRUE, $theme = '')
        {
            // If we don't want caching, disable it
            if ( $caching === FALSE )
            {
                $this->CI->smarty->disable_caching();
            }

            // If no file extension dot has been found default to defined extension for view extensions
            if ( ! stripos($template, '.'))
            {
                $template = $template.".".$this->CI->smarty->template_ext;
            }

            // Are we overriding the theme on a per load view basis?
            if ( $theme !== '' )
            {
                $this->set_theme($theme);
            }

            // Get the location of our view, where the hell is it?
            // But only if we're not accessing a smart resource
            if ( ! stripos($template, ':') )
            {
                $template = $this->_find_view($template);
            }

            // If we have variables to assign, lets assign them
            if ( ! empty($data) )
            {
                foreach ($data AS $key => $val)
                {
                    $this->CI->smarty->assign($key, $val);
                }
            }

            // Load our template into our string for judgement
            $template_string = $this->CI->smarty->fetch($template);

            // If we're returning the templates contents, we're displaying the template
            if ( $return === FALSE )
            {
                $this->CI->output->append_output($template_string);
                return TRUE;
            }

            // We're returning the contents, fo' shizzle
            return $template_string;
        }

        /**
         * CSS
         *
         * An asset function that returns a CSS stylesheet
         *
         * @access public
         * @param $file
         * @return string
         */
        public function css($file, $attributes = array())
        {
            $defaults = array(
                'media' => 'screen',
                'rel'   => 'stylesheet',
                'type'  => 'text/css'
            );

            $attributes = array_merge($defaults, $attributes);

            $return = '<link rel="'.$attributes['rel'].'" type="'.$attributes['type'].'" href="'.base_url($this->CI->config->item('smarty.theme_path').$this->get_theme()."/css/".$file).'" media="'.$attributes['media'].'">';

            return $return;
        }

        /**
         * JS
         *
         * An asset function that returns a script embed tag
         *
         * @access public
         * @param $file
         * @return string
         */
        public function js($file, $attributes = array())
        {
            $defaults = array(
                'type'  => 'text/javascript'
            );

            $attributes = array_merge($defaults, $attributes);

            $return = '<script type="'.$attributes['type'].'" src="'.base_url($this->CI->config->item('smarty.theme_path').$this->get_theme()."/js/".$file).'"></script>';

            return $return;
        }

        /**
         * Theme URL
         *
         * A web friendly URL for determining the current
         * theme root location.
         *
         * @access public
         * @param $location
         * @return string
         */
        public function theme_url($location = '')
        {
            // The path to return
            $return = base_url($this->CI->config->item('smarty.theme_path').$this->get_theme())."/";

            // If we want to add something to the end of the theme URL
            if ( $location !== '' )
            {
                $return = $return.$location;
            }

            return trim($return);
        }

        /**
        * Find View
        *
        * Searches through module and view folders looking for your view, sir.
        *
        * @access protected
        * @param $file
        * @return string The path and file found
        */
        protected function _find_view($file)
        {
            // We have no path by default
            $path = NULL;

            // Get template locations
            $locations = $this->_template_locations;

            // Get the current module
            $current_module = $this->current_module();

            if ( $current_module !== $this->_module )
            {
                $new_locations = array(
                    $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/views/modules/' . $current_module .'/layouts/',
                    $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/views/modules/' . $current_module .'/',
                    APPPATH . 'modules/' . $current_module . '/views/layouts/',
                    APPPATH . 'modules/' . $current_module . '/views/'
                );

                foreach ($new_locations AS $new_location)
                {
                    array_unshift($locations, $new_location);
                }
            }

            // Iterate over our saved locations and find the file
            foreach($locations AS $location)
            {
                if ( file_exists($location.$file) )
                {
                    // Store the file to load
                    $path = $location.$file;

                    $this->_current_path = $location;

                    // Stop the loop, we found our file
                    break;
                }
            }

            // Return the path
            return $path;
        }

        /**
        * Add Paths
        *
        * Traverses all added template locations and adds them
        * to Smarty so we can extend and include view files
        * correctly from a slew of different locations including
        * modules if we support them.
        *
        * @access protected
        */
        protected function _add_paths()
        {
            // Iterate over our saved locations and find the file
            foreach($this->_template_locations AS $location)
            {
                $this->CI->smarty->addTemplateDir($location);
            }
        }

        /**
         * Update Theme Paths
         *
         * Adds in the required locations for themes
         *
         * @access protected
         */
        protected function _update_theme_paths()
        {
            // Store a whole heap of template locations
            $this->_template_locations = array(
                $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/views/modules/' . $this->_module .'/layouts/',
                $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/views/modules/' . $this->_module .'/',
                $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/views/layouts/',
                $this->CI->config->item('smarty.theme_path') . $this->_theme_name . '/views/',
                APPPATH . 'modules/' . $this->_module . '/views/layouts/',
                APPPATH . 'modules/' . $this->_module . '/views/',
                APPPATH . 'views/layouts/',
                APPPATH . 'views/'
            );

            // Will add paths into Smarty for "smarter" inheritance and inclusion
            $this->_add_paths();
        }

        /**
        * String Parse
        *
        * Parses a string using Smarty 3
        *
        * @param string $template
        * @param array $data
        * @param boolean $return
        * @param mixed $is_include
        */
        public function string_parse($template, $data = array(), $return = FALSE, $is_include = FALSE)
        {
            return $this->CI->smarty->fetch('string:'.$template, $data);
        }

        /**
        * Parse String
        *
        * Parses a string using Smarty 3. Never understood why there
        * was two identical functions in Codeigniter that did the same.
        *
        * @param string $template
        * @param array $data
        * @param boolean $return
        * @param mixed $is_include
        */
        public function parse_string($template, $data = array(), $return = FALSE, $is_include = false)
        {
            return $this->string_parse($template, $data, $return, $is_include);
        }
}
  1. Trong thư mục Core bạn tạo thêm một file : MY_Output.php
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Output extends CI_Output {

        public function _display($output = '')
        {
            parent::_display($output);
            // If Smarty is active - NOTE: $this->output->enable_profiler(TRUE) active Smarty debug to simplify
            if (class_exists('CI_Controller') && class_exists('Smarty_Internal_Debug') && (config_item('smarty_debug') || $this->enable_profiler)) {
                $CI =& get_instance();
                Smarty_Internal_Debug::display_debug( $CI->smarty);
            }
        }
}
  1. Để auto load Smarty. Bạn mở file application/config/autoload.php để import thư viện MY_Parser.php
$autoload['libraries'] = array('parser');
  • Ở đây vì sao file mình khởi tạo là MY_Parser.php lúc load file lại là array('parser'). Vì ở trong config/config.php ở đó mình config subclass prefix của nó là :
$config['subclass_prefix'] = 'MY_';
  1. Như vậy là mình đã hoàn thành tích hợp Smarty vào Codeigniter. Bây giờ mình hướng dẫn các sử dụng Smarty cơ bản
  • Trong thư mục Controller bạn tạo một file : Smarty_Example.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Smarty_Example extends MY_Controller
{
            public function __construct()
            {
                parent::__construct();
            }

            public function index()
            {
                $data = array(
                	'title' => 'Smarty_Example',
                    'body' => 'This is body text to show that the Smarty Parser works!'
                );
                $this->parser->parse('index.tpl', $data);
            }
    }
  • Ở thư mục View bạn tạo file : index.tpl
<html>
            <head>
                <title>{$title}</title>
            </head>
            <body>
                {$body}
            </body>
</html>
  • Smarty có định dạng file là .tpl. Ở trong Controller tất cả các tham số được đưa vào một mảng và định nghĩa nó bởi các keyword
  • Khi sang View bạn gọi nó bằng cách {$value or object}
  • Smarty còn hỗ trợ các ngôn ngữ lập trình cao : for, foreach, if else, ...
  • Bạn có thể tìm hiểu tài liệu của nó tại : http://www.smarty.net/documentation

Kết luận

  • Qua bài này ta thấy khi sử dụng Smarty code sẽ sạch hơn rất nhiều khi ta sử dụng code PHP.
  • Smarty hay bất kỳ dạng template nào cũng sẽ không chạy nhanh như khi ta viết PHP thuần được, tuy nhiên với cơ chế lưu Cache phù hợp thì nó chạy nhanh hơn nhiều.

All Rights Reserved