Welcome Guest, Not a member yet? Register   Sign In
Use of Closures in Views
#11

(This post was last modified: 09-25-2015, 07:04 AM by jLinux. Edit Reason: Added results to example )

(09-09-2015, 09:25 AM)mwhitney Wrote: This is really more of an observation than a question, but I guess I was wondering if anyone else has found a use for this (or even discovered it in the first place), as it is apparently a side-effect of the way the loader passes data to views (plus the addition of closures in PHP 5.3+). Essentially, if I assign a closure to a variable which is then passed to my view, I can use the closure in the view, allowing me to use what might otherwise be more complicated functionality in my view or prevent the need to go through my data multiple times to prepare it for display.

Because I primarily use Bonfire's Template library rather than loading my views directly, the example below may have an issue or two (other than the simplification for the sake of an example), but the basic idea should work.

In the controller, I setup my data, then define a closure to handle some more complicated logic which I want available in my view, but don't necessarily want to reside directly in my view, then pass the data (with the closure) and name of the view to the loader:





PHP Code:
// $list = $query->result() returned from model
$data = ['list' => $list'listCount' => count($list)];

// Define a closure to build an img element based on the record's image_name field.
$data['imageHandler'] = function ($imageName$altText) {
    $imageDir '/path/to/public/image/dir/';
    if (empty($imageName)) {
        return "<img src='{$imageDir}placeholder.jpg' alt='image not available for {$altText}' />";
    }
    return "<img src='{$imageDir}{$imageName}' alt='{$altText}' />";
};

$this->load->view('list'$data); 


Then the relevant portion of the view looks something like this:





PHP Code:
<div class='examples'>
    <?php foreach ($list as $record) : ?>
    <div class='record'>
        <?php 
        
// Use the closure to generate the img element, echo the result.
        echo $imageHandler($record->image_name$record->alt_text); 
        echo $record->content
        ?>
    </div>
    <?php endforeach; ?>
</div> 

In theory I could go a step further and define the closure itself in another class or some other location to prevent placing code in the controller which generates HTML for the view, but this is, more or less, something I stumbled across while trying to cleanup one of the more complicated modules I developed fairly early on in my time with Bonfire/CodeIgniter/PHP.


Question though, are you sure this is a closure? I think its actually an anonymous function.


I could be wrong though, thats why im asking, you seem a lot more experienced than I am with PHP.

From what I understand, closures will encapsulate all the variables in the environment around them, while anonymous functions need to be passed variables to have access to them.

Heres my example of the difference..
PHP Code:
$foo 'test';

// Anonymous Function
$anon = function($var)
{
 
   echo "Anonymous Function: \$var is {$var}\n";
};

$anon($foo);
// Result: Anonymous Function: $var is test
 
   
// Closure  
$closure = function() use($foo){
 
   echo "Closure: \$foo is $foo\n";
};

$closure();
// Result: Closure: $foo is test 


But yes, PHP does now support both types of anonymous functions, I was just referring to your example

You can also see the difference in my example
Reply
#12

(09-25-2015, 06:46 AM)jLinux Wrote: Question though, are you sure this is a closure? I think its actually an anonymous function.

I could be wrong though, thats why im asking, you seem a lot more experienced than I am with PHP.

From what I understand, closures will encapsulate all the variables in the environment around them, while anonymous functions need to be passed variables to have access to them.

I honestly always thought they were the same thing with Closure being the specific class name, while anonymous function was just a "layman's term". A little research shows there is a difference in other languages like Javascript but, in PHP they are the same thing. Whenever you create an anonymous function it creates an instance of the class Closure.

Source: PHP Manual on Closures
Reply
#13

(09-25-2015, 07:04 AM)kilishan Wrote:
(09-25-2015, 06:46 AM)jLinux Wrote: Question though, are you sure this is a closure? I think its actually an anonymous function.

I could be wrong though, thats why im asking, you seem a lot more experienced than I am with PHP.

From what I understand, closures will encapsulate all the variables in the environment around them, while anonymous functions need to be passed variables to have access to them.

I honestly always thought they were the same thing with Closure being the specific class name, while anonymous function was just a "layman's term". A little research shows there is a difference in other languages like Javascript but, in PHP they are the same thing. Whenever you create an anonymous function it creates an instance of the class Closure.

Source: PHP Manual on Closures

Ive always thought a closure is a type of anonymous function

Quote:Anonymous Function - An Anonymous Function is a function that is defined, and occasionally invoked, without being bound to an identifier. It also has the variable scoping characteristics of a Closure (see below).

Closure - A Closure is a function that captures the current containing scope, then provides access to that scope when the Closure is invoked. The Closure binds non-local variable references to the corresponding variables in scope at the time the Closure is created.

Sourcehttp://dhorrigan.com/post/29209695084/an...s-closures

From what I'm reading, a closure captures the current scope, and the examples in the first post don't, thats what threw me off.

Granted, the "closures" I'm my example don't capture the entire scope, they pick the ones specified in the use() variables
Reply
#14

Also, since I'm guessing a lot of people here are new to Anonymous Functions and Closures, the article sourced in my above post is very useful, and it details 3 things that one should know before implementing them.

Quote:QUIRK #1

You MUST send the function all of the variables you want bound to the scope of the Closure using the use keyword. This is different from most other languages, where this is done automatically (like in my JS example above).
Code:
$foo = 'foo';
$bar = 'bar';

$baz = function () use ($foo, $bar) {
   echo $foo, $bar;
};

QUIRK #2
The bound variables are copies, of the variable, not references. If you want to be able to change the variable inside the Closure, you MUST send it by reference:
Code:
$foo = 'foo';
$bar = 'bar';

$baz = function () use (&$foo, &$bar) {
   $foo = 'Hello ';
   $bar = 'World!';
};
$baz();
echo $foo, $bar; // Outputs "Hello World!";

QUIRK #3
In PHP 5.3 if you are using a Closure inside of a class, the Closure will not have access to $this. You must send a reference to $this, however, you cannot send $this directly:

Code:
<?php
class Test
{
   protected $foo = 'foo';

   public function baz() {
       // Get a reference to $this
       $self = &$this;
       $func = function () use ($self) {
           echo $self->foo;
       }
   }
}

Note: Because of the way object references are handled in PHP, you do not have to send a reference to $self if you wish to modify it.
Reply
#15

Thanks for sharing that article. He does a very good job of explaining the differences. I knew most of that, but the 3rd quirk I had never even thought about since I have never tried to do that. Good to know.
Reply
#16

(09-25-2015, 07:32 AM)kilishan Wrote: Thanks for sharing that article. He does a very good job of explaining the differences. I knew most of that, but the 3rd quirk I had never even thought about since I have never tried to do that. Good to know.

I didn't know #2 or #3. For number 3, I don't use $this much inside of CI models/libraries, I set a local static::$CI property, so in my closure, self::$CI or static::$CI work just fine. but I'm sure I would have ran into it sooner or later
Reply
#17

Eventually, I hope to get around to replying to everything directed towards me since my last post. However, I wanted to point out that quirk #3 is of course isolated to 5.3, and does not exist in 5.4+. I also happen to know that @kilishan has used $this in an anonymous function at some point, because it had to be rewritten to support PHP 5.3 Smile

From the link about anonymous functions vs. closures, though:
Quote:From this we can derive: All Anonymous Functions are Closures, but not all Closures are Anonymous Functions.

Also:
Quote:Anonymous Functions are implemented as Closure objects. PHP takes the idea that “Anonymous Functions are Closures without a name” to heart, because that is EXACTLY what they are.

Finally:
Quote:How should you know when to call it a Closure or Anonymous Function? Simple: You can always call it a Closure (because all Anonymous Functions are Closures), and if it doesn’t have a name, it is Anonymous. Pretty simple, right?

However, I don't think this is necessarily correct, it's just an artifact of PHP's implementation. The fact that PHP implements an anonymous function as a Closure object doesn't really make it a closure. This just tells you that PHP allows you to create Closures which are anonymous functions, and Closures which are not anonymous functions, except that I'm not sure whether you could actually create a class which extends Closure to create a "named" Closure.

In the long run, I think you'll find that PHP's implementation of Closure is not quite what most people expect from a closure when coming from another language. As my later example demonstrates, the Closure or anonymous function is not isolated from the executing scope, even if I were to pass variables from the defining scope with the use() construct. Of course, the fact that I used a class from the executing scope within my Closure also means I posted an example which I would consider bad code.

I'd also like to point out that, although it's helpful in some circumstances, comparing JavaScript to PHP will eventually give you a headache. JavaScript's entire idea of what a function, object, or variable is happens to be intertwined in a way that makes comparison to languages with a more traditional use of these concepts difficult, at best. This is also why there are a whole slew of meta-languages which compile to JavaScript but enforce the more traditional concepts.
Reply
#18

(09-24-2015, 03:42 PM)spjonez Wrote: Separating logic from views is not always convenient but it does provide value long term. Complex SQL queries are faster as a single recordset which often requires PHP loops to process a flat array and turn it into a nested array. You can do formatting at the same time so you aren't adding a lot of overhead by pre-processing.

At this point, you're looking at a whole series of trade-offs between performance and complexity in several different portions of the application. However, the only thing that really has anything to do with the view is that you're advocating formatting data while performing other processing, but still before passing it to the view. In my opinion, most of the processing you're talking about should be performed in the model (or the SQL, because sometimes it is faster there), but the only way the model can format the data for the view is if you provide the model with a lot of information about the way in which the data will ultimately be presented.

(09-24-2015, 03:42 PM)spjonez Wrote: IMO views are presentation only and should compile from raw JSON. Using views in this nature allows you to swap to virtually any templating language with minimal effort. You have better separation of logic in your application (traditional MVC). You can create two way data binds by passing JSON back and forth instead of compiled HTML. You can do client side templating to reduce bandwidth. Some of the newer frameworks are moving in this direction with web components.

Which is it, the views are presentation or compilation should be involved? Even if you create a single-page application and require your user to have JavaScript enabled for your site to even function, you have to send HTML in response to the first request. Further, it's unclear here whether you are encoding to JSON directly from a controller or you're actually using a view for this purpose. Is it just the presence of PHP in a view that offends you? After all, it sure sounds like you're perfectly OK with off-loading a lot of logic to the client.

(09-24-2015, 03:42 PM)spjonez Wrote:
(09-24-2015, 07:29 AM)mwhitney Wrote: While this is an ideal which one should look to attain in their views, at some point someone has to look around and realize that some views must do one or the other, or you have to back-door the situation by using a template language which allows you to specify logic (and sometimes function calls) without technically including it directly in the view.

It requires more work and planning up front but I feel the benefits are worth it.

The amount of work and planning has nothing to do with this particular choice. It takes a little more work to write my own JavaScript framework or my own templating language, if I feel I need them, but I would usually only choose to use one (or both) because it made it easier, in the long term, to do what I need my site to do. In other words, I would usually choose to do something like that because it reduced the amount of work I needed to do. The amount of planning required should not change at all, but it is easier, in the short term, to just start writing code without planning much of anything, and none of us are likely to make informed architectural decisions about our sites that way.

My main point, though, was that you're not removing logic from your views, you're simply moving it to another portion of the View layer. If PHP parses the template, it's possible that the parser itself is relatively well isolated from the view layer, but the use of the parser to convert the template is still a use of logic within the view layer. If JavaScript parses the template, somewhere along the line the parser itself is in your view.

(09-24-2015, 03:42 PM)spjonez Wrote:
(09-24-2015, 07:29 AM)mwhitney Wrote: How exactly does one accomplish even a simple list without logic or function calls in their view? At some point you're going to need at least a loop.

You will yes. Do you use an ORM? I don't so more often than not I'm looping my query results anyway.

No, I usually don't use an ORM, but an ORM shouldn't have any impact on whether you have to loop through your results in a view, because the ORM is accessed by the Model.

You jumped right on that idea of separation of the logic from the View, but you don't seem to care about the separation of any of the other layers, and I see no acknowledgement of the amount of logic required in the View layer for the alternatives you propose.
Reply
#19

(09-25-2015, 06:46 AM)jLinux Wrote: Question though, are you sure this is a closure? I think its actually an anonymous function.

I could be wrong though, thats why im asking, you seem a lot more experienced than I am with PHP.

From what I understand, closures will encapsulate all the variables in the environment around them, while anonymous functions need to be passed variables to have access to them.

In case I wasn't clear in my previous posts, I didn't differentiate between Closures and anonymous functions in my initial post at least in part from laziness. I don't believe PHP fully supports closures, really has two different types of Closures, nor that Closures are different from anonymous functions in PHP.

What PHP has is support for anonymous functions which are represented by a class called Closure, with a language construct (use) which allows the defining environment to copy data into the function's internal scope. Since the anonymous function has access to the calling environment regardless of the arguments passed to the function and can only modify the parent scope if variables passed to the use construct also specify that they are passed by reference, PHP has somehow managed to create something which is almost the opposite of a closure, so, of course, they named it Closure.

This shouldn't be completely unexpected in a language which considers "0" (a string containing the character representing the number zero) to be empty and actually gives || a different precedence from OR (which is one of my favorite issues with CodeIgniter's style guidelines).
Reply
#20

(This post was last modified: 09-25-2015, 11:28 AM by spjonez.)

(09-25-2015, 10:03 AM)mwhitney Wrote: At this point, you're looking at a whole series of trade-offs between performance and complexity in several different portions of the application. However, the only thing that really has anything to do with the view is that you're advocating formatting data while performing other processing, but still before passing it to the view. In my opinion, most of the processing you're talking about should be performed in the model (or the SQL, because sometimes it is faster there), but the only way the model can format the data for the view is if you provide the model with a lot of information about the way in which the data will ultimately be presented.

Yes I perform queries in models, typically as a single query then I loop and format the results in PHP. If for whatever reason you have a datetime and want two distinct formats you can always add keys as you loop.

(09-25-2015, 10:03 AM)mwhitney Wrote: Which is it, the views are presentation or compilation should be involved

Presentation is a representation of the data. I should be able to use the same data array and present it as HTML, CSV, or any other format without each compile requiring special functions.

(09-25-2015, 10:03 AM)mwhitney Wrote: Even if you create a single-page application and require your user to have JavaScript enabled for your site to even function, you have to send HTML in response to the first request.

You can send an uncompiled view and the data as JSON to the client, then only send the data on subsequent requests and cache the view in the DOM or localstorage. This abstraction is required for things like two way data binding. If you don't you'll be sending HTML blobs and delegating events on parent containers which is not performant or maintainable long term.

(09-25-2015, 10:03 AM)mwhitney Wrote: Further, it's unclear here whether you are encoding to JSON directly from a controller or you're actually using a view for this purpose.

I'm returning JSON from the server. Views are included as a key.

(09-25-2015, 10:03 AM)mwhitney Wrote: Is it just the presence of PHP in a view that offends you? After all, it sure sounds like you're perfectly OK with off-loading a lot of logic to the client.

PHP in views drives me crazy. Yes it's fast, yes it's easy, but it's a poor way to manage a code base. Why create a dependency when you don't need to? Why should my server have to hit the disk every time it wants to generate the same view over and over?

(09-25-2015, 10:03 AM)mwhitney Wrote: The amount of work and planning has nothing to do with this particular choice. It takes a little more work to write my own JavaScript framework or my own templating language, if I feel I need them, but I would usually only choose to use one (or both) because it made it easier, in the long term, to do what I need my site to do. In other words, I would usually choose to do something like that because it reduced the amount of work I needed to do. The amount of planning required should not change at all, but it is easier, in the short term, to just start writing code without planning much of anything, and none of us are likely to make informed architectural decisions about our sites that way.

That depends on your project needs. Long term flexibility is something we should all think about when we build applications at scale.

(09-25-2015, 10:03 AM)mwhitney Wrote: My main point, though, was that you're not removing logic from your views, you're simply moving it to another portion of the View layer. If PHP parses the template, it's possible that the parser itself is relatively well isolated from the view layer, but the use of the parser to convert the template is still a use of logic within the view layer. If JavaScript parses the template, somewhere along the line the parser itself is in your view.

I tend to think of MVC layers as independent systems. Controller takes a request and validates it. If data is required from your database it may request data from a model. The result is handed to a view which compiles it. By using a templating language your view is a standalone system that's simply injecting strings into a fragment. Want to use Mustache? Handlebars? doT? What if you want to template client side? The transition is easy.

(09-24-2015, 07:29 AM)mwhitney Wrote: No, I usually don't use an ORM, but an ORM shouldn't have any impact on whether you have to loop through your results in a view, because the ORM is accessed by the Model.

The reason I asked is with an ORM you often receive data back as a nested array/object. If your data is already well formed you can call functions from views (formatting etc) and skip the PHP loops which the ORM is doing for you. Under those circumstances users may forgo pre-processing to avoid a perceived hit to performance.

(09-24-2015, 07:29 AM)mwhitney Wrote: You jumped right on that idea of separation of the logic from the View, but you don't seem to care about the separation of any of the other layers, and I see no acknowledgement of the amount of logic required in the View layer for the alternatives you propose.

View parsing can be automated. Ember does it, Angular does it, Cinder (my project in the Addins forum) does it.

What I'm advocating for is treating your application like a series of micro services each with a distinct job and minimal awareness of the layers surrounding it. And to avoid vendor lock in where you can. One of the biggest mistakes I made starting out with CI was using PHP in views instead of the template parser class.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB