Smarty View for 1.2

By Michael Houghton (icedcheese)
This expands on the Smarty View code from Version 1.1. While its only a beginning, I am hoping someone else will pick up from what we had in version 1.1

In version 1.1 of Cakephp I used the Smarty View and thought it was a great additional. In my opinion Smarty and Cake work so well together because it allows a web design to stick to the Smarty side of things, and the developer to use Cake.


With Smarty Caching, I never found any performance issues using Smarty. It really does go well with Cake... In saying that, I have started a plugin for Smarty for Cake 1.2 which expands on Smarty View from 1.1 http://bakery.cakephp.org/articles/view/how-to-use-smarty-with-cake-smartyview


Note: At this point I have only found the HTML helpers work, not the new form functions which Cake has. I am hoping someone can expand on what I have got and will write those!


Installation


Step 1: Make the file /app/view/smarty.php


Download code
<?php
/* SVN FILE: $Id: view.php 6311 2008-01-02 06:33:52Z phpnut $ */

/**
 * Methods for displaying presentation data in the view.
 *
 * PHP versions 4 and 5
 *
 * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
 * Copyright 2005-2008, Cake Software Foundation, Inc.
 *                                1785 E. Sahara Avenue, Suite 490-204
 *                                Las Vegas, Nevada 89104
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @filesource
 * @copyright        Copyright 2005-2008, Cake Software Foundation, Inc.
 * @link            http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
 * @package            cake
 * @subpackage        cake.cake.libs.view
 * @since            CakePHP(tm) v 0.10.0.1076
 * @version            $Revision: 6311 $
 * @modifiedby        $LastChangedBy: phpnut $
 * @lastmodified    $Date: 2008-01-02 00:33:52 -0600 (Wed, 02 Jan 2008) $
 * @license            http://www.opensource.org/licenses/mit-license.php The MIT License
 */

/**
 * Include Smarty. By default expects it at ( VENDORS.'smarty'.DS.'Smarty.class.php' )
 */
vendor('smarty'.DS.'Smarty.class');

/**
 * CakePHP Smarty view class
 *
 * This class will allow using Smarty with CakePHP
 *
 * @version      1.1.0.0
 * @package      cake
 * @subpackage   cake.app.views
 * @since        CakePHP v 1.2
 */
class SmartyView extends View
{
/**
 * SmartyView constructor
 *
 * @param  $controller instance of calling controller
 */
    
function __construct (&$controller)
    {
        
parent::__construct($controller);
        
$this->Smarty = &new Smarty();
        
// requires views be in a 'smarty' subdirectory, you can remove this limitation if you aren't using other inherited views that use .tpl as the extension
        
$this->subDir 'smarty'.DS;
        
$this->ext'.tpl';
        
$this->Smarty->plugins_dir[] = VIEWS.'smarty_plugins'.DS;
        
$this->Smarty->compile_dir TMP.'smarty'.DS.'compile'.DS;
        
$this->Smarty->cache_dir TMP.'smarty'.DS.'cache'.DS;
        
$this->Smarty->error_reporting 'E_ALL & ~E_NOTICE';
        
$this->Smarty->debugging true;
    }

/**
 * Overrides the View::_render()
 * Sets variables used in CakePHP to Smarty variables
 *
 * @param string $___viewFn
 * @param string $___data_for_view
 * @param string $___play_safe
 * @param string $loadHelpers
 * @return rendered views
 */
    
function _render($___viewFn$___data_for_view$___play_safe true$loadHelpers true)
    {
        if (
$this->helpers != false && $loadHelpers === true)
        {
            
$loadedHelpers =  array();
            
$loadedHelpers $this->_loadHelpers($loadedHelpers$this->helpers);

            foreach(
array_keys($loadedHelpers) as $helper)
            {
                
$replace strtolower(substr($helper01));
                
$camelBackedHelper preg_replace('/\\w/'$replace$helper1);

                ${
$camelBackedHelper} =& $loadedHelpers[$helper];
                if(isset(${
$camelBackedHelper}->helpers) && is_array(${$camelBackedHelper}->helpers))
                {
                    foreach(${
$camelBackedHelper}->helpers as $subHelper)
                    {
                        ${
$camelBackedHelper}->{$subHelper} =& $loadedHelpers[$subHelper];
                    }
                }
                
$this->loaded[$camelBackedHelper] = (${$camelBackedHelper});
                
$this->Smarty->assign_by_ref($camelBackedHelper, ${$camelBackedHelper});
            }
        }

        
$this->register_functions();

        foreach(
$___data_for_view as $data => $value)
        {
            if(!
is_object($data))
            {
                
$this->Smarty->assign($data$value);
            }
        }
        
$this->Smarty->assign_by_ref('view'$this);
        return 
$this->Smarty->fetch($___viewFn);
    }
    
/**
 * Returns layout filename for this template as a string.
 *
 * @return string Filename for layout file (.ctp).
 * @access private
 */
    
function _getLayoutFileName() {
        if (isset(
$this->webservices) && !is_null($this->webservices)) {
            
$type strtolower($this->webservices) . DS;
        } else {
            
$type null;
        }

        if (isset(
$this->plugin) && !is_null($this->plugin)) {
            if (
file_exists(APP 'plugins' DS $this->plugin DS 'views' DS 'layouts' DS $this->layout $this->ext)) {
                
$layoutFileName APP 'plugins' DS $this->plugin DS 'views' DS 'layouts' DS $this->layout $this->ext;
                return 
$layoutFileName;
            }
        }
        
$paths Configure::getInstance();

        foreach(
$paths->viewPaths as $path) {
            if (
file_exists($path 'layouts' DS $this->subDir $type $this->layout $this->ext)) {
                
$layoutFileName $path 'layouts' DS $this->subDir $type $this->layout $this->ext;
                return 
$layoutFileName;
            }
        }

        
// added for .ctp viewPath fallback
        
foreach($paths->viewPaths as $path) {
            if (
file_exists($path 'layouts' DS  $type $this->layout '.ctp')) {
                
$layoutFileName $path 'layouts' DS $type $this->layout '.ctp';
                return 
$layoutFileName;
            }
        }

        if(
$layoutFileName fileExistsInPath(LIBS 'view' DS 'templates' DS 'layouts' DS $type $this->layout '.ctp')) {
        } else {
            
$layoutFileName LAYOUTS $type $this->layout.$this->ext;
        }
        return 
$layoutFileName;
    }
    
    
/**
 * Returns filename of given action's template file (.tpl) as a string.
 * CamelCased action names will be under_scored! This means that you can have
 * LongActionNames that refer to long_action_names.ctp views.
 *
 * @param string $action Controller action to find template filename for
 * @return string Template filename
 * @access protected
 */
    
function _getViewFileName($name null) {
        
$subDir null;

        if (!
is_null($this->webservices)) {
            
$subDir strtolower($this->webservices) . DS;
        }
        if (!
is_null($this->subDir)) {
            
$subDir $this->subDir DS;
        }

        if (
$name === null) {
            
$name $this->action;
        }

        if (
strpos($name'/') === false && strpos($name'..') === false) {
            
$name $this->viewPath DS $subDir Inflector::underscore($name);
        } elseif (
strpos($name'/') !== false) {
            if (
$name{0} === '/') {
                if (
is_file($name)) {
                    return 
$name;
                }
                
$name trim($name'/');
            } else {
                
$name $this->viewPath DS $subDir $name;
            }
            if (
DS !== '/') {
                
$name implode(DSexplode('/'$name));
            }
        } elseif (
strpos($name'..') !== false) {
            
$name explode('/'$name);
            
$i array_search('..'$name);
            unset(
$name[$i 1]);
            unset(
$name[$i]);
            
$name '..' DS implode(DS$name);
        }

        
$paths $this->_paths($this->plugin);
        foreach (
$paths as $path) {
            if (
file_exists($path $name $this->ext)) {
                return 
$path $name $this->ext;
            } elseif (
file_exists($path $name '.ctp')) {
                return 
$path $name '.ctp';
            } elseif (
file_exists($path $name '.thtml')) {
                return 
$path $name '.thtml';
            }
        }

        return 
$this->_missingView($paths[0] . $name $this->ext'missingView');
    }    

    
/**
     * checks for existence of special method on loaded helpers, invoking it if it exists
     * this allows helpers to register smarty functions, modifiers, blocks, etc.
     */
    
function register_functions() {
        foreach(
array_keys($this->loaded) as $helper) {
            if (
method_exists($this->loaded[$helper], '_register_smarty_functions')) {
                
$this->loaded[$helper]->_register_smarty_functions(&$this->Smarty);
            }
        }
    }
}
?>

Comment out this line so that Smarty files don't have to go in /app/views/pages/smarty/home.tpl but /app/views/pages/home.tpl instead


Download code
$this->subDir = 'smarty'.DS;

Step 2:


Download Smarty[br] http://smarty.php.net/download.php[br] Extract tarball so Smarty.class.php sits at:[br] /vendor/smarty/Smarty.class.php


Step 3:


Create the folders /app/tmp/smarty[br]
/app/tmp/smarty/compile (chmod 777)[br]
/app/tmp/smarty/cache (chmod 777)


Step 4:


Include in your controller[br] (in app_controller.php to do it app-wide)


Download code
var $view = 'Smarty';

 

Comments 602

CakePHP Team Comments Author Comments
 

Comment

1 deprecated vendor

Hi,

I'm testing cake since 2 days, as I've a great experience with Smarty, I followed the instructions.
the way vendor is used to import the smarty class is deprecated in the version 1.2.0.7125 RC1.

my little modifications in the class SmartyView extends View :

just add "App::import" but be aware smarty files contains dot (https://trac.cakephp.org/changeset/6600)
and comment or delete the deprecated vendor

Controller Class:

<?php 
//vendor('smarty'.DS.'libs'.DS.'Smarty.class');
?>

Controller Class:

<?php 
function __construct (&$controller)
{
    
App::import('Vendor''Smarty', array('file' => 'smarty'.DS.'libs'.DS.'Smarty.class.php'));
....    
?>

hope this will help beginners like me
Posted Jun 19, 2008 by magamar
 

Comment

2 a usefull thing in smarty

Using smarty let you include other template files like this :

...
<div class="languages">
   <a href=""><img src="..." alt="..." title="..." border="0" /></a>
</div>
{include file="login_box.tpl"}

I haven't found how this could be using smarty with cake php.
Using this syntax make it search in a default smarty directory called "templates". If you want it to search in views/elements for instance just add in the constructor of SmartyView Class

$this->Smarty->template_dir = VIEWS.'elements'.DS;
nice bake
Posted Jun 22, 2008 by magamar
 

Comment

3 Loading Helpers

It doesn't look like helpers are actually being assigned by reference. If the layout isn't a Smarty template, and it has $scripts_for_layout in the , and somewhere a Smarty template tries to do {$javascript->link('some_script_that_goes_in_head', false)}, then it won't work. Because the Pages controller and the Smarty template uses different copies of the Javascript helper

I think you need to change this:

$loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers);
to this:

$loadedHelpers =& $this->_loadHelpers($loadedHelpers, $this->helpers);
Posted Jun 28, 2008 by Kasper Sevaj
 

Comment

4 Latest 1.2 release candidates

Does anyone have this working with the latest 1.2 releases, ie. it doesn't seem to work with RC2 or RC3 for me?

I get:

Notice (8): Undefined property: SmartyView::$webservices [APP/views/smarty.php, line 170]
Posted Nov 3, 2008 by Paul
 

Comment

5 rc3

Hi Paul, I've just migrated to rc3, and I didn't have this error.

But looking at the function _getViewFileName in smarty.php, you can see that if the property "webservices" is set the function will search your templates files in this directory, if not then it will search in "subDir" property, if these 2 properties are null, it's the classic path.

To use other path for templates (instead of app/views/Users for instance)
you could use :
var $webservices = "Your path";
OR in constructor :
$this->subDir = 'YourPath'.DS;

To use smarty syntax {include file="login_box.tpl"}, you should add : $this->Smarty->template_dir = VIEWS.'elements'.DS; in smarty php on line 70 in constructor.
It's useful for all other little block of html you use and reuse in your app, it's independent of all model or constructor, views
Posted Nov 3, 2008 by magamar
 

Comment

6 rc3

I'm not sure what's going on here but adding a null webservices has fixed that warning.. I was also trying to use the above suggestion of removing the need for a smarty template subdirectory but that no longer seems to work.

Thanks for the pointer though as I now have a working installation.
Posted Nov 4, 2008 by Paul
 

Question

7 Problem with migration with Smarty

I have problem in migrating it with smarty.,

Undefined property: SmartyView::$webservices

Help me to solve it.,
Posted Dec 1, 2008 by Ramesh R
 

Comment

8 Undefined property: SmartyView::$webservices

Hi, People

It's a pleasure to help. This what i did, and it's not my intention to say that my workaround it's the last word. In that way, i would like to heard opinions for another people:

For this: Undefined property: SmartyView::$webservices

In the file:smarty.php, try this:

At the function: function _getViewFileName($action);
change this line:

if (!is_null($this->webservices))

by this:

if (isset($this->webservices) && !is_null($this->webservices)){


For me was ok! What do you think?

Cheers!
Orlando Agostinho
Posted Mar 5, 2009 by Orlando Agostinho
 

Comment

9 More about _getViewFilename

Noone ever used the cake's email component? It will simply fail because this functions is lacking layoutPath code that email component sets between email html and text layouts.

Here's the fix:

if (isset($this->layoutPath) && !is_null($this->layoutPath))
$type .= $this->layoutPath . DS;


This easy!
Posted Jun 16, 2009 by Jan Seidl