(CakePHP) Get a Database Connection Instance

In my current project I am working on, I am using CakePHP and need to fetch some data from the database very early in the request cycle (don’t worry; it’s cached to avoid unnecessary queries).

In past versions of Cake you could use ConnectionManager::getInstance(). However, in 2.2 (and possibly in earlier versions of 2.x; I do not have the time to check), this no longer works.

The solution is to use the getDataSource method. So, by calling ConnectionManager::getDataSource('default') I can retrieve an instance of the default database connection and go along my day. Yay!

Advertisements

UPDATED (Laravel) Auto Generate Named Routes For Easy Reverse Routing

UPDATED: It occured to me that having the route names match the controller name might not lend itself well to preventing the need to update multiple places in the event of a controller’s name changing. I have updated this post with a better method to tackle that.

What *Is* Reverse Routing?
Reverse routing is an awesome feature. In Laravel (as well as other frameworks) when you create a route you can also define a name for it. Then, when you need to generate a link in your app, instead of doing your standard

<a href="/blah/bler/blo">Link</a>

You can do this:

<a href="<?php echo URL::to_route('some_route', array('bler', 'blo')); ?>">Link</a>

The difference, you ask? Let’s say later you decide to rename the Blah controller to Clover. You’d have to go through your entire app and replace any links starting with “blah” with “clover”.

With reverse routing, you’d make exactly one replacement: in the routes.php file. Any links in your app using the second example will automatically go to the clover controller. Yay!

So How Do I Do The Magic?

As you can tell, I very much like using reverse routing. However, I realized that Laravel does not automatically generate names for the routes created by Route/r::controller (which is not a critical flaw; not everyone has the need for it, and that’s fine). After a bit of digging and head-scratching I came up with a fairly easy way to have it do the work of naming all routes for me.

NOTE: This requires manually setting your controller names instead of using Controller::detect() (probably not a bad idea anyway since it’s less file lookups for Laravel to do)

1. Open routes.php and list the controllers for the bundle. If you want to specify a name, do a key => val pair. Example:
Route::controller(array(
// This will set the admin/home controller as a route named foo
'foo' => 'admin.home',
// This will be a route called users, since we did not set a name
'users'
));

2. Create 2 files in your application folder. I placed mine in application/libraries. route.php and router.php.

3. Open up router.php and put the following into it:

<?php

class Router extends Laravel\Routing\Router {

/**
* Register a controller with the router.
*
* @param string|array $controller
* @param string|array $defaults
* @param bool $https
* @return void
*/
public static function controller($controllers, $defaults = 'index', $https = null)
{
foreach ((array) $controllers as $as => $identifier)
{
list($bundle, $controller) = Bundle::parse($identifier);

// First we need to replace the dots with slashes in thte controller name
// so that it is in directory format. The dots allow the developer to use
// a cleaner syntax when specifying the controller. We will also grab the
// root URI for the controller's bundle.

///// Here is where the named route will be set. If the $as variable is numeric,
//// we will use the controller name
(is_numeric($as)) and $as = $controller;
$controller = str_replace('.', '/', $controller);
$root = Bundle::option($bundle, 'handles');

// If the controller is a "home" controller, we'll need to also build a
// index method route for the controller. We'll remove "home" from the
// route root and setup a route to point to the index method.
if (ends_with($controller, 'home'))
{
static::root($identifier, $controller, $root);
}

// The number of method arguments allowed for a controller is set by a
// "segments" constant on this class which allows for the developer to
// increase or decrease the limit on method arguments.
$wildcards = static::repeat('(:any?)', static::$segments);

// Once we have the path and root URI we can build a simple route for
// the controller that should handle a conventional controller route
// setup of controller/method/segment/segment, etc.
$pattern = trim("{$root}/{$controller}/{$wildcards}", '/');

// Finally we can build the "uses" clause and the attributes for the
// controller route and register it with the router with a wildcard
// method so it is available on every request method.
$uses = "{$identifier}@(:1)";
///// Just adding the as variable to the compact statement so it'll get set upon register
$attributes = compact('uses', 'defaults', 'https', 'as');

static::register('*', $pattern, $attributes);
}
}

}

Here we are basically adding a couple of lines so the named route (defined by the ‘as’ attribute) will be created for us when Laravel is detecting controllers & generating routes.

4. Open up your route.php file and add the following:

<?php

class Route extends Laravel\Routing\Route {
/**
* Register a controller with the router.
*
* @param string|array $controllers
* @param string|array $defaults
* @return void
*/
public static function controller($controllers, $defaults = 'index')
{
Router::controller($controllers, $defaults);
}

/**
* Register a secure controller with the router.
*
* @param string|array $controllers
* @param string|array $defaults
* @return void
*/
public static function secure_controller($controllers, $defaults = 'index')
{
Router::controller($controllers, $defaults, true);
}
}

The reason we need to extend the route.php file as well is that the default file (found in laravel/routing/route.php) points to laraveroutingrouter, which means our custom router class would never get used. So, we simply override the controller functions and add the handy backslash in front of the Router call so it will look for our router file.

5. Open up application/config/application.php and comment out Route and Router in the aliases section.

You should now be have auto-generated named routes. The best part is that any custom route names you define will not be overridden by this method.

(Laravel) Quick Tip: Dynamically Route a Controller

When I’m working on a controller, I usually will combine my add/edit methods to help keep my controllers lean and mean. In the other frameworks I used this was generally as easy as adding a route like so


// CodeIgniter
$config['([a-z\_]+)/add'] = '$1/edit';
// Kohana
Route::set('manage', '<controller>/add', array(
'controller' => '[a-z0-9\_]+'
))
->defaults(array('action' => 'edit'));
// CakePHP
Router::connect('/:controller/add', array('action' => 'edit'));

However, in working with Laravel it just wasn’t that easy, probably because Laravel expects all controllers to be defined so it can do a super-quick search for them at runtime. I came up with a (hacky) solution that modified the core controller class (while I really enjoy working with Laravel the fact is its core just isn’t terribly extensible). I really hate modifying core classes.

And then – the lightbulb went on! I remembered that a) I can use anonymous functions for routing and b) Controller::call can be used anywhere.

NOTE: For now this only appears to work in subfolder controllers. It seems (from examining the generated route list) that any route that *starts* with a wildcard (eg, (:any)) will be moved to the bottom of the list. For me, this wasn’t a big deal; my add/edit classes are almost always in admin folders anyway.


Route::any('admin/(:any)/add/(:any?)', function($controller,$params=null) {
return Controller::call('admin.'.$controller.'@edit', (array) $params);
});

There you have it! 🙂

Quick Tip: Laravel 3.2.0 Auth Driver

I am upgrading a current project to Laravel 3.2 and had a bit of confusion with the (newly implemented) Auth drivers. I’m sure a lot of developers are able to use the Auth class as-is, but because I need some custom stuff returned when logging in/retrieving a user, I needed to figure out how to re-implement this logic.

Previously, the auth.php config file held the logic for logging in and retrieiving a user. Now, that logic has been moved to drivers (which, really, makes much more sense, but that’s just my opinion). With some help from the (awesome) forums, I was able to quickly port my code over.

1. Create a file in /libraries. For example purposes, we’ll call ours nerdy.php
2. The skeleton class for an Auth driver should look like this:

<?php
class Nerdy extends \Laravel\Auth\Drivers\Driver {

public function retrieve($id)
{
}

public function attempt($arguments=array())
{
}
}

2. Add your logic to the 2 methods.
3. Open up your application/start.php file and add the following:
Autoloader::map(array(
// Of course, you would change the class name and path to
// the actual name & path for your app.
'Nerdy' => __DIR__.'/libraries/nerdy.php
));

Auth::extend('nerdy', function() {
// Again, the class name should be the name you chose
return new Nerdy;
});

4. Finally, open your applicatin/auth.php file and change the driver value to (in this example) nerdy and save.

Not so bad once you know what to do! 🙂

(Source)

Extending CodeIgniter’s Database Forge Class

UPDATED 2/13: Added License (MIT) Info to code
UPDATED 1/13: Changed ‘required’ ‘required_once’ to avoid errors of redeclaring CI_DBforge

I am working on a project in CodeIgniter that requires use of the database forge class to create/modify/delete database tables. Because the project makes use of database transactions, creating tables with the MYISAM engine was not going to work. So I was faced with two choices: Make all the database creation, etc run by manual queries such as
CREATE TABLE `blah`...
or extend the dbforge class. After some tinkering about I finally managed to successfully do this. Here’s how:
(Note: I am using CI 2. This also assumes you are somewhat familiar with extending CI classes)

  1. Create a MY_Loader.php file in application/core (or open it if you already have one).
  2. If you had to create the MY_Loader file, paste the following:
    <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
    class MY_Loader extends CI_Loader {
    }
  3. Paste the following in your MY_Loader file:

    /**
    * Extend the Database Forge Class
    *
    * @access public
    * @author (Original) EllisLabs / (Extension) J Elmore
    * @license MIT
    * @version 1.0
    * @return string
    */
    function dbforge()
    {
    if ( ! class_exists('CI_DB'))
    {
    $this->database();
    }

    $CI =& get_instance();
    $class = 'CI_DB_'.$CI->db->dbdriver.'_forge';

    require_once BASEPATH.'database/DB_forge'.EXT;
    require_once BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge'.EXT;

    if( file_exists(APPPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge'.EXT) ) {
    require_once APPPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge'.EXT;
    $class = 'DB_'.$CI->db->dbdriver.'_forge';
    }

    $CI->dbforge = new $class();
    }

    This basically adds the code to find/include the extended class, and instantiate it

  4. Next you need to create some folders in your application directory. First, create the folder database. Then, within the database folder, you’ll need to create a drivers folder. Within the drivers folder you’ll need to create a folder for the database type (mysql, mssql, etc) you are using. Since I am using MySQL, I created a mysql folder. My folder structure, then, looked like this:

    application/
    -->database/
    ----->drivers/
    -------->mysql/
  5. In the driver folder you created (in my case, the mysql folder), create a file in the format _forge.php. In my case I created mysql_forge.php.
  6. Create your extended class:

    <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
    class DB_mysql_forge extends CI_DB_mysql_forge {
    }
  7. Have fun extending the functions! To get my tables to specifically create with the InnoDB engine I used the following code:

    /**
    * Modify _create_table to use InnoDB
    *
    * @access public
    * @author (Original) EllisLabs / (Extension) J Elmore
    * @license MIT
    * @version 1.0
    * @return string
    */
    function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists)
    {
    $sql = parent::_create_table($table, $fields, $primary_keys, $keys, $if_not_exists);

    $sql = rtrim($sql, ';');
    /* Should note here that I have a custom config file with the key/value pair to hold the
    default database engine to use */
    return $sql.' ENGINE='.config_item('database_engine').';';
    }

I tried simply extending the dbforge class but since all the magic happens in the driver classes it was necessary to extend the driver and modify the functions there. Also, I know there are ways to set the mysql server to default to InnoDB creation but since this project may be sold/released at some point it is easier to modify the CodeIgniter files than to try and provide instructions for something most people wouldn’t be able to or know how to do.

Bespin Theme for Geany IDE

I love the Bespin theme that can be found for Textmate, Notepad++, and Gedit. Recently I started using the Geany IDE (http://www.geany.org/) as it’s a lot lighter than Eclipse/Netbeans/etc.

I spent the better part of an afternoon using the Online Theme Editor found at (http://geany.pdarko.co.cc/filetypes/) to recreate (to the best of my abilities) this theme which can now be found at http://github.com/nerdmom/Geany-Bespin-Theme. Please note that I only changed a few of the file definition files as that was all I needed to work with. If you are using a language other than HTML, PHP, and Javascript you’ll have to visit the theme editor I linked to above and make the changes there.

Enjoy!

A Follow-Up on Kohana 3 & CodeIgniter 2 – The Flip Side

The other day I wrote about my experiences/opinions with Kohana 3 and CodeIgniter 2. I’ve received feedback from both sides of the debate, and want to share some follow up thoughts and some of (IMO) CI2’s downsides.

Shadowhand commented:

…This is typical of pretty much all programming tools. For example, look at Python right now… they have been on the 2.x series for a long time and 3.x is not backward compatible.

I will be the first to admit that Kohana has had development instability in the past, but 3.x is rock solid and has a real release process in place. Please help us by judging 3.x separately from other versions.

I should say that my basis for opinion on the “instability” of Kohana has been based on its rewrite between versions 2 and 3, and the rapidity in which those major versions came out. IIRC, the 2.3 series (when I first started dabbling with it) was released in the early-mid 2008, and the 3.0.x series started to become available in mid-late 2009. If I had written an app for release with 2.3 in late 2008 and then been faced with a rewrite a year later that would’ve cost quite a bit just in man hours just to learn the framework (again) and figure out a semi-painless way to migrate my existing code to work with a new code base. Not to mention rewriting docs for my users, and so on.

To be completely fair, CodeIgniter 2 has a lot of its own shortcomings. I previously mentioned the procedural helpers, and the fair bit of hacking required (for me) to get it working the way I want (usually involving modules and autoloading, which yes, are a core part of Kohana). Another thing that irks me with CI is how the config items are lumped into one huge array which can make it very difficult at times to ascertain which config file I got a variable from, not to mention the possibility of item name collisions. Usually to get around this I would simply prefix my custom config files with file_configitemname, but that’s not entirely helpful for the core config files.

And please, PLEASE don’t get me started on using “eval()” in the database class. I know it’s a bit of a workaround…and a lot of times I use my own class or Doctrine, but even so..

Stewart commented

…I do have a problem with how they went about it (they should of made it clear KO2 had no future, rather than stringing people on and a new version number doesn’t really make it clear it is really a new framework.

This sums up my feelings pretty well. From what I’ve been able to ascertain (looking through commits on github and such), the API has remained fundamentally the same in the latest series.

And perhaps, for me, that was it. I just want to know there is fundamental stability within the framework itself, without the worry that my code will have to be completely reworked every 12-16 months.

FWIW I do find developing with Kohana quite easy and fun, and if the recent fundamental stability holds out I may be singing a different tune.

I should also take a moment to thank both EllisLab and the Kohana dev team for putting the countless hours into developing these frameworks so I don’t have to! 🙂

Kohana 3 & CodeIgniter 2

For the last month or so I’ve been developing an app side-by-side with Kohana 3 and CodeIgniter 2 to see which framework fits my programming style better. Nothing terribly fancy; just a simple CMS.

I’ve been using frameworks to develop since 2006, back around CI 1.5.x. The differences between CI 1.7.x (the latest version as of this post) and CI 2.0 are negligible. My biggest nitpick comes with the helpers. I’m lazy and I’m also somewhat speed-conscious, which means I either need to autoload all the helpers my app needs in the autoload.php file (or early in my app) to appease my laziness, or load them as needed to appease my speed-consciousness. It irritates me that they’re basically files holding groups of procedural functions and if I want to access the CI super global I have to instantiate it in every function.

I started dabbling with Kohana a couple of years ago, in the 2.3.x era. I really loved their use of static functions (I’d only had basic OOP experience at the point), but I just never felt comfortable with the way things changed so much between versions. Last year when v3 was announced I had a look through the code and said “forget it”. But eventually I became curious again and started learning it to see how easy/hard it was to develop with it. I personally did not have a lot of trouble learning Kohana 3, between the user guide and the unofficial wiki. Perhaps my biggest stumbling block came with the Database module, which was a complete change from any of the v2.x database classes.

I really didn’t find either framework harder than the other to code for. I was able to more or less figure out how to do what I wanted to do in both. Yes, Kohana’s docs suck and CI’s are much better. For me it wasn’t much of an issue.

In the end it comes down to framework stability for me. And in this case, CI wins, hands down. Don’t get me wrong; Kohana is beautifully written and introduced me to a prime example of clean, well commented code. The devs are extremely intelligent and more talented than I’ll probably ever be. CI is also very clean, though there’s a bit more hacking involved for me to get it to work the way I like. However, I don’t want to be writing my own clean, well-commented code for one version of a framework only to find out the next version is going to totally break backwards compatibility. The lead Kohana devs have stated that there are no guarantees of stability between major versions (ie 1.x-2.x, etc). Comments like “well if it ain’t broke, don’t fix it” are dandy, but if part of the reason I’m using a framework is to let someone else worry about the basics for me, why would I stick with something that is no longer actively developed because the devs are on version 342.x?

While I respect progress and not letting code stagnate there’s something to be said for code stability when you’re maintaining a framework. Uber-optimising something is great (as there are repeated mentions of “x function is faster than w function” in the Kohana forums) but if it’s only saving me 0.02 ms of page load time or 0.25 mb memory, I’m not sure unstable code is worth that bit of extra speed. Furthermore, using an older codebase when you’re a one-(wo)man show can be a nightmare, since it’s a lot easier for one person to miss something than two, whether it’s a simple invalid method call or a glaring security hole.

I want to create apps to earn a living and I want to know that my code will stay more or less relevant between framework versions. API changes are to be expected between versions but complete rewrites, with very little thought for BC is just poor project management, IMO.

Please bear in mind that these are simply my views on the frameworks and my experiences and since I’m not a BDFL PHP guru (or something), you should perform your own research to see what works for you.

Update: I’ve posted a follow up post with some more thoughts (and such): https://nerdmom.wordpress.com/2010/08/31/a-follow-up-on-kohana-3-codeigniter-2-the-flip-side/

CakePHP 1.3, Smarty 3, and Theming. Woohoo!

There are various solutions out there for integrating Smarty with CakePHP. I’m here to introduce another one, that I feel is a little leaner than the other solutions I’ve found.

Jump to the code: https://gist.github.com/672398

Requirements:

  • Cake 1.3.2 (No idea if it will work on < 1.3.2)
  • Smarty 3

Just place this in your /app/views folder andmake sure your AppController has the following lines:

public $view = "Theme";
public $layout = "default";
// The following line is optional; the default ext in the ThemeClass is set to .html.
// Uncomment to set to your desired extension
// public $ext = ".some_extension";

Your views would (unless you change the $ext variable) then be saved with .html extensions.

CakePHP, Using Edit For Add/Edit Functions, & Plugins (Oh My!)

When I work on a project I usually combine my add/edit functions into just edit. This saves some space in my controllers and makes maintenance a lot easier as I have only one function to edit instead of 2.

To accomplish this in Cake, you’d just do something like this:
Router::connect('/:prefix/:controller/add', array('action' => 'edit', "admin" => true));
This will map your controllers in your “app/controllers” folder nicely.

But let’s say you’re building an app that includes plugins. You may think “Ha hah! I’ll just slap this in my routes.php file”:
Router::connect('/:prefix/:plugin/:controller/add', array('action' => 'edit', "admin" => true));
And it will work..kinda.

More specifically, it will work for the subcontrollers of your plugin. For example, if you have a news plugin, and the news plugin has a comments controller, the above route will work, because it will map “/admin/news/comments/add” to /admin/news/comments/edit”.

HOWEVER!! This will NOT work for the news controller itself
Dramatic Chipmunk

*ahem* Luckily, the fix is easy and only took me 2 days to figure out (hey, I have kids; things get put aside…yeah, that’s my reason)
Router::connect('/:prefix/:plugin/add', array('action' => 'edit', 'admin' => true));
Now you can access “/admin/news/add” as you’d expect!