joebeeson http://blog.joebeeson.com programming, knick-knacks, minutiae posterous.com Fri, 10 Feb 2012 18:51:00 -0800 Lithium - Diving into the core, Collection http://blog.joebeeson.com/lithium-diving-into-the-core-collection http://blog.joebeeson.com/lithium-diving-into-the-core-collection

Taking us into another episode of “Lithium – Diving into the core” is the Collection object which is, possibly, one of the most undervalued classes in the entire library. With a little bit of luck you’ll come out of this post with an inkling of just how integral this bit of code is to everything you do in the library.

Purpose

The Collection object is, you guessed it, a means of storing a collection of information. While you would usually delegate such responsibility to an array, by using an object we get a lot of extra functionality without sacrificing its array nature… yes, array nature.

SPL

The Standard PHP Library, or SPL, is a PHP 5.0+ extension which allows you to do a lot of really neat things in PHP, not the least of which is the ability to treat an object as though it were an array.

Let that sink in for a second. You can treat an object as though it were an array. Makes sense actually, why shouldn’t you be able to iterate over an object in the same manner you could an array? Well, with SPL you can.

This is useful to note because the Collection object and all of its children implement just such functionality. This lets us reuse the entirety of PHP’s array functions on the object along with the power of, well, objects.

(If you haven’t taken the time to check out the SPL and all it has to offer, I’d really recommend doing that. You’ll learn some neat things in the process)

Usage

For the most part you probably won’t ever have to use the Collection object directly, many parts in the library that return data will give you one automatically.

$posts = Posts::all(); 
foreach ($posts as $post) { /* Work with the record */ }
var_dump(is_array($posts)); // Nope
var_dump(is_object($posts)); // Yep!

One of the most common places you’ll run into the Collection object is when dealing with a model’s recordset. Note that running is_array against it will return false — this is because even though we can treat it like an array, it isn’t one. If you have any checks in your views or such that use is_array you may want to think about changing those out for something like this:

if (is_array($posts) or is_a($posts, '\ArrayAccess')) {
    /* We can treat it like an array */
}

What we’re doing here is allowing the $posts variable to be either a full-fledged array or an object which implements ArrayAccess which is the interface that provides the ability to iterate an object as an array.

Methods

Because of this whole array access functionality the object has to keep a handful of methods around to tell PHP how to properly access it as one. You can safely ignore these as they’re mostly for PHP.

Not to fear though, the Collection object still has a couple of tricks up its sleeves that are worth noting.

Manipulation

$posts = Posts::all();

/**
 * Note that since `$posts` is a Collection object full
 * of record objects, in our closure we will get the actual
 * record object!
 */
$myPosts = $posts->find(function($post) {
    return $post->author == 'Joe';
});

Here’s the find method for example. It wraps around the array_filter method with the provided callback and, when it’s all done with its filtering, will hand you back a brand new Collection object with the values you wanted.

This is really useful for retrieving a specific subset of records that you’ve already retrieved, that way you can save a hit against your database.

The class also has map, a wrapper around the array_map function that, like array_filter, will return you a new Collection instance with the updated values.

Last but not least is the sort method which does exactly what you think it does. The first argument for the method is a callable argument which the instance will use to sort itself.

Formatting

While the Collection object is a great place to store information, it knows that you may need to, at some point, change its data out into another format. For that it provides the to method.

$posts = Posts::all()->to('array');
var_dump(is_object($posts)); // Nope.
var_dump(is_array($posts)); // Sure is.

Collection comes with one formatting built in, array, which will simply return you an array of its data. This is handy when you absolutely need to have an array of your data.

We’re not done there yet though. The formatting in Collection is a sort of “dumb” object in that it delegates the actual process of formatting somewhere else, this means we can register our own. From the source:

Collection::formats('json', function($collection, $options) {
    return json_encode($collection->to('array'));
});

Now if we did $posts->to('json'); we’d get a lovely string of JSON data which represents our original data. Pretty nice, huh?

Caveats, notes

Sometimes you’ll think you’re working with a Collection instance but are instead dealing with an array. This is usually indicative of a call to Collection::data or Collection::to('array') somewhere in execution. Watch out for these.

While it’s tempting to put all of the possible ways you could format your data into Collection::formats, don’t go overboard and forget that it is still the primary job of your view to handle any appropriate translation of data into the requested format.

Closing

Again, hopefully this has been helpful to illustrate the functionality of the Collection object and the lithium library as a whole. As always feel free to comment here, on Twitter, IRC, etc, if you have any questions or comments!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Thu, 09 Feb 2012 19:05:00 -0800 Lithium - Diving into the core, Adaptable http://blog.joebeeson.com/lithium-diving-into-the-core-adaptable http://blog.joebeeson.com/lithium-diving-into-the-core-adaptable

Bringing us to another installment of “Lithium – Diving into the core” is the Adaptable object. Adaptable gives us the the ability to provide an interaction interface without hard wiring the concrete implementation to perform said interaction.

While this may be a bit long winded, it’s really worth it as Adaptable is one of the most useful classes in the entire library. So grab some coffee, maybe pop open the repository to follow along and kick your feet up.

A great example of the Adaptable object is the Session object. A session, at it’s most basic, is a means to persist information about the current user across requests. As such it implements a read and write method, along with some others, to do its job. The beauty of Adaptable is that it actually allows the class to leave the heavy lifting to its adapters; in this case Cookie, Memory or Php

Implementation

Comparing the Adaptable and Session object you’ll notice that it’s the job of the adapter to actually implement the interface it will end up providing. For Session this is pretty straight forward: read, write, delete, clear and check — all things that make sense when working with a session.

When creating your own Adaptable subclass you’ll need to determine the methods that make sense when dealing with the problem in question. Here’s a couple examples off the top of my head to help illustrate this point:

  • Bank deposit, charge and balance — Credit card, debit card or check, these are all applicable actions.
  • Music Player play, stop, status, songs — iTunes, Winamp, VLC, etc. These are universally recognized actions.
  • SocialSite getPosts, addPost, message — Facebook, Twitter anyone?

The thing to take away from this is that the adapter must be able to implement all of the methods for the class that uses it. The goal is to let someone swap adapters at anytime and not to have the code notice anything different.

Configuration

Continuing with the Session object lets take a look at how it’s configured in app/config/bootstrap/session.php

Session::config(array(
    'cookie' => array('adapter' => 'Cookie', 'name' => $name),
    'default' => array('adapter' => 'Php', 'session.name' => $name)
));

Here we’re defining two configurations: cookie and default. One uses the Cookie adapter and the other Php. This tells the Session object which adapter to utilize when we use a specific configuration.

You’ll notice that there is some extra, specific configuration bits. This is normally ignored by the Session object (aside from “strategies”, more on that in a bit) and instead used to help configure the specific adapter.

Why would you need to pass options to an adapter? Well when writing your own adapters you may require certain things: API keys, URLs, API version, etc. The configuration is the place for those. It is not, however, the place to store data that should otherwise be passed in as an option when calling a method in the adapter.

Anatomy of a request

Alright, now lets get our hands dirty. To do the Adaptable object justice we’ll need to look at an actual method in the Session object. Bear with me, here.

public static function read($key = null, array $options = array()) {
    $defaults = array('name' => null, 'strategies' => true);
    $options += $defaults;
    $method = ($name = $options['name']) ? static::adapter($name)->read($key, $options) : null;
    $settings = static::_config($name);

    if (!$method) {
        foreach (array_keys(static::$_configurations) as $name) {
            if ($method = static::adapter($name)->read($key, $options)) {
                break;
            }
        }
        if (!$method || !$name) {
            return null;
        }
    }
    $filters = $settings['filters'] ?: array();
    $result = static::_filter(__FUNCTION__, compact('key', 'options'), $method, $filters);

    if ($options['strategies']) {
        $options += array('key' => $key, 'mode' => 'LIFO', 'class' => __CLASS__);
        return static::applyStrategies(__FUNCTION__, $name, $result, $options);
    }
    return $result;
}

(I’m going to gloss over certain areas that aren’t fundamentally important to this discussion — I recommend you open and read the Session.php file to dig in further)

Now if you didn’t look closely you may have missed it. The key here is:

$method = ($name = $options['name']) ? static::adapter($name)->read($key, $options) : null;

If you’ve told it which configuration to use it will call the read method on that adapter. The if block below it is triggered if you don’t tell it which configuration to use so that it can iterate over all of its adapters and try the read method on each.

That’s it!

Since each adapter knows if it’s capable of responding and will if it can, the Session object just needs to loop over and ask each to read from its concrete implementation; Php, Memory, Cookie, whatever. How they actually get their information is immaterial to the Session object itself.

The static::_filter call is your run of the mill trigger for any filters that may be waiting to listen for or manipulate the data. A neat thing to point out is that if there are filters attached, they don’t need to be attached to the concrete implementation (Php::read for instance) but instead can filter the Session class directly.

Now, about those other bits…

Strategies

Strategies are, in a way, Lithium provided filters that can operate on an action (Session::read in this case) regardless of what the concrete implementation may be.

Similar to adapter objects they may have their own configuration and should implement the same methods that the “base” class does when it needs to operate. A perfect example of this is the Encrypt strategy.

Like our adapter it implements read and write but unlike them it doesn’t care about check, delete, or any of the others. This is because there really isn’t any encryption or decryption to be done when you just want to delete an entry. The strategy should implement all the methods it needs to perform its task.

On the other hand, the Hmac strategy, which maintains a signature to confirm the validity of its data does need to tack into delete since the signature will change when data is removed.

Both Encrypt and Hmac need to store extra data away to do their jobs. For instance Hmac needs to write away the signature for future requests to confirm there has been no tampering.

$class::write('__signature', $signature, array('strategies' => false) + $options);

This is the line from the Hmac strategy to store away the signature its compiled and you’ll notice that it uses the write method to tuck its signature away. Again, because the $class (which is Session in this case) merely delegates off to an adapter, Hmac doesn’t care who that may be. To make sure other strategies don’t interfere with its operation or to cause an infinite loop, it tells Session not to trigger any strategies when performing its write

A quick note to bring things full circle. From our little SocialSite example above, it’s pretty easy to think up some strategies that could be used for all implementations:

  • UrlShortener When posting a message find any URLs and shorten them with a service.
  • Shortener Attempt to shorten a message by replacing common words with acronyms.
  • LinkReplacer When reading messages, find anything that looks like a URL and wrap it up in the appropriate HTML.

(These are just meant as crude examples)

Notes

  • A strategy should, more or less, be completely transparent to the operation and removing or adding one should not stop the class from performing its duties.
  • An adapter must be able to perform all the methods available to the application from the abstract object (Session for example)
  • An adapter should be interchangeable without impacting the application that may be using it aside from possibly requiring certain configuration.

Closing

whew — Hopefully this gives you a better grasp of the Adaptable object and its children and their purpose in Lithium. Now, your homework is to go crack open the Session.php and Cache.php files to try and apply your new knowledge to better understand them.

Extra credit for those who go read the strategies.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 08 Feb 2012 14:03:00 -0800 Lithium - Diving into the core, Environment http://blog.joebeeson.com/lithium-diving-into-the-core-environment http://blog.joebeeson.com/lithium-diving-into-the-core-environment

The Environment class is a core, static object which will inspect and determine the environment based on certain criteria from the current request. One of the neatest bits of functionality it gives us is that it will automatically configure any Adaptable class based on the current environment. This lets us do things like:

Cache::config(array(
    'userData' => array(
        'development' => array('adapter' => 'File'),
        'production' => array('adapter' => 'Memcache')
    )
));

Now, depending on the environment, when we attempt to use the Cache class against the “userData” configuration it will automatically use the appropriate environment.

Determining environment

While this is all well and good, how does the Environment class determine the current environment in the first place? Looking at the class we don’t see anything which is automatically triggered to actually determine which environment we’re in. That’s because there is none. It needs to be told about the current request.

If you’re using the framework repository as your basis (which you should be!) then the file you want to look at is app/config/bootstrap/action.php

Dispatcher::applyFilter('run', function($self, $params, $chain) {
    Environment::set($params['request']);

    foreach (array_reverse(Libraries::get()) as $name => $config) {
        if ($name === 'lithium') {
            continue;
        }
        $file = "{$config['path']}/config/routes.php";
        file_exists($file) ? include $file : null;
    }
    return $chain->next($self, $params, $chain);
});

The line in question is Environment::set($params['request']) which grabs the Request object from the Dispatcher and throws it at the class. From there the class will trigger the _detector method and figure out the current environment.

One thing to notice, and it’s a big one, is that your Environment object will not know where it “is” until this filter executes which means waiting for the Dispatcher::run method to fire. This also means that using Environment anywhere in your bootstrap, without being wrapped in a filter that comes after Dispatcher::run, will not give you the desired results.

Environment detector

When Environment::set gets the Request object it turns around and fires it at its Environment::_detector method which handles the dirty work of determining the environment and returning the name.

Inside _detector you’ll see that it checks for a static member variable called $_detector (more on that later) before returning its own, built-in, closure to handle determining the environment.

Custom detector

Now that we’ve got that out of the way, let’s see how we can define our own detector for the Environment class to use. This will let us create new environments or add criteria for choosing an environment.

Remember earlier when we mentioned the static member variable, $_detector? Well Lithium lets us override the built-in closure for determining the environment with our own closure by setting this variable. Here’s how…

Environment::is(function($request) {

    /* Determine which environment and return as string */

    // Always be sure to fall back onto "production"    
    return 'production';
});

This will store away our closure into the object for use when it receives the Request object (like, during the Dispatcher::run filter above) — always, always, always be sure that your closure returns a value of some sort. The value can be any string you want but it will become the name of the current environment.

Since the call to Environment::set is responsible for setting our current environment you’ll need to set your custom detector before it is triggered, otherwise it won’t end up being used.

Another note is to remember that this call is destructive and not additive. You won’t receive any of the built-in checks if you add your own detector.

Arbitrary environment specific storage

This is actually the easiest part and is fairly well covered in the documentation itself but while we’re here, a quick example.

Environment::set('production', array('debug' => false));
Environment::set('development', array('debug' => true));

Later, performing Environment::get('debug') will give us the corresponding value depending on the environment that was detected. The get method also allows us to use a convenient dot notation for getting at nested values.

Closing

Hopefully this has been a fairly clear outline of the awesome Environment class and a bit of its inner workings. If you have any questions, feel free to ask!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Thu, 15 Sep 2011 10:19:00 -0700 The Greatest Speech Ever Made http://blog.joebeeson.com/the-greatest-speech-ever-made http://blog.joebeeson.com/the-greatest-speech-ever-made

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 14 Sep 2011 13:20:00 -0700 Untitled http://blog.joebeeson.com/70271962 http://blog.joebeeson.com/70271962

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 14 Sep 2011 07:33:00 -0700 Unreal Tournament + Nyan Cat = Awesome http://blog.joebeeson.com/unreal-tournament-nyan-cat-awesome http://blog.joebeeson.com/unreal-tournament-nyan-cat-awesome

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Tue, 13 Sep 2011 19:33:00 -0700 Leaning on CakePHP http://blog.joebeeson.com/leaning-on-cakephp http://blog.joebeeson.com/leaning-on-cakephp

There is a ton of information that CakePHP sets up for each request. Looking at the Controller object API, you can see that a sizable chunk is devoted to maintaining the data about the current request (as it should be) but what, if anything, are you doing with that information? Here's some tidbits I've ran into while developing a plugin that you can use to start "leaning on CakePHP" in your application.

One of the first things you should do for any application is to create your basic CRUD and index actions in the AppController. This give us a reliable base to build upon, helps keep things DRY and make our lives easier down the road by easily transitioning into API verbs. Here's an example of the index method...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

/**
* Handles index requests.
*
* @return void
* @access public
*/
public function index() {

    // Only do this work if our concrete controller has not.
    if (!isset($this->viewVars[Inflector::pluralize($this->modelClass)])) {
        $this->set(array(
            Inflector::pluralize($this->modelClass) => $this->paginate(
                $this->modelClass,

                /**
* Here we toss in any conditions that were found as named
* parameters and which match up to a column in the model
* schema.
*/
                array_intersect_key(
                    $this->params['named'],
                    $this->{$this->modelClass}->schema()
                )
            )
        ));
    }
}

As this is intended to go into the AppController you can see that we're using $this->modelClass to determine the controller's default model. We also don't perform any "work" if we see that it has already been done by the controller. Finally, as a bit of nice to have code we are looking in the named parameters for anything that matches a column name and automatically applying that as a condition to our pagination. This lets us easily create an address such as /posts/index/author_id:1

One thing to point out here is that this can easily be overridden by the controller. If it needs to do more than is available we can easily create an index method in the "concrete" controller and only call parent::index() if it's still usable. Let's check out another method...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

/**
* Handles deletion requests.
*
* @param $id string
* @return void
* @access public
*/
public function delete($id) {
    if ($this->{$this->modelClass}->delete($id)) {
        $this->_flashSuccess('Record successfully deleted.');
        $this->redirect(array(
            'action' => 'index'
        ));
    } else {
        $this->_flashError('There was an error deleting the record.');
    }
}

[There are some custom methods defined in there that should be relatively self-explanatory] This one is pretty straight forward: The user asked us to delete something, we're going to attempt to delete it and based on the outcome give the user a textual response and possibly redirect them to the index action of the current controller.

I can already hear you saying "But... but... Joe, what if they don't have permissions to delete the record?" well, that's not the job of this method to determine. These are only methods to be built upon. Hence why they're in our AppController. Perform your authentication checks in the concrete controller and only call the parent method if everything checks out.

Alright one more now, my favorite.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

/**
* Handles update requests.
*
* @param $id string
* @return void
* @access public
*/
public function update($id) {
    if (!empty($this->data)) {

        /**
* Since the `create` method does most of the dirty work for us
* we simply set away the ID for the records we're attempting
* to update and fire off our request.
*/
        $this->data[$this->modelClass][$this->{$this->modelClass}->primaryKey] = $id;
        $this->create();
    } else {

        /**
* By using our `view` method we'll get all of our view variables
* setup for us.
*/
        $this->view($id);
    }
}

[This code is extremely verbose because of the plugin's nature] Here we're leveraging on our own code. If the user hasn't sent any data we trigger our view method to get all of our view variables setup (and possibly perform authentication) before rendering. If the user has sent us data we simply toss in the ID of the record they're updating and then trigger our create method. How easy was that?

With this base of simple, straightforward CRUD methods you can save a large amount of time by reusing code and only writing modifications for specific cases.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Tue, 13 Sep 2011 12:23:00 -0700 My development toolchain http://blog.joebeeson.com/my-development-toolchain http://blog.joebeeson.com/my-development-toolchain

The question comes up often enough on IRC that I thought I'd go ahead and share my development "toolchain" -- the tools I use to get work done day to day.

Where it begins...

My company provided Dell Latitude E6520. While it probably won't be playing Crysis anytime soon it's more than enough for the day to day coding and the backlit keyboard surprisingly comes in handy more often than you would think.

The operating system...

Ubuntu 11.04. I may be in the minority on this one but I actually like the changes brought in by the Natty Narwhal update. Where I used to use a launcher program to quickly find applications the Unity launcher now takes care of everything.

Coordinating...

Despite liking the Ubuntu 11.04 release I absolutely hate the default mail application, Evolution. To that end, I also hate the messaging application, Empathy. Here you have Opera which does triple duty: IM client, email and browser for testing. If you haven't tried it out, give it a shot.

Coding

I've got to give props to my buddy Joshua McNeese for recommending this one: PhpStorm. It strikes a good balance between the bloat found in most modern IDEs and the assistance it provides.

"But it costs money!" -- yes and no. JetBrains (the makers of PhpStorm) are kind enough to extend free licenses to individuals who have open source projects. I simply sent them an email with the address to one of my open source projects and was rewarded with a license.

Terminal

This one is easily one of my favorites. Tilda. It's a drop down terminal that hides when not in use. I can't tell you how many seconds this application has shaved off my workday. Need a terminal? Hit a hotkey, there you go. Want it to go away? Hit it again, gone. Always the first thing on any new laptop of mine.

Database management

I use SQLyog for working with my MySQL databases. I've liked the application so much that it's one of the few Windows applications I find myself installing Wine for on Ubuntu.

Repository management

While I'm usually dangerous competent enough to deal with my Git repositories in the terminal, sometimes it's nice to have a GUI to wade through commits and branches. Enter SmartGit. The application is free for personal use. It can optionally integrate with Github.

There you have it. All the (major) tools I use. Recommend something better? Use the same things? Let's hear it.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Tue, 13 Sep 2011 09:42:00 -0700 Custom Posterous subdomain with Gandi.net http://blog.joebeeson.com/custom-posterous-subdomain-with-gandinet http://blog.joebeeson.com/custom-posterous-subdomain-with-gandinet

The blog was down for a bit as I moved all of my domains from GoDaddy to Gandi.net. So far I couldn't be happier with the move save for one hiccup which was completely self-inflicted: editing DNS records with Gandi. Let me reiterate that last sentance one more time, I screwed up. You'd think someone who has experience with this stuff would be better at it, but nope. Problem was once I edited the DNS it would take hours to propagate then I would forget, check it  the next day, tweak the records, repeat. 

Here's how to get everything setup and running...

Setup the custom domain with Posterous

Create a new CNAME record at Gandi.net

Note the period at the end of posterous.com

That's it!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Thu, 18 Aug 2011 09:48:00 -0700 Gimmie all your lovin' http://blog.joebeeson.com/gimmie-all-your-lovin http://blog.joebeeson.com/gimmie-all-your-lovin

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 17 Aug 2011 09:48:00 -0700 Don't blink... http://blog.joebeeson.com/dont-blink http://blog.joebeeson.com/dont-blink

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Mon, 25 Jul 2011 06:35:00 -0700 Untitled http://blog.joebeeson.com/62438438 http://blog.joebeeson.com/62438438

Media_httpiimgurcom7p_cgnzg

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 06 Jul 2011 20:25:00 -0700 Untitled http://blog.joebeeson.com/59942827 http://blog.joebeeson.com/59942827

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 29 Jun 2011 16:00:00 -0700 Introducing the Filterable behavior http://blog.joebeeson.com/introducing-the-filterable-behavior http://blog.joebeeson.com/introducing-the-filterable-behavior

I'm sure you've been there before. Cleaning up data in every conceivable way that the user could muddle it up just so you can get the information saved or updated. Trimming whitespace, stripping HTML, parsing formatting languages, etc. The problem is that, while necessary, this sucks.

The idea is a simple one: do some stuff with the data just before we pass it off for validation. In the past people have done this in the controller action, perhaps in beforeValidate, hell you may even have some Javascript doing it for you client-side. Now, we've got a one-stop place for all the tweaking.

Anywho, enough chit-chat. Let's begin by showing off some of the fun tidbits it can do.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

class Post extends AppModel {

public $behaviors = array('Filterable');

public $filters = array(
'title' => array(
'trim',
'ucwords'
),
'body' => array(
'htmlentities'
)
);

}

Here we're using a handful of PHP's functions to cleanup some of our fields. Nothing too special here, but wait, there's more!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

class Post extends AppModel {

public $behaviors = array('Filterable');

public $filters = array(
'title' => array(
'_spaceToAsterisks'
),
'body' => array(
'_rickRolled'
)
);

/**
* Takes a string and makes it fabulous.
*
* @param string $string
* @return mixed
* @access protected
*/
protected function _spaceToAsterisks($string) {
return str_replace(' ', '*', $string);
}

/**
* Automated Rick Rolling. The best kind there is.
*
* @param string $string
* @return mixed
* @access protected
*/
protected function _rickRolled($string) {
return str_replace(
'Rick',
'<a href="http://www.youtube.com/watch?v=dQw4w9WgXcQ">Rick</a>',
$string
);
}

}

Yup, that's right. Using our own methods to screw tidy things up. But we're not done yet, we've got one more trick up our sleeve...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

class Post extends AppModel {

public $behaviors = array('Filterable');

public $filters = array(
'title' => array(
array(
'SomeCustomObject',
'staticMethod'
),
array(
'SomeCustomObject',
'anotherStaticMethod'
),
)
);

}

And we're done. Hopefully the examples make the usage fairly straightforward. If you want you can still perform your own cleanup wherever you'd like, this won't interfere at all.

It's worth noting that none of this is to be a replacement to proper validation, think of it more as an attempt to get your data ready prior to validation. Without further delay, the code.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Wed, 29 Jun 2011 16:00:00 -0700 Relieve your database with this quick tip http://blog.joebeeson.com/59050067 http://blog.joebeeson.com/59050067

Here's a quick little tidbit which while may be obvious to more seasoned coders, will really help out your database and make your model code more robust.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

/**
* AppModel
*
* Base application model.
*
* @author Joe Beeson <jbeeson@gmail.com>
*/
class AppModel extends Model {

/**
* Number of associations to recurse through
* during find calls.
*
* @var int
* @access public
* @see http://book.cakephp.org/view/1063/recursive
*/
public $recursive = -1;

/**
* Behaviors.
*
* @var array
* @access public
* @see http://book.cakephp.org/view/1071/Behaviors
*/
public $actsAs = array(
'Containable'
);

}

Your standard AppModel more or less but with two entries: recursive is being set to -1 and we're adding ContainableBehavior.

Setting the recursive value to -1 tells CakePHP that we don't want it to bring in any associations when we run a query. Off the cuff this sounds like a bad idea, especially if you've never used ContainableBehavior before because you'd have to do some extra leg work to get associated records. That's the point. You should be explicitly asking the model to bring in the data you need and nothing more.

The ContainableBehavior is in there to help facilitate getting the associated data when we need to, instead of running two queries or manually writing our own ad-hoc joins.

While this all seems like extra work, your code will be cleaner and better off because of it -- being verbose with your database is a good thing.

 

 

 

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Sat, 25 Jun 2011 09:30:00 -0700 Untitled http://blog.joebeeson.com/58435972 http://blog.joebeeson.com/58435972

Nxvon

- Cyanide & Happiness #2322

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Fri, 24 Jun 2011 15:00:00 -0700 Logging all database queries http://blog.joebeeson.com/logging-all-queries http://blog.joebeeson.com/logging-all-queries

Ever find yourself in a situation where CakePHP executes a handful of database queries but the controller immediately redirects you, effectively throwing away the log of everything that happened during that request? What about trying to see "behind the scenes" of an AJAX request? Well, here's a little tidbit to help sneak a peak under the hood..

First off, yes, I am aware of things like FirePHP and how to use it within CakePHP and... well... I don't like Firefox. There. I said it. I'm quite at home with my Chrome browser, thank you very much. Now, hopefully before a full-on browser war starts, here's a solution to log all queries to the database into something a bit more universal, a log file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

/**
* Called after the controller action is run, but before the view is rendered.
*
* @return void
* @access public
*/
public function beforeRender() {
if (Configure::read()) {
foreach (ConnectionManager::sourceList() as $datasource) {
$this->_logDataSourceQueries(
ConnectionManager::getDataSource($datasource)
);
}
}
parent::beforeRender();
}

/**
* Logs the queries from a `DataSource` object. The optional `$log`
* parameter denotes where to send the logs.
*
* @param DataSource $datasource
* @param string $log
* @return void
* @access protected
*/
protected function _logDataSourceQueries(DataSource $datasource, $log = 'sql') {
if ($datasource->isInterfaceSupported('getLog')) {
$queries = $datasource->getLog(false, false);
foreach ($queries['log'] as $query) {
CakeLog::write($log, sprintf(
'%3s %3s %3s %s',
$query['affected'],
$query['numRows'],
$query['took'],
$query['query']
));
if (!empty($query['error'])) {
CakeLog::write('sql', $query['error']);
}
}
}
}

You'll notice that inside the beforeRender we're checking to make sure we're in debug mode. By default none of the DataSource objects will log any queries if we're not debugging. From there it loops over all available DataSource objects and kicks them out to our _logDataSourceQueries method.

From there it's a pretty simple hop, skip and a jump to logging with CakeLog. We make sure the DataSource supports logging before grabbing out all of the queries and sprintfing them out to our log.

If you're feeling fancy you can mix-up the display of the queries, here's some ideas I'm too lazy to implement.

  • Adding warnings when a query exceeds a time threshold.
  • Organizing the queries by model.
  • Creating a summary of all queries. 
  • Logging the time took by controller, action for observation.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Thu, 23 Jun 2011 18:21:00 -0700 Forcing a maximum pagination limit http://blog.joebeeson.com/forcing-a-maximum-pagination-limit http://blog.joebeeson.com/forcing-a-maximum-pagination-limit

You know what's a really fun and easy way to bog down someone's server? Make a request against a controller for hundreds of thousands of records. Here's how to keep that from happening in your applications.

Your application has hundreds, thousands, however many, records sitting in a model and all paginated away in the controller. Awesome. Now go back to your application and add limit:9999999 into the URL, what happens? At best your server begrudgingly and slowly returns the gigantic resultset and at worst has shat the bed. Do this repeatedly and you have yourself a fairly easy denial of service attack.

Despite all the chaos such a thing can cause, it's actually really easy to fix, like, six line easy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

/**
* AppController
*
* Base application controller.
*
* @author Joe Beeson <jbeeson@gmail.com>
*/
class AppController extends Controller {

/**
* Pagination results limit.
*
* @var int
* @access protected
* @see AppController::beforeFilter
*/
protected $_maximumPaginationResults = 25;

/**
* Triggered prior to the action.
*
* @return void
* @access public
*/
public function beforeFilter() {
if (isset($this->passedArgs['limit'])) {
$this->passedArgs['limit'] = min(
$this->_maximumPaginationResults,
$this->passedArgs['limit']
);
}
}

}

By always asking for the lowest of the two values (one from the request, the other hardwired in the controller) we make sure that our pagination never goes off and commits database seppuku and, if you ever feel so inclined, you can just change the _maxPagination value in specific controllers to get different limits.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Thu, 23 Jun 2011 15:00:00 -0700 Recursive model associations http://blog.joebeeson.com/recursive-model-associations http://blog.joebeeson.com/recursive-model-associations

While usually pretty rare there are times when you want to define an association for a model that relates back to itself; parent record, children records, etc. Here's an example of how to get this up and running in your own model.

Here's a model called Person which holds records for, you guessed it, people. The schema consists of id, father_id, mother_id, first_name and last_name. Our goal here is to configure three extra associations for the model, PersonMother, PersonFather and PersonChildren so that CakePHP will automatically handle these associations for us.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php

/**
* Person
*
* Application model for `Person` records.
*
* Schema: id, father_id, mother_id, first_name, last_name
*
* @author Joe Beeson <jbeeson@gmail.com>
*/
class Person extends AppModel {

/**
* "Belongs to" associations.
*
* @var array
* @access public
*/
public $belongsTo = array(
'PersonMother' => array(
'className' => 'Person',
'foreignKey' => 'mother_id'
),
'PersonFather' => array(
'className' => 'Person',
'foreignKey' => 'father_id'
)
);

/**
* "Has many" associations.
*
* @var array
* @access public
*/
public $hasMany = array(
'PersonChildren' => array(
'className' => 'Person',

/**
* Since we have [two] fields that could possibly associate us
* to our `PersonChild` records we have to override the query
* to make sure we're searching both.
*/
'finderQuery' => 'SELECT `PersonChildren`.* FROM `people` AS `PersonChildren` WHERE `PersonChildren`.`father_id` IN ({$__cakeID__$}) OR `PersonChildren`.`mother_id` IN ({$__cakeID__$})',

// Note we also need to say false to "foreignKey"
'foreignKey' => false
)
);

}

Our PersonFather and PersonMother records are a belongs to association, not a has one and the reason being that we want to be able to see the records from the other "direction" -- we get our parent records when we're queried, not visa-versa.

The secret sauce here is actually the className declaration in the association. This tells CakePHP that despite the association name, which usually tells it which model to load, that we want to use the Person model. This means that all of our other configurations, table, functions, behaviors, etc, will not need to be repeated since we are the model that will be associated.

The PersonChildren record is a bit trickier, reason being because there are two columns that may associate a parent with their children, father_id and mother_id. Luckily CakePHP gives us a configuration option, finderQuery, to help tell it how to get its associated records. If we didn't have such a unique situation we could get away without manually defining our own query.

One of the great things about defining all of your associations instead of relying upon your own custom code for finding records is that you can utilize tools like Containable or Linkable for selecting only the associations you need.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson
Thu, 23 Jun 2011 07:21:00 -0700 Heavy is spy! http://blog.joebeeson.com/heavy-is-spy http://blog.joebeeson.com/heavy-is-spy

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1305637/42a9ad8d2315a7c6384163af80299a65.jpeg http://posterous.com/users/36p0W9fBRbsB Joe Beeson joebeeson Joe Beeson