Posts Tagged ‘ CakePHP ’

Still alive and kicking

Hello people! It’s been quite a long time since my last post, during this time I’ve been quiet some things changed, I resumed my studies, but i’m still working as a freelance web developer. The absence of new posts is also due to the fact i’m developing my personal homepage, and i intend to move my blog there, but while it’s not done, i decided to keep posting stuff in here, and keep sharing :)

With this post i also want to announce that i released on github a few super-simple CakePHP plugins i’ve been using in the last years in some projects, some of them have not been much used lately, but they should work fine on any CakePHP 2.X app.

The released plugins are:

Settings
A plugin to provide application wide persistent editable settings support.
Sortable
Sortable model records behavior for CakePHP.
Navigation
CakePHP Plugin which provides some navigation related facilities.
JS Manager
Provides automatic inclusion of javascript scripts in CakePHP projects.
Transactions
A simple behavior that provides database transactions control under CakePHP.
ValidationSet
CakePHP support for multiple sets of model validation rules.

Currently there’s no documentation available on them, but i’ll take care of that in the next few days, as well as unit tests.

Advertisements

An easy way to extend/rename CakePHP helpers transparently

There were times, while developing CakePHP apps, when i felt that a certain helper wasn’t doing the right thing for me, there was something missing (for example, i felt the lack of some features on the PaginatorHelper, i’m working on an extended version of it that i’ll publish soon), and the only way to fix it would be extending the helper and replacing all it’s occurrences on my views, this can be a very time consuming task.

Fortunately, someone found a nice workaround for doing it transparently, i found plugin called Analogue, it allows you to remap helpers transparently just by adding it to the controller.

You can check it on the author’s github page

The usage is pretty straight forward, you can see it on the plugin’s readme file.

A big thank you to the plugin’s author!

(understand that the ideal solution for the problem would be forking the plugin you think could be improved, improve it, and send your changes back in a push request, in a diff, whatever.. But this helper can still be handy sometimes)

CakePHP and in-memory query caching

CakePHP models by default cache in-memory all database queries you make in each request, this reduces the database querying overhead when redundant queries are made, assuming that nothing changed. But sometimes there’s a need to implement complex methods in your models to perform complex and crazy actions with your data, and sometimes when you do that you need to make the same query many times, for example, when you’re checking something inside any loop, and when you do that, if your model is caching queries, you’ll get always the same result, that will bring you for sure an unexpected behavior. So when you do that, you just need to disable query caching by setting the cacheQueries variable to false in your model before the database query you don’t want to cache, and enabling it after, if needed. It’ll make you model to generate a real fetch of data in the server. I believe that some of you are thinking which it can be avoided if you do any kind of data handling in your logic, avoiding the model to do repeated fetches to the database, and you’re right, but each situation is unique, and this need is not very usual. I just want you to be aware about the models query cache existence :)

Simple use of route prefixes within AuthComponent in a CakePHP App

In any regular website you have a restricted area for management, and a public webpage.
When writing a CakePHP app you would usually use the AuthComponent for authentication, and for allowing and denying actions. A boring task of using Auth is that you have to define in every controller that has public actions the allowed actions in that controller, it can be done by overriding the beforeFilter() method or by using any other technique, like looking for any defined variable with the allowed actions names and handle it in the AppController::beforeFilter(), or you can do it automatically! Yes, you can can assume a convention telling that every restricted action has a route prefix. So you won’t need to define any additional information on each controller.
The authorization logic for allowing users to reach a specified action is then set in the AppController::beforeFilter() by playing with the $this->params[‘prefix’] value.

A simple example is shown below:

We enable a single route prefix by editing app/config/core.php

	Configure::write('Routing.prefixes', array('admin'));

Then we edit our app/app_controller.php

class AppController extends Controller {

    public function beforeFilter () {
        if (empty($this->params['prefix'])) {
            $this->Auth->allow($this->action);
        } else {
            $this->layout = 'admin';
        }
    }

}

The example above checks for an non prefixed request (following our own conventions, it’ll be a public action), and if it is non prefixed just allow it, for example:

domain.com/posts/view/123

In other hand, if it is prefixed and there’s not a logged in user, it is not allowed, being denied by default.
So if a not logged user tries to request for:

domain.com/admin/posts/view/123

He is immediately redirected to the AuthComponent logoutRedirect url.

It’s just a very simple tip, i hope it helps someone :-D

Show SQL dump on redirect using CakePHP 1.3

Hi there!

An inconvenient i faced when i moved from CakePHP 1.2 to 1.3 is the fact of the debug SQL dump is now rendered as an element, this can be usefull if you want to customize or display it in any other place than in the bottom of the rendered output. But, in some situations, when you have an action without a view, those kind of actions usually redirect immediately after doing something, it means that the layout is never rendered, and as a consequence, the SQL dump too.

My solution is pretty simple and straight forward, i’ve just overrided the Controller::redirect() method in the AppController to detect if a debug was triggered (if any header was sent, it happens when you output something, in this case it’s sent by a debug() call), and if so, just render the sql_dump element, and stops.

The code is show below:

class AppController extends Controller {

    public function redirect ($url, $status = null, $exit = true) {
        if (headers_sent()) {
            $this->viewPath = 'elements';
            echo $this->render('sql_dump', false);
            if ($exit) {
                $this->_stop();
            }
        }
        parent::redirect($url, $status, $exit);
    }

}

find(‘list’) and Containable

As you must know, when calling $this->Model->find(‘list’) passing a contain parameter, it won’t take effect, and you won’t be able to do something like:


$workers = $this->Worker->Person->find(
    'list',
    array(
        'fields' => array('Worker.id', 'Person.name')
        'contain' => array(
            'Worker',
            'PersonType' => array(
                'conditions' => array('PersonType.type' => 'Worker')
            )
        )
    )
);

a solution for this problem would be something like this:


$workers = $this->Worker->Person->find(
    'all',
    array(
        'fields' => array('Worker.id', 'Person.name')
        'contain' => array(
            'Worker',
            'PersonType' => array(
                'conditions' => array('PersonType.type' => 'Worker')
            )
        )
    )
);
$workers = Set::combine($workers, '{n}.Worker.id', '{n}.Person.id'')

Well.. it’s not bad at all, but we could get a better solution – we could create a custom find type for a find list with Containable support!
We’ll be following the Matt Curry’s custom find method published in it’s awesome CakePHP e-book.

So, at first you must download Matt Curry’s find plugin vendor’s folder, put it under your app’s plugin/find/ folder, you should create the find folder.
Then in your app’s app_model.php you should import the find_app_model.php vendor file, and your AppModel class would extends FindAppModel, this would include some initial setup done for custom find methods. Now you just add our __findListContainable() method to it, it defines our custom find list method.
And you will end up with an AppModel like this:

App::import('Vendor', 'Find.find_app_model');

class AppModel extends FindAppModel {

    public $actsAs = array('Containable');

    public function __findListContainable($options) {
        $default_fields = array("{$this->alias}.{$this->primaryKey}", "{$this->alias}.{$this->displayField}", null);
        if(!empty($options['fields'])) {
            $fields = $default_fields + $options['fields'];
            unset($options['fields']);
        } else {
            $fields = $default_fields;
        }
        $results = $this->find('all', $options);
        foreach($fields as $key => $field) {
            if(!empty($field)) {
                $fields[$key] = '{n}.'.$field;
            }
        }
        return Set::combine($results, $fields[0], $fields[1], $fields[2]);
    }

}

Ok, now you can do any find list with containable parameters with no problems like the Worker’s example above.

Have fun!

Ubuntu Lucid Lynx 10.4 LTS with PHP5.2 / CakePHP1.1, Ubuntu Lucid Lynx 10.4 LTS, and PHP5.3

Note: If you want to downgrade to PHP5.2 under Ubuntu Lucid 10.4 LTS just scroll down till the bottom of the post.

Hello! Last weekend I decided to upgrade my linux distro to the new ubuntu one, I was using a 2 years old ubuntu distro and after the launching of ubuntu 10.4 LTS with PHP 5.3 packages included, i thought it was a great chance for trying it and keep myself updated. But I forgot a little, but important, detail: I’m currently integrated in a project that consists in developing an huge information system based on CakePHP 1.1, it was the stable version of CakePHP when the development started, in that time CakePHP was in its 1.2RC2 release – it’s not of my buisness, but i find always myself wondering how cool it would be if that person had thought the development and maintenance would take some years, and the 1.2 would become stable.. 1.3.. and so on..
Anyway, let’s talk about what matters, i could not develop with CakePHP 1.1 under PHP 5.3, and i needed to make it work till Monday , i was left with 3 solutions:

  1. Fix the CakePHP 1.1 core for running under PHP 5.3
  2. Downgrade the PHP install for 5.2
  3. Setup a virtual machine as close as the deploying environment.

Well… in my mind the first one one was inconceivable, i dont have enough time to spent with what i’m not paid for in my spare times. About the second one…i didn’t want to downgrade, i wanted to make it only as fallback option. So i went ahead and started with the third solution.

So i downloaded and installed VirtualBox, downloaded a Debian Lenny small ISO image, created a new virtual machine with two virtual network adapters, the first one in the host-only mode, that means that the VM could not reach the outside network, only the hosting machine, that connection creates a loopback interface for the communicatio. The second adapter i setup with NAT mode, it allows you to reach the outside world through the hosting system giving the WM a virtual ip address. Why this network setup? Because we want to have access to the outside world, for software packages downloading, etc.. and we need to have access to our VM HTTP server, and through the loopback device ip address we can do it. During the debian install wizzard it asked me what kind of software did i want to install, i answered with an webserver direct answer! And it setup a system with apache. I installed PHP5 packages and it’s apache2 module more  the database one. I decided to leave the database and the document root filesystem in my physical system, since this app has some extremely heavy components with lots of queries and calculations in the database, so i configured my postgresql for accepting connections in my local virtual ip address, and configured my app for connecting to the virtual host machine ip address. Installed an SSH client in the VM, generated a DSA SSH key with ssh-keygen, and put it in my host machine authorized_keys with no command execution, port forwarding, X11 forwarding, and PTY allocation allowed. Setup an init script for mounting it using SSHFS in the document root, running it as www-data, edited the default apache’s VirtualHost and changed the DocumentRoot directive accordingly. Restarted all services and voilá! Opened my browser, typed it’s ip address and I had my wonderfull CakePHP 1.1 app running under ubuntu 10.4. I had my victory.

After some testing i realized that, as expected, it was a bit slow while handling a big bunchs of data. And I pondered to go through the third solution and removed all PHP5.3 packages from the system, and found this explaining a strategy to install Karmic packages in 10.4 to run PHP as CGI, I prefer PHP5.2 to run as an apache module, so everyting i did was this:


# remove all php packge
sudo aptitude purge `dpkg -l | grep php| awk '{print $2}' |tr "\n" " "`
sudo su
# use karmik for php pakage
echo -e "Package: php5\nPin: release a=karmic\nPin-Priority: 991\n"  >> /etc/apt/preferences.d/php
apt-cache search php5-|grep php5-|awk '{print "Package:", $1,"\nPin: release a=karmic\nPin-Priority: 991\n"}' >> /etc/apt/preferences.d/php
apt-cache search -n libapache2-mod-php5 |awk '{print "Package:", $1,"\nPin: release a=karmic\nPin-Priority: 991\n"}' >> /etc/apt/preferences.d/php
exit
# add karmik to source list
sed s/lucid/karmic/g /etc/apt/sources.list | sudo tee -a /etc/apt/sources.list.d/karmic.list
# update package database (use apt-get if aptitude crash)
sudo apt-get update
# install php
sudo apt-get install -t karmic php5-cli libapache2-mod-php5

But i won’t give up of PHP5.3 anyway ;-)

EDIT: Quoting a comment from Pablo

A fourth solution could be install an older xampp package, which is isolated from “system’s” php packages and libs. xampp-linux-1.7.1 has php 5.2.9