Zend\View

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Zend\View

gargoyle-3
Sorry for creating another thread, but after chatting with Evan this morning and having a play round, I think this is a bit more than just finding view scripts.

Anyway, after speaking with Evan, we decided it would be nice if two boxes could be ticked:-
        * Being able to override individual view files from modules.
        * Being able to use different renderers by checking the file extension.

So, I've been having a little play around and come up with the following idea.

Instead of the view being an instance of Zend\View\Renderer, I created a "top level" Zend\View class (which for the sake of getting it working quick, also implements the renderer interface). This top level class is created in index.php and listens for the loadModule event. If the module in question implements the Zend\Module\Consumer\ViewProvider interface, then the view calls getViewConfig() to get view information; It uses this information to keep an internal mapping of file extensions to renderers and namespaces / controller classes to view script paths.

As each config is loaded, the view paths are scanned, and an internal list of found files is maintained for each map entry. When a call is made to render a script, the map is checked in reverse order (so modules loaded later can override earlier ones) looking for matching files.

I have a basic working example on my blog which you can see via github:-
        * my copy of zf2 - https://github.com/gargoyle/zf2/tree/ViewUpdates 
        * and the code for my blog:- https://github.com/gargoyle/ga.rgoyle.com 

(Warning: It's more than a little but rough round the edges and still needs much work!)


At the moment I am assigning it to a global var (I told you it was rough!) and fetching the same instance in the View\Listener. What are your thoughts on the best way to handle getting the instance into the View\Listener? My first though would be a singleton, is there any reason for an app to have more than one view? Or maybe it becomes initialised as part of Zend\Module\Listener\DefaultListenerAggregate?


Apologies if this does not make much sense, it's getting late!  Your thoughts and ideas will be much appreciated.

Paul
(Gargoyle)
--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Zend\View

Jurian Sluiman
CONTENTS DELETED
The author has deleted this message.
Reply | Threaded
Open this post in threaded view
|

Re: Zend\View

EvanDotPro
In reply to this post by gargoyle-3
Hey Paul / all,

I wanted to real quickly post the diff links to Paul's work on Github
to help people who just want to take a quick glance:

In the ZF2 library,
https://github.com/gargoyle/zf2/compare/c9bf15469b77...ViewUpdates

And in his blog,
https://github.com/gargoyle/ga.rgoyle.com/commit/dd2a5ddf59d9fd952b42f824d843dfb997c014db

--
Evan Coury

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Zend\View

EvanDotPro
In reply to this post by Jurian Sluiman
Hi Paul, Jurian,

First of all, Thank you Paul for taking the time to code out a proof
of concept of your ideas around the view stuff!

On Sun, Dec 11, 2011 at 6:00 AM, Jurian Sluiman <[hidden email]> wrote:

> On Sunday 11 December 2011 03:02:13 Paul Court wrote:
>> Sorry for creating another thread, but after chatting with Evan this
>> morning and having a play round, I think this is a bit more than just
>> finding view scripts.
>>
>> Anyway, after speaking with Evan, we decided it would be nice if two boxes
>> could be ticked:- * Being able to override individual view files from
>> modules.
>>       * Being able to use different renderers by checking the file extension.
>>
>> So, I've been having a little play around and come up with the following
>> idea.
>>
>> Instead of the view being an instance of Zend\View\Renderer, I created a
>> "top level" Zend\View class (which for the sake of getting it working
>> quick, also implements the renderer interface). This top level class is
>> created in index.php and listens for the loadModule event. If the module
>> in question implements the Zend\Module\Consumer\ViewProvider interface,
>> then the view calls getViewConfig() to get view information; It uses this
>> information to keep an internal mapping of file extensions to renderers
>> and namespaces / controller classes to view script paths.
>>
>> As each config is loaded, the view paths are scanned, and an internal list
>> of found files is maintained for each map entry. When a call is made to
>> render a script, the map is checked in reverse order (so modules loaded
>> later can override earlier ones) looking for matching files.
>>
>> I have a basic working example on my blog which you can see via github:-
>>       * my copy of zf2 - https://github.com/gargoyle/zf2/tree/ViewUpdates
>>       * and the code for my blog:- https://github.com/gargoyle/ga.rgoyle.com
>>
>> (Warning: It's more than a little but rough round the edges and still needs
>> much work!)
>>
>>
>> At the moment I am assigning it to a global var (I told you it was rough!)
>> and fetching the same instance in the View\Listener. What are your
>> thoughts on the best way to handle getting the instance into the
>> View\Listener? My first though would be a singleton, is there any reason
>> for an app to have more than one view? Or maybe it becomes initialised as
>> part of Zend\Module\Listener\DefaultListenerAggregate?
>>
>>
>> Apologies if this does not make much sense, it's getting late!  Your
>> thoughts and ideas will be much appreciated.
>>
>> Paul
>> (Gargoyle)
>
> I think you could have written rough in capitals to make a better statement ;)
>
> However, I honestly like your idea and played with something similar without
> the rendering "engines" but with a little more sophistication around the
> TemplateResolver.

I agree that the view script mapping idea is a great one, and
worth-while strategy to pursue.

> I liked the idea to inject the view scripts within the DI config and question
> why you want to provide that with an additional Zend\Module\Consumer? If
> Zend\View\PhpRenderer and Zend\View\TemplatePathStack are modified such it
> allows namespaced scripts, you should be able to do that with DI as well.

I think there's a balance to be had here. Personally, I think we
should shy away from so heavily relying on DI. I'd much prefer if we
first implement a proof of concept around views without any reliance
on Zend\Di, then if it's appropriate to wire up the implementation
with DI later, then users can do so. I think implementing something
with a depedency on Zend\Di from the start is a bad approach (I have
this same gripe with the current method of resolving controllers).

Now regarding it being a loadModule event listener, I really don't see
anything wrong there. However I think it needs to be decoupled from
that. For example, the resolver could look something like this:

<?php

namespace Zend\View;

class TemplateMapStack implements TemplateResolver
{
    /**
     * Can optionally pass scriptMaps to constructor
     *
     * @param array|Traversable $scriptMaps
     * @return void
     */
    public function __construct($scriptMaps = null);

    /**
     * An array of script map arrays
     *
     * @param array|Traversable $scriptMaps
     * @return TemplateMapStack
     */
    public function setScriptMaps($scriptMaps);

    /**
     * addScriptMap
     *
     * @param array|Traversable $scriptMap
     * @return TemplateMapStack
     */
    public function addScriptMap($scriptMap);

    /**
     * addMappedResolver
     *
     * @param MappedResolver $mappedResolver
     * @return TemplateMapStack
     */
    public function addMappedResolver(MappedResolver $mappedResolver);

    /**
     * Retrieve the filesystem path to a view script
     *
     * @param  string $name
     * @return string
     */
    public function getScriptPath($name);
}

A separate loadModule listener could still be used to wire this up
from the individual Module classes, but the interface would also be
DI-friendly and lend itself well to manual configuration and easy unit
testing.

Additionally, I could see renderers being handled via some sort of
MultiRenderer class that implements Zend\View\Renderer, but possibly
uses the Zend\Loader\PluginBroker or (preferably) Zend\EventManager
system to allow the proper registered renderer to render each view
script. I believe this could potentially even eliminate/replace what
we currently know as the "view listener". The ultimate goal would be
to allow an application using Twig to still be able to install and use
a module that uses phtml. It would be a shame to end up with a ton of
forks of the same exact module but ported for different template
systems.

> Another point is the way scripts are coupled to controllers. I'd like to have
> this based on namespaces as well:
>
> 'Blog' => __DIR__ . '/views/scripts/blog',
> 'BlogAdmin => __DIR__ . '/views/scripts/blog-admin',
> 'Blog\Controller\SpecialController' => __DIR__ . '/views/scripts/special'
>
> You need to resolve the map in reverse order, having the most specific mappings
> at the bottom of the stack. This way:
>
> * you don't need to specify the same directory over and over again
> * allow groupings of controllers (eg subnamespaces, multiple namespaces within
> a module)
> * still have the option to override script paths per controller

I think that Paul's approach makes how the initial mapping works
largely inconsequential. You could do it either way without much
changing the outcome due to the fact that it would ultimately be
scanning and making a detailed map of all the individual view scripts
that are available for each "stack". How we organize or group the
"stacks" is just an implementation detail. Personally I'm fine with
either namespace or controller as long as with either method it
remains possible for modules to ultimately override *individual* view
scripts (I'm thinking scenarios like themes here). That granularity
will be key, IMO.

> As last I'd like to point to your multiple-engines approach: I am not sure
> what kind of options will be available for different renderers, but I can
> imagine your module A requires a renderer X with option Y turned ON while
> module B's scripts are based on renderer X with option Y turned OFF. How would
> you handle that? Therefore it might be better to have these options inside the
> namespace mapping for view scripts as well:
>
> 'Blog' => array(
>  'path' => __DIR__ . '/views/scripts/blog',
>  'renderer' => array(
>    'type' => 'Zend\View\PhpRenderer',
>    'options' => array()
>  )
> )
>
> This way the grouped namespace mapping makes even more sense (you'd use that
> for renderer options and script path together). Now you need to specify this
> explicitly in every module, but a sane default to Zend\View\PhpRenderer might
> mitigate this.

This is an excellent point, Jurian. We should definitely accommodate
this. Ideally, it would be nice if we could still share instances of
the renderer when possible, for example, Module A and B both require
renderer X with the same options.



Overall, great work Paul! It's great to finally see some public work
around views. I'm excited to see where this leads.

PS: Nick Belhomme, if you're following this thread, I think you were
looking at possibly doing some work in this space... I would be
interested to hear your thoughts on this approach as well. Maybe you
could collaborate with Paul here on the list or in IRC to help turn
this into something great!

Actually, that goes for everyone: The list has been a bit quiet
lately, but I'd really like to get everyone's input on this if you
have any ideas, as I think view script resolving and rendering is one
place where we still have a *lot* of room for improvement in ZF2.

--
Evan Coury

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Zend\View

gargoyle-3

On 11 Dec 2011, at 15:04, Evan Coury wrote:

> Hi Paul, Jurian,
>
> First of all, Thank you Paul for taking the time to code out a proof
> of concept of your ideas around the view stuff!
>
> On Sun, Dec 11, 2011 at 6:00 AM, Jurian Sluiman <[hidden email]> wrote:
>> On Sunday 11 December 2011 03:02:13 Paul Court wrote:
>>> Sorry for creating another thread, but after chatting with Evan this
>>> morning and having a play round, I think this is a bit more than just
>>> finding view scripts.
>>>
>>> Anyway, after speaking with Evan, we decided it would be nice if two boxes
>>> could be ticked:- * Being able to override individual view files from
>>> modules.
>>>       * Being able to use different renderers by checking the file extension.
>>>
>>> So, I've been having a little play around and come up with the following
>>> idea.
>>>
>>> Instead of the view being an instance of Zend\View\Renderer, I created a
>>> "top level" Zend\View class (which for the sake of getting it working
>>> quick, also implements the renderer interface). This top level class is
>>> created in index.php and listens for the loadModule event. If the module
>>> in question implements the Zend\Module\Consumer\ViewProvider interface,
>>> then the view calls getViewConfig() to get view information; It uses this
>>> information to keep an internal mapping of file extensions to renderers
>>> and namespaces / controller classes to view script paths.
>>>
>>> As each config is loaded, the view paths are scanned, and an internal list
>>> of found files is maintained for each map entry. When a call is made to
>>> render a script, the map is checked in reverse order (so modules loaded
>>> later can override earlier ones) looking for matching files.
>>>
>>> I have a basic working example on my blog which you can see via github:-
>>>       * my copy of zf2 - https://github.com/gargoyle/zf2/tree/ViewUpdates
>>>       * and the code for my blog:- https://github.com/gargoyle/ga.rgoyle.com
>>>
>>> (Warning: It's more than a little but rough round the edges and still needs
>>> much work!)
>>>
>>>
>>> At the moment I am assigning it to a global var (I told you it was rough!)
>>> and fetching the same instance in the View\Listener. What are your
>>> thoughts on the best way to handle getting the instance into the
>>> View\Listener? My first though would be a singleton, is there any reason
>>> for an app to have more than one view? Or maybe it becomes initialised as
>>> part of Zend\Module\Listener\DefaultListenerAggregate?
>>>
>>>
>>> Apologies if this does not make much sense, it's getting late!  Your
>>> thoughts and ideas will be much appreciated.
>>>
>>> Paul
>>> (Gargoyle)
>>
>> I think you could have written rough in capitals to make a better statement ;)
>>
>> However, I honestly like your idea and played with something similar without
>> the rendering "engines" but with a little more sophistication around the
>> TemplateResolver.
>
> I agree that the view script mapping idea is a great one, and
> worth-while strategy to pursue.
>
>> I liked the idea to inject the view scripts within the DI config and question
>> why you want to provide that with an additional Zend\Module\Consumer? If
>> Zend\View\PhpRenderer and Zend\View\TemplatePathStack are modified such it
>> allows namespaced scripts, you should be able to do that with DI as well.
>
> I think there's a balance to be had here. Personally, I think we
> should shy away from so heavily relying on DI. I'd much prefer if we
> first implement a proof of concept around views without any reliance
> on Zend\Di, then if it's appropriate to wire up the implementation
> with DI later, then users can do so. I think implementing something
> with a depedency on Zend\Di from the start is a bad approach (I have
> this same gripe with the current method of resolving controllers).
>
> Now regarding it being a loadModule event listener, I really don't see
> anything wrong there. However I think it needs to be decoupled from
> that. For example, the resolver could look something like this:
>
> <?php
>
> namespace Zend\View;
>
> class TemplateMapStack implements TemplateResolver
> {
>    /**
>     * Can optionally pass scriptMaps to constructor
>     *
>     * @param array|Traversable $scriptMaps
>     * @return void
>     */
>    public function __construct($scriptMaps = null);
>
>    /**
>     * An array of script map arrays
>     *
>     * @param array|Traversable $scriptMaps
>     * @return TemplateMapStack
>     */
>    public function setScriptMaps($scriptMaps);
>
>    /**
>     * addScriptMap
>     *
>     * @param array|Traversable $scriptMap
>     * @return TemplateMapStack
>     */
>    public function addScriptMap($scriptMap);
>
>    /**
>     * addMappedResolver
>     *
>     * @param MappedResolver $mappedResolver
>     * @return TemplateMapStack
>     */
>    public function addMappedResolver(MappedResolver $mappedResolver);
>
>    /**
>     * Retrieve the filesystem path to a view script
>     *
>     * @param  string $name
>     * @return string
>     */
>    public function getScriptPath($name);
> }
>
> A separate loadModule listener could still be used to wire this up
> from the individual Module classes, but the interface would also be
> DI-friendly and lend itself well to manual configuration and easy unit
> testing.
>
> Additionally, I could see renderers being handled via some sort of
> MultiRenderer class that implements Zend\View\Renderer, but possibly
> uses the Zend\Loader\PluginBroker or (preferably) Zend\EventManager
> system to allow the proper registered renderer to render each view
> script. I believe this could potentially even eliminate/replace what
> we currently know as the "view listener". The ultimate goal would be
> to allow an application using Twig to still be able to install and use
> a module that uses phtml. It would be a shame to end up with a ton of
> forks of the same exact module but ported for different template
> systems.
>
>> Another point is the way scripts are coupled to controllers. I'd like to have
>> this based on namespaces as well:
>>
>> 'Blog' => __DIR__ . '/views/scripts/blog',
>> 'BlogAdmin => __DIR__ . '/views/scripts/blog-admin',
>> 'Blog\Controller\SpecialController' => __DIR__ . '/views/scripts/special'
>>
>> You need to resolve the map in reverse order, having the most specific mappings
>> at the bottom of the stack. This way:
>>
>> * you don't need to specify the same directory over and over again
>> * allow groupings of controllers (eg subnamespaces, multiple namespaces within
>> a module)
>> * still have the option to override script paths per controller
>
> I think that Paul's approach makes how the initial mapping works
> largely inconsequential. You could do it either way without much
> changing the outcome due to the fact that it would ultimately be
> scanning and making a detailed map of all the individual view scripts
> that are available for each "stack". How we organize or group the
> "stacks" is just an implementation detail. Personally I'm fine with
> either namespace or controller as long as with either method it
> remains possible for modules to ultimately override *individual* view
> scripts (I'm thinking scenarios like themes here). That granularity
> will be key, IMO.
>
>> As last I'd like to point to your multiple-engines approach: I am not sure
>> what kind of options will be available for different renderers, but I can
>> imagine your module A requires a renderer X with option Y turned ON while
>> module B's scripts are based on renderer X with option Y turned OFF. How would
>> you handle that? Therefore it might be better to have these options inside the
>> namespace mapping for view scripts as well:
>>
>> 'Blog' => array(
>>  'path' => __DIR__ . '/views/scripts/blog',
>>  'renderer' => array(
>>    'type' => 'Zend\View\PhpRenderer',
>>    'options' => array()
>>  )
>> )
>>
>> This way the grouped namespace mapping makes even more sense (you'd use that
>> for renderer options and script path together). Now you need to specify this
>> explicitly in every module, but a sane default to Zend\View\PhpRenderer might
>> mitigate this.
>
> This is an excellent point, Jurian. We should definitely accommodate
> this. Ideally, it would be nice if we could still share instances of
> the renderer when possible, for example, Module A and B both require
> renderer X with the same options.
>
>
>
> Overall, great work Paul! It's great to finally see some public work
> around views. I'm excited to see where this leads.
>
> PS: Nick Belhomme, if you're following this thread, I think you were
> looking at possibly doing some work in this space... I would be
> interested to hear your thoughts on this approach as well. Maybe you
> could collaborate with Paul here on the list or in IRC to help turn
> this into something great!
>
> Actually, that goes for everyone: The list has been a bit quiet
> lately, but I'd really like to get everyone's input on this if you
> have any ideas, as I think view script resolving and rendering is one
> place where we still have a *lot* of room for improvement in ZF2.
>
> --
> Evan Coury
>
> --
> List: [hidden email]
> Info: http://framework.zend.com/archives
> Unsubscribe: [hidden email]
>
>


--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: Zend\View

gargoyle-3
In reply to this post by EvanDotPro
Great ideas Evan,

You are correct in that these should be separated out. So with that in mind, I think one of the first things to do is to nail what exact the TemplateResolver interface needs to define.

For a start, I think getScriptPath() needs an extra parameter - a "namespace". I put namespace in quotes because it doesn't have to be a namespace, but a unique ID so that there is some way to distinguish index.phtml in one module from index.phtml in module 2. This also provides a convenient way to match view scripts with controllers and actions, as we can get this from the event params in the viewListener.

I'm going to focus my efforts on Evan's suggestions to make the TemplateMapStack and MappedResolver classes.

This leaves a few other remaining hurdles. One of which is what is a "view" in ZF2?  In ZF1, it's an instance of ViewRenderer, but we want to try and support multiple renderers.

Please share your thoughts and ideas.

Paul



On 11 Dec 2011, at 15:04, Evan Coury wrote:

> Hi Paul, Jurian,
>
> First of all, Thank you Paul for taking the time to code out a proof
> of concept of your ideas around the view stuff!
>
> On Sun, Dec 11, 2011 at 6:00 AM, Jurian Sluiman <[hidden email]> wrote:
>> On Sunday 11 December 2011 03:02:13 Paul Court wrote:
>>> Sorry for creating another thread, but after chatting with Evan this
>>> morning and having a play round, I think this is a bit more than just
>>> finding view scripts.
>>>
>>> Anyway, after speaking with Evan, we decided it would be nice if two boxes
>>> could be ticked:- * Being able to override individual view files from
>>> modules.
>>>       * Being able to use different renderers by checking the file extension.
>>>
>>> So, I've been having a little play around and come up with the following
>>> idea.
>>>
>>> Instead of the view being an instance of Zend\View\Renderer, I created a
>>> "top level" Zend\View class (which for the sake of getting it working
>>> quick, also implements the renderer interface). This top level class is
>>> created in index.php and listens for the loadModule event. If the module
>>> in question implements the Zend\Module\Consumer\ViewProvider interface,
>>> then the view calls getViewConfig() to get view information; It uses this
>>> information to keep an internal mapping of file extensions to renderers
>>> and namespaces / controller classes to view script paths.
>>>
>>> As each config is loaded, the view paths are scanned, and an internal list
>>> of found files is maintained for each map entry. When a call is made to
>>> render a script, the map is checked in reverse order (so modules loaded
>>> later can override earlier ones) looking for matching files.
>>>
>>> I have a basic working example on my blog which you can see via github:-
>>>       * my copy of zf2 - https://github.com/gargoyle/zf2/tree/ViewUpdates
>>>       * and the code for my blog:- https://github.com/gargoyle/ga.rgoyle.com
>>>
>>> (Warning: It's more than a little but rough round the edges and still needs
>>> much work!)
>>>
>>>
>>> At the moment I am assigning it to a global var (I told you it was rough!)
>>> and fetching the same instance in the View\Listener. What are your
>>> thoughts on the best way to handle getting the instance into the
>>> View\Listener? My first though would be a singleton, is there any reason
>>> for an app to have more than one view? Or maybe it becomes initialised as
>>> part of Zend\Module\Listener\DefaultListenerAggregate?
>>>
>>>
>>> Apologies if this does not make much sense, it's getting late!  Your
>>> thoughts and ideas will be much appreciated.
>>>
>>> Paul
>>> (Gargoyle)
>>
>> I think you could have written rough in capitals to make a better statement ;)
>>
>> However, I honestly like your idea and played with something similar without
>> the rendering "engines" but with a little more sophistication around the
>> TemplateResolver.
>
> I agree that the view script mapping idea is a great one, and
> worth-while strategy to pursue.
>
>> I liked the idea to inject the view scripts within the DI config and question
>> why you want to provide that with an additional Zend\Module\Consumer? If
>> Zend\View\PhpRenderer and Zend\View\TemplatePathStack are modified such it
>> allows namespaced scripts, you should be able to do that with DI as well.
>
> I think there's a balance to be had here. Personally, I think we
> should shy away from so heavily relying on DI. I'd much prefer if we
> first implement a proof of concept around views without any reliance
> on Zend\Di, then if it's appropriate to wire up the implementation
> with DI later, then users can do so. I think implementing something
> with a depedency on Zend\Di from the start is a bad approach (I have
> this same gripe with the current method of resolving controllers).
>
> Now regarding it being a loadModule event listener, I really don't see
> anything wrong there. However I think it needs to be decoupled from
> that. For example, the resolver could look something like this:
>
> <?php
>
> namespace Zend\View;
>
> class TemplateMapStack implements TemplateResolver
> {
>    /**
>     * Can optionally pass scriptMaps to constructor
>     *
>     * @param array|Traversable $scriptMaps
>     * @return void
>     */
>    public function __construct($scriptMaps = null);
>
>    /**
>     * An array of script map arrays
>     *
>     * @param array|Traversable $scriptMaps
>     * @return TemplateMapStack
>     */
>    public function setScriptMaps($scriptMaps);
>
>    /**
>     * addScriptMap
>     *
>     * @param array|Traversable $scriptMap
>     * @return TemplateMapStack
>     */
>    public function addScriptMap($scriptMap);
>
>    /**
>     * addMappedResolver
>     *
>     * @param MappedResolver $mappedResolver
>     * @return TemplateMapStack
>     */
>    public function addMappedResolver(MappedResolver $mappedResolver);
>
>    /**
>     * Retrieve the filesystem path to a view script
>     *
>     * @param  string $name
>     * @return string
>     */
>    public function getScriptPath($name);
> }
>
> A separate loadModule listener could still be used to wire this up
> from the individual Module classes, but the interface would also be
> DI-friendly and lend itself well to manual configuration and easy unit
> testing.
>
> Additionally, I could see renderers being handled via some sort of
> MultiRenderer class that implements Zend\View\Renderer, but possibly
> uses the Zend\Loader\PluginBroker or (preferably) Zend\EventManager
> system to allow the proper registered renderer to render each view
> script. I believe this could potentially even eliminate/replace what
> we currently know as the "view listener". The ultimate goal would be
> to allow an application using Twig to still be able to install and use
> a module that uses phtml. It would be a shame to end up with a ton of
> forks of the same exact module but ported for different template
> systems.
>
>> Another point is the way scripts are coupled to controllers. I'd like to have
>> this based on namespaces as well:
>>
>> 'Blog' => __DIR__ . '/views/scripts/blog',
>> 'BlogAdmin => __DIR__ . '/views/scripts/blog-admin',
>> 'Blog\Controller\SpecialController' => __DIR__ . '/views/scripts/special'
>>
>> You need to resolve the map in reverse order, having the most specific mappings
>> at the bottom of the stack. This way:
>>
>> * you don't need to specify the same directory over and over again
>> * allow groupings of controllers (eg subnamespaces, multiple namespaces within
>> a module)
>> * still have the option to override script paths per controller
>
> I think that Paul's approach makes how the initial mapping works
> largely inconsequential. You could do it either way without much
> changing the outcome due to the fact that it would ultimately be
> scanning and making a detailed map of all the individual view scripts
> that are available for each "stack". How we organize or group the
> "stacks" is just an implementation detail. Personally I'm fine with
> either namespace or controller as long as with either method it
> remains possible for modules to ultimately override *individual* view
> scripts (I'm thinking scenarios like themes here). That granularity
> will be key, IMO.
>
>> As last I'd like to point to your multiple-engines approach: I am not sure
>> what kind of options will be available for different renderers, but I can
>> imagine your module A requires a renderer X with option Y turned ON while
>> module B's scripts are based on renderer X with option Y turned OFF. How would
>> you handle that? Therefore it might be better to have these options inside the
>> namespace mapping for view scripts as well:
>>
>> 'Blog' => array(
>>  'path' => __DIR__ . '/views/scripts/blog',
>>  'renderer' => array(
>>    'type' => 'Zend\View\PhpRenderer',
>>    'options' => array()
>>  )
>> )
>>
>> This way the grouped namespace mapping makes even more sense (you'd use that
>> for renderer options and script path together). Now you need to specify this
>> explicitly in every module, but a sane default to Zend\View\PhpRenderer might
>> mitigate this.
>
> This is an excellent point, Jurian. We should definitely accommodate
> this. Ideally, it would be nice if we could still share instances of
> the renderer when possible, for example, Module A and B both require
> renderer X with the same options.
>
>
>
> Overall, great work Paul! It's great to finally see some public work
> around views. I'm excited to see where this leads.
>
> PS: Nick Belhomme, if you're following this thread, I think you were
> looking at possibly doing some work in this space... I would be
> interested to hear your thoughts on this approach as well. Maybe you
> could collaborate with Paul here on the list or in IRC to help turn
> this into something great!
>
> Actually, that goes for everyone: The list has been a bit quiet
> lately, but I'd really like to get everyone's input on this if you
> have any ideas, as I think view script resolving and rendering is one
> place where we still have a *lot* of room for improvement in ZF2.
>
> --
> Evan Coury
>
> --
> List: [hidden email]
> Info: http://framework.zend.com/archives
> Unsubscribe: [hidden email]
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Zend\View

weierophinney
Administrator
-- Paul Court <[hidden email]> wrote
(on Thursday, 15 December 2011, 10:03 PM +0000):

> You are correct in that these should be separated out. So with that in
> mind, I think one of the first things to do is to nail what exact the
> TemplateResolver interface needs to define.
>
> For a start, I think getScriptPath() needs an extra parameter - a
> "namespace". I put namespace in quotes because it doesn't have to be a
> namespace, but a unique ID so that there is some way to distinguish
> index.phtml in one module from index.phtml in module 2. This also
> provides a convenient way to match view scripts with controllers and
> actions, as we can get this from the event params in the viewListener.
>
> I'm going to focus my efforts on Evan's suggestions to make the
> TemplateMapStack and MappedResolver classes.

I'm actually thinking we need to move away from using extensions when
calling render. Instead of:

    $view->render('foo/index.phtml')

we'd do this:
   
    $view->render('foo/index')

In other words, the requested "script" is now simply a token.

This allows the individual renderers to determine what view script to
use (if any -- some renderers may map those to strategies instead), in a
way that's agnostic of the technology. As an example, the PhpRenderer
would use ".phtml", but a Mustache renderer might use ".mustache", or a
Smarty renderer ".tpl".

Which leads into the next point:

> This leaves a few other remaining hurdles. One of which is what is a
> "view" in ZF2?  In ZF1, it's an instance of ViewRenderer, but we want
> to try and support multiple renderers.

I think we need a layer above the renderers that does the following:

 * Context switching based on the Request object. Typically, this would
   mean introspecting the Accept headers to determine what content types
   the client can handle, and then mapping that to an appropriate
   renderer. Alternates to the PhpRenderer strategy might include a JSON
   renderer, or XML or RSS or Atom renderer. Which leads to:

 * Aggregation of renderer strategies. Typically, these would be mapped
   to content-types, but we could have it event-driven, which would
   allow for more complex strategies.

 * Layout-awareness. Based on the request and selected renderer, we
   could hint which layout to use, if any, and have rendered content
   immediately injected into the layout. This would simply be via a
   listener on the view object.

 * Response integration. Renderers and/or listeners would set the
   Content-Type on the response, as well as inject the rendered content
   into the response.

Basically, a true "View" object, not just a renderer.



> On 11 Dec 2011, at 15:04, Evan Coury wrote:
>
> > Hi Paul, Jurian,
> >
> > First of all, Thank you Paul for taking the time to code out a proof
> > of concept of your ideas around the view stuff!
> >
> > On Sun, Dec 11, 2011 at 6:00 AM, Jurian Sluiman <[hidden email]> wrote:
> >> On Sunday 11 December 2011 03:02:13 Paul Court wrote:
> >>> Sorry for creating another thread, but after chatting with Evan this
> >>> morning and having a play round, I think this is a bit more than just
> >>> finding view scripts.
> >>>
> >>> Anyway, after speaking with Evan, we decided it would be nice if two boxes
> >>> could be ticked:- * Being able to override individual view files from
> >>> modules.
> >>>       * Being able to use different renderers by checking the file extension.
> >>>
> >>> So, I've been having a little play around and come up with the following
> >>> idea.
> >>>
> >>> Instead of the view being an instance of Zend\View\Renderer, I created a
> >>> "top level" Zend\View class (which for the sake of getting it working
> >>> quick, also implements the renderer interface). This top level class is
> >>> created in index.php and listens for the loadModule event. If the module
> >>> in question implements the Zend\Module\Consumer\ViewProvider interface,
> >>> then the view calls getViewConfig() to get view information; It uses this
> >>> information to keep an internal mapping of file extensions to renderers
> >>> and namespaces / controller classes to view script paths.
> >>>
> >>> As each config is loaded, the view paths are scanned, and an internal list
> >>> of found files is maintained for each map entry. When a call is made to
> >>> render a script, the map is checked in reverse order (so modules loaded
> >>> later can override earlier ones) looking for matching files.
> >>>
> >>> I have a basic working example on my blog which you can see via github:-
> >>>       * my copy of zf2 - https://github.com/gargoyle/zf2/tree/ViewUpdates
> >>>       * and the code for my blog:- https://github.com/gargoyle/ga.rgoyle.com
> >>>
> >>> (Warning: It's more than a little but rough round the edges and still needs
> >>> much work!)
> >>>
> >>>
> >>> At the moment I am assigning it to a global var (I told you it was rough!)
> >>> and fetching the same instance in the View\Listener. What are your
> >>> thoughts on the best way to handle getting the instance into the
> >>> View\Listener? My first though would be a singleton, is there any reason
> >>> for an app to have more than one view? Or maybe it becomes initialised as
> >>> part of Zend\Module\Listener\DefaultListenerAggregate?
> >>>
> >>>
> >>> Apologies if this does not make much sense, it's getting late!  Your
> >>> thoughts and ideas will be much appreciated.
> >>>
> >>> Paul
> >>> (Gargoyle)
> >>
> >> I think you could have written rough in capitals to make a better statement ;)
> >>
> >> However, I honestly like your idea and played with something similar without
> >> the rendering "engines" but with a little more sophistication around the
> >> TemplateResolver.
> >
> > I agree that the view script mapping idea is a great one, and
> > worth-while strategy to pursue.
> >
> >> I liked the idea to inject the view scripts within the DI config and question
> >> why you want to provide that with an additional Zend\Module\Consumer? If
> >> Zend\View\PhpRenderer and Zend\View\TemplatePathStack are modified such it
> >> allows namespaced scripts, you should be able to do that with DI as well.
> >
> > I think there's a balance to be had here. Personally, I think we
> > should shy away from so heavily relying on DI. I'd much prefer if we
> > first implement a proof of concept around views without any reliance
> > on Zend\Di, then if it's appropriate to wire up the implementation
> > with DI later, then users can do so. I think implementing something
> > with a depedency on Zend\Di from the start is a bad approach (I have
> > this same gripe with the current method of resolving controllers).
> >
> > Now regarding it being a loadModule event listener, I really don't see
> > anything wrong there. However I think it needs to be decoupled from
> > that. For example, the resolver could look something like this:
> >
> > <?php
> >
> > namespace Zend\View;
> >
> > class TemplateMapStack implements TemplateResolver
> > {
> >    /**
> >     * Can optionally pass scriptMaps to constructor
> >     *
> >     * @param array|Traversable $scriptMaps
> >     * @return void
> >     */
> >    public function __construct($scriptMaps = null);
> >
> >    /**
> >     * An array of script map arrays
> >     *
> >     * @param array|Traversable $scriptMaps
> >     * @return TemplateMapStack
> >     */
> >    public function setScriptMaps($scriptMaps);
> >
> >    /**
> >     * addScriptMap
> >     *
> >     * @param array|Traversable $scriptMap
> >     * @return TemplateMapStack
> >     */
> >    public function addScriptMap($scriptMap);
> >
> >    /**
> >     * addMappedResolver
> >     *
> >     * @param MappedResolver $mappedResolver
> >     * @return TemplateMapStack
> >     */
> >    public function addMappedResolver(MappedResolver $mappedResolver);
> >
> >    /**
> >     * Retrieve the filesystem path to a view script
> >     *
> >     * @param  string $name
> >     * @return string
> >     */
> >    public function getScriptPath($name);
> > }
> >
> > A separate loadModule listener could still be used to wire this up
> > from the individual Module classes, but the interface would also be
> > DI-friendly and lend itself well to manual configuration and easy unit
> > testing.
> >
> > Additionally, I could see renderers being handled via some sort of
> > MultiRenderer class that implements Zend\View\Renderer, but possibly
> > uses the Zend\Loader\PluginBroker or (preferably) Zend\EventManager
> > system to allow the proper registered renderer to render each view
> > script. I believe this could potentially even eliminate/replace what
> > we currently know as the "view listener". The ultimate goal would be
> > to allow an application using Twig to still be able to install and use
> > a module that uses phtml. It would be a shame to end up with a ton of
> > forks of the same exact module but ported for different template
> > systems.
> >
> >> Another point is the way scripts are coupled to controllers. I'd like to have
> >> this based on namespaces as well:
> >>
> >> 'Blog' => __DIR__ . '/views/scripts/blog',
> >> 'BlogAdmin => __DIR__ . '/views/scripts/blog-admin',
> >> 'Blog\Controller\SpecialController' => __DIR__ . '/views/scripts/special'
> >>
> >> You need to resolve the map in reverse order, having the most specific mappings
> >> at the bottom of the stack. This way:
> >>
> >> * you don't need to specify the same directory over and over again
> >> * allow groupings of controllers (eg subnamespaces, multiple namespaces within
> >> a module)
> >> * still have the option to override script paths per controller
> >
> > I think that Paul's approach makes how the initial mapping works
> > largely inconsequential. You could do it either way without much
> > changing the outcome due to the fact that it would ultimately be
> > scanning and making a detailed map of all the individual view scripts
> > that are available for each "stack". How we organize or group the
> > "stacks" is just an implementation detail. Personally I'm fine with
> > either namespace or controller as long as with either method it
> > remains possible for modules to ultimately override *individual* view
> > scripts (I'm thinking scenarios like themes here). That granularity
> > will be key, IMO.
> >
> >> As last I'd like to point to your multiple-engines approach: I am not sure
> >> what kind of options will be available for different renderers, but I can
> >> imagine your module A requires a renderer X with option Y turned ON while
> >> module B's scripts are based on renderer X with option Y turned OFF. How would
> >> you handle that? Therefore it might be better to have these options inside the
> >> namespace mapping for view scripts as well:
> >>
> >> 'Blog' => array(
> >>  'path' => __DIR__ . '/views/scripts/blog',
> >>  'renderer' => array(
> >>    'type' => 'Zend\View\PhpRenderer',
> >>    'options' => array()
> >>  )
> >> )
> >>
> >> This way the grouped namespace mapping makes even more sense (you'd use that
> >> for renderer options and script path together). Now you need to specify this
> >> explicitly in every module, but a sane default to Zend\View\PhpRenderer might
> >> mitigate this.
> >
> > This is an excellent point, Jurian. We should definitely accommodate
> > this. Ideally, it would be nice if we could still share instances of
> > the renderer when possible, for example, Module A and B both require
> > renderer X with the same options.
> >
> >
> >
> > Overall, great work Paul! It's great to finally see some public work
> > around views. I'm excited to see where this leads.
> >
> > PS: Nick Belhomme, if you're following this thread, I think you were
> > looking at possibly doing some work in this space... I would be
> > interested to hear your thoughts on this approach as well. Maybe you
> > could collaborate with Paul here on the list or in IRC to help turn
> > this into something great!
> >
> > Actually, that goes for everyone: The list has been a bit quiet
> > lately, but I'd really like to get everyone's input on this if you
> > have any ideas, as I think view script resolving and rendering is one
> > place where we still have a *lot* of room for improvement in ZF2.
> >
> > --
> > Evan Coury
> >
> > --
> > List: [hidden email]
> > Info: http://framework.zend.com/archives
> > Unsubscribe: [hidden email]
> >
> >
>

--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc

--
List: [hidden email]
Info: http://framework.zend.com/archives
Unsubscribe: [hidden email]