CakePHP - Save page output as HTML

In this article, we are going to discuss about How to Save page output as HTML with cakePHP. As of now, I am working on a project that requres store the dynamically created output HTML files on a CDN (Content Delivery Network). I need to dynamically create a HTML page with database data and then store that HTML file on a CDN (Amazon S3) which cannot parse PHP. 

I was looking for a way to output a view, and save it as an HTML file which could then be uploaded via the S3 API. In the CakePHP API, we see the Controller render method returns the output of the View model's render method which returns a string of the HTML output to be loaded in the browser. But we don't want to call the Controller's render method as that will send the user's browser to that page. Instead we want to use the View's render method so we get the string value that can be saved into an HTML file.

Assume we have a publish controller method which is called when the user wants to publish the HTML file. We also have a private method to generate the HTML and save it as a file in the local filesystem.

<?php
class VideosController extends AppController {

    protected function publish( $id ){
        $html_file = $this->generate( (int)$id );
        //do whatever needs to be done with the HTML file
        //for example, this is where I upload the file to S3
    }

    private function generate( $id ){
        //set whatever data is required to build the page;
        //be sure this is done before instantiating the View class so all
        //set variables are passed when you pass this controller object in the constructor
        $video = $this->Video->read( null, $id );
        $this->set('video', $video);

        //instantiate a new View class from the controller
        $view = new View($this);

        //call the View object's render method which will return the HTML.
        //Note, I'm setting the layout to HTML and telling the view to render the html.ctp file in /app/views/videos/ directory
        $viewdata = $view->render(null,'html','html');

        //set the file name to save the View's output
        $path = WWW_ROOT . 'files/html/' . $id . '.html';
        $file = new File($path, true);

        //write the content to the file
        $file->write( $viewdata );

        //return the path
        return $path;

    }
}

Let me know if you run into any problems. But so far, seems to be working well.

Setting Error page layout based on Auth status with CakePHP

In this tutorial, we are going to discuss about How to setting the separate layout for the error pages based on the Auth Status with CakePHP. By default, CakePHP error pages load within the default layout. This is enough for most of the applications. But in some applications we need a separate layout for logged in users. So here I'm going to explain about how to set the separate layout for the error pages based on the Auth status with CakePHP.

For example, the navigation changes when a user logged in. Basically including the proper elements based in the user's login status. But for the some projects, the entire layout needs to be changed based on the user status. Therefore I needed to find a way to be sure the proper layout was loaded when 404 errors appeared.

To achieve this, we need to create an app_error.php file in our /app directory. Our AppError class should extend the ErrorHandler class. Now extend the error404 method. You'll have a reference to the controller via $this->controller so that you can access the Auth component. So just see if we have a valid logged in user, and if not, set the layout to 'guest', or whatever your layout happens to be named.

Be sure to call the parent method, passing in the $params variable to be sure the error is handled properly by the ErrorHandler's error404 method.

<?php
class AppError extends ErrorHandler {
    function error404($params) {
            if( !$this->controller->Auth->User() ){
                $this->controller->layout = "guest";
            }
            parent::error404($params);
    }
}

Pagination Caching in CakePHP - Step by step tutorial

In this article, we are going to discuss about How to achieve pagination cachin in CakePHP. Pagination is nothing but, it is a mechanism which provides users with additional navigation options for browsing through single parts of the given article. Parts of the article are usually referred to by numbers, hints, arrows as well as "previous" and "next"-buttons. In this, we are going to discuss about How to achieve this in CakePHP.

In most cases pagination is better than traditional "previous – next" navigation as it offers visitors a more quick and convenient navigation through the site. It's not a must, but a useful nice-to-have-feature.

If we have large number of datas it will producing unneccessary high load on the database. To reduce the load in database, we can use "Caching". Unlike normal returned data from the database paginated data can not be cached as easily, as the paginate method needs to be called to generate the pagination numbers etc. So we need to do a custom pagination query with cache built in.

To implement pagination caching in CakePHP, add the below code in your app_model.php file.

function paginate ($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) 
{
    $args = func_get_args();
    $uniqueCacheId = '';
    foreach ($args as $arg) {
        $uniqueCacheId .= serialize($arg);
    }
    if (!empty($extra['contain'])) {
        $contain = $extra['contain'];   
    }
    $uniqueCacheId = md5($uniqueCacheId);
    $pagination = Cache::read('pagination-'.$this->alias.'-'.$uniqueCacheId, 'paginate_cache');
    if (empty($pagination)) {
        $pagination = $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group', 'contain'));
        Cache::write('pagination-'.$this->alias.'-'.$uniqueCacheId, $pagination, 'paginate_cache');
    }
    return $pagination;
}

function paginateCount ($conditions = null, $recursive = 0, $extra = array()) {
    $args = func_get_args();
    $uniqueCacheId = '';
    foreach ($args as $arg) {
        $uniqueCacheId .= serialize($arg);
    }
    $uniqueCacheId = md5($uniqueCacheId);
    if (!empty($extra['contain'])) {
        $contain = $extra['contain'];   
    }
     
    $paginationcount = Cache::read('paginationcount-'.$this->alias.'-'.$uniqueCacheId, 'paginate_cache');
    if (empty($paginationcount)) {
        $paginationcount = $this->find('count', compact('conditions', 'contain', 'recursive'));
        Cache::write('paginationcount-'.$this->alias.'-'.$uniqueCacheId, $paginationcount, 'paginate_cache');
    }
    return $paginationcount;
}

This will then take over from any paginate calls and generate a cached version of the dataset for the paginated items and the pagination controls, unique to each page and query set.


You need to specify the caching rule in core.php, something like:

Cache::config('paginate_cache', array(
    'engine'        => 'File',
    'path'      => CACHE .'sql'. DS,
    'serialize' => true,
        'duration' => '+1 hour',
));

Steps to Install CakePHP on Shared Hosting

In this article, we are going to discuss about How to install CakePHP on Shared Hosting. I have installed cakaPHP on my hosted web but I Couldn't get it to work quite right. After a few hours playing around with cakePHP, finally I got the correct installation.

My hosting service allows me to publish multiple websites/domains on the one account. They have FTP access to upload the websites and each website/domain appears as a separate directory off the root directory.

/
  /site1.com
     site1_files.htm
  /site2.com
     site2_files.htm
  /site3.com
     site3_files.htm 

My original installation was to simply unzip the CakePHP files to my new site. The result was something like:


  /site1.com 
     site1_files.htm 
  /site2.com 
     site2_files.htm 
  /site3.com 
     site3_files.htm 
  /site4.com 
     /cake 
        /config 
        /console 
        /libs 
        /tests 
        basics.php 
        bootstrap.php 
        dispatcher.php 
     /app 
        /config 
        /controllers 
        /models 
        /tests 
        /tmp 
        /vendors 
        /webroot 
            /css 
            /files 
            /img 
            /js 
            .htaccess 
            index.php 
        .htaccess 
        index.php 
     .htaccess 

     index.php

I have not listed all files and directories here. My webhost doesn't allow the DocumentRoot to be changed in the .htaccess file nor via the CPanel. I moved the 'app', 'cake' and 'vendors' directories and contents into the root directory. I deleted the .htaccess and index.php files from the site4.com directory (i.e the first level). 

I moved the contents of the 'webroot' directory into the site4.com directory and then deleted the empty webroot directory. I also changed the name of my 'app' directory to 'site4app', this allows me to run multiple cake apps from the one server, one cake app for each domain.

This is the resulting structure:


  /cake 
     /config 
     /console 
     /libs 
     /tests 
     basics.php 
     bootstrap.php 
     dispatcher.php 
  /site4app 
     /config 
     /controllers 
     /models 
     /tests 
     /tmp 
     /vendors 
     .htaccess 
     index.php 
  /site1.com 
     site1_files.htm 
  /site2.com 
     site2_files.htm 
  /site3.com 
     site3_files.htm 
  /site4.com 
     /css 
     /files 
     /img 
     /js 
     .htaccess 
     index.php 
  /vendors 
     /css 

     /js 

I then had to edit the index.php file in the site4.com directory (the old app/webroot directory) to point to the 'cake' and 'app' (now called site4app) directories. The CPanel of my account listed the actual directory of my site4.com domain as /hsphere/local/home/my_account_name/site4.com. Therefore I had to change;

ROOT to look at /hsphere/local/home/my_account_name

APP_DIR to look at /hsphere/local/home/my_account_name/site4app

CAKE_CORE_INCLUDE_PATH to look at /hsphere/local/home/my_account_name/cake

ROOT = /hsphere/local/home/my_account_name, APP_DIR = site4app, and 

CAKE_CORE_INCLUDE_PATH = /hsphere/local/home/my_account_name. 

The web document root has already been set in the CPanel settings from my web host as /hsphere/local/home/my_account_name/site4.com and therefore doesn't need to be set anywhere in cakephp.

The section below is what the relevant section in my index.php file looks like.

/** 
 * The full path to the directory which holds "app", WITHOUT a trailing DS. 
 * 
 */ 
    if (!defined('ROOT')) { 
        define('ROOT', DS.'hsphere'.DS.'local'.DS.'home'.DS.'my_account_name'); 
    } 
/** 
 * The actual directory name for the "app". 
 * 
 */ 
    if (!defined('APP_DIR')) { 
        define('APP_DIR', 'site4app'); 
    } 
/** 
 * The absolute path to the "cake" directory, WITHOUT a trailing DS. 
 * 
 */ 
    if (!defined('CAKE_CORE_INCLUDE_PATH')) { 
        define('CAKE_CORE_INCLUDE_PATH', DS.'hsphere'.DS.'local'.DS.'home'.DS.'my_account_name'); 
    } 


/** 

I used the standarf .htaccess file in the site4.com directory (the old webroot) so it would load the correct page when someone went to www.site4.com. Anyway, mine looks like this:

<IfModule mod_rewrite.c>      
  RewriteEngine On 
  RewriteCond %{REQUEST_FILENAME} !-d 
  RewriteCond %{REQUEST_FILENAME} !-f 
  RewriteRule ^(.*)$ /index.php?url=$1 [QSA,L] 

</IfModule> 

I went to www.site4.com and it was all working. Nice! There was only one problem, I was getting the session ID appended to the URL. The advice of setting the "php_flag session.trans_id off" only caused a web server error.

To fix the problem I changed a setting in the core.php file in the site4app/config (old app/config) directory. Change the session.save value from 'php' to 'cake'. It's about line 104 in my file.

Configure::write('Session.save', 'cake'); 

Now when I loaded the page at www.site4.com it displayed some errors. This turned out to be due to a missing directory. I went to site4app/tmp (old app/tmp) directory and created a directory called 'sessions'. Loaded the page again. Now No errors.

I hope this method also works for you. Good luck. Have a great day.