An intriguing use of lambda functions

I’ve been working hard on Goodsie.com lately trying to bring it to launch. It’s been great being in on a new PHP project from (near) the beginning, as it frees up a number of things.

One of those, is the fact that I can be using PHP 5.3 and all the new features that come with PHP 5.3. While I’ve used my fair share of the short-cut ternary already (?:), the bigger win for me, are the Lambda functions with scoping (anonymous functions).

I found a very specific use out of the blue of Lambda functions that I have now used and I see as a great use-case. Which is specifically passing functions/logic from your Controller to your View.

In the case of Goodsie, I’m using PHP for my templating language and as usual I’m trying to remove as much logic from my View as possible, while still allowing the view to be malleable.

The specific case I had, was a subview that was generating some pagination code for me. You know, the standard ‘previous, page 1, page 1, next’ section of links. The basic HTML template I had, looked looked similar to:

<div class="pagination">
    <a href="<?= $baseurl . '/page:' . ($page - 1) ?>">&larr; Previous</a>
    Page <?= $page ?> of <?= $total ?>
    <a href="<?= $baseurl . '/page:' . ($page + 1) ?>">Next &rarr;</a>
</div>

Rather straight forward, but I quickly ran into a problem. The way it worked, as you see, is that you passed in a base URL, and the page number you are currently on, and it generated appropriate forward/back links. (Ok, there was also some other logic where it determined if you needed the prev/next links at all, but I’ve removed that for clarity)

But I then had a case, where I wanted to reuse this subview in an ajax situation. Where instead of straight URL’s being passed in, I might want to pass in a javascript function, and have that function be called with the page number as a parameter. That would be nice as I could use it in both situations. What pagination looked like, could completely change, and still work on both cases. Perhaps we’d want to give a full list of all possible pages. Or show a couple forward/back, etc. The view could handle all of that without a change to the controller.

But therein lied the problem. When using a URL based pagination, I wanted to concat the page number onto the end of the URL. But when using javascript, it wasn’t pure concatenation, it instead needed to wrap the page number with the function call. Oh the pain a simple ) could cause me.

I started writing code, where I ended up with tons of switch statements and logic inside of the view. I’d have to pass in two different possible values, a URL or a javascript function. The view at every point where it would output a link, would need to see which version was being used, and from that decide what type of output to create. In short, it was a mess.

But then the solution dawned upon me. A lambda function would work admirably here. So what I did, is inside of my controller I created a function on the fly, that would generate the appropriate type of link that I was wanting. It looks something like:

if ($jsfunc) {
    $url = function ($p) use ($jsfunc) { return "javascript:{$jsfunc}({$p})"; };
} elseif ($baseurl) {
    $url = function ($p) use ($baseurl) { return "{$baseurl}/page:{$p}"; };
}

Now I could simply rewrite my original template, to use this lambda function $url to generate it’s URLs.

<div class="pagination">
    <a href="<?= $url($page - 1) ?>">&larr; Previous</a>
    Page <?= $page ?> of <?= $total ?>
    <a href="<?= $url($page - 1) ?>">Next &rarr;</a>
</div>

Now not only would this work for my specific situation, but ANY controller could reuse this pagination subview and define exactly how it wanted it’s URLs to be formed. Now, the view could completely change around how the pagination section is displayed, show as many, or as few pages as it wants to, and all that without ever touching the controller.

This is one simple example, but I’ve become enamored of this approach. Using lambda functions in this way, you are able to have complicated logic represented inside of your view, but encapsulated/created by the controller. Also of note is the fact that the view is managing to use the $jsfunc and $baseurl values, but without actually having to be granted access to them. This allows for another level of encapsulation, as I exposed one function, instead of 2 separate variables. In the future if other data points start being needed to determine what a URL should be, the view never needs know that, as the controller will continue to update the function on it’s behalf.

8 Responses to An intriguing use of lambda functions

  1. itpastorn says:

    I do think that you’ve used the lambda function in PHP to minimize your template clutter in a smart way. (And I am also glad that you are using the word lambda, since in the PHP community the word “closure” is a bit overused.

    One thing that would like to see improved, however, is that you’d drop the inline JavaScript altogether and used progressive enhancement instead.

    A script block like this one should do the trick (using JQuery for brevity):

    $(“#pagination a”).click(function() {
    jsfunc(this.href);
    return false;
    });

    • Eli says:

      Thanks for the comments itpastorn. I agree that doing progressive enhancement can be a good tactic here, and I’ve used it before many times.

      I always find it ‘another trick in the toolkit’ though, as there are times when it’s absolutely the way you want to execute a concept. But at other times, I get frustrated by it because of the ‘magic’ nature of it. You get frustrated because of the disconnected nature of an HTML template sitting in one directory, and the ‘magic’ code happening in a completely different .js file stored elsewhere, with no direct link between the two concepts.

      In any case, I’m a fan of using the ‘right tool for the job’. Sometimes to me that’s progressive enhancement, sometimes it’s a direct on* handler. And in a couple rare cases, it’s a javascript: url.

      In this particular case, the nature of the structure of current codebase I’m working within, this, IMO, was the cleaner option for now.

  2. […] at foxhole PHP, expostulate gilding at the original (another 1389 bytes) Related Posts:php|architect: It’s not all about the codeSony AK […]

  3. […] a new post to his blog today Eli White takes a look at an interesting use of lambda functions he’s figured out for a his development at work. I found a very specific use out of the blue […]

  4. […] Eli White offers a very smart use of lambda functions in PHP. Now not only would this work for my specific situation, but ANY controller could reuse this pagination subview and define exactly how it wanted it’s URLs to be formed. Now, the view could completely change around how the pagination section is displayed, show as many, or as few pages as it wants to, and all that without ever touching the controller. […]

  5. […] PHP 5.3, functional programming and an inspirational post from Eli White.  We can use the factory method pattern and closures to meet all the requirements and isolate […]

  6. John Kleijn says:

    Notes about closures in PHP…

    When PHP 5.3 came out, I was ecstatic. Namespaces, finally! Actually some parts of the implementation were a bit disappointing, but we’ll leave that for another time. In that same enthousiasm, I jumped on closures like a hungry dog on a steak. Only to…

Leave a comment