RE: [SMARTY-DEV] Integrating Smarty with Content Management Syste m, questions and design issues (long posting)

From: Date: Wed, 20 Nov 2002 20:56:56 +0000
Subject: RE: [SMARTY-DEV] Integrating Smarty with Content Management Syste m, questions and design issues (long posting)
Groups: php.smarty.dev 
Request: Send a blank email to php-smarty-dev+get-340@lists.php.net to get a copy of this message
REGARDING: > 1. Template "inheritance" > ... > Do you see any good way to implement such behavior with > Smarty? >... I had a similar problem which I have solved with a patch to smarty, and a new file-resource for loading templates. I'm pretty pleased with how well it has worked out for me. SUMMARY ------- The resource plugin lets smarty load (display, fetch, {include }, etc...) templates from a hierarchical list of directories (kind of like a classpath). With a few little waves of the programmatic magic wand, this lets me set up the ability for different users or subsystems to override templates at any level, by simply placing an overriding template file into a directory where none previously existed. Example: If a given template file exists in a user's home directory, it will be loaded instead of the default version of the template in the common directory. The patch makes smarty query the resource plugin for the compile_id at template-load time, when no compile id has been specified manually. The resource plugin simply builds the compile_id from the full-path-to-template. [this solves the "files with same name overwrite each other on compilation" problem]. The patch also makes smarty understand a new architecture for resource plugins. This architecture is class-based. Instead of requiring functions to be declared in the static namespace, you simply define a class that implements the appropriate methods. smarty instantiates the class, and uses it to load resources when you specify the corresponding resource name ('tps:<whatever>.tpl'). This provides complete encapsulation of the resource, and the files loaded by the resource. It also allows resource plugins to maintain state. I'm planning to make similar mods to the rest of the plugin types at some point. Oh...and I didn't break smarty's support for the "old" style plugins either, this is just an add-on. if you're interested, read on. If not...cheers -Lee DETAILED DESCRIPTION --------------------- I have built a portal server that uses smarty to allow users to define, customize, and extend portals in a variety of situations, with out changing any PHP code. In my portal server, each portal is a class with methods much like your content engine (initialize(), allReady(), renderHead(), renderBody() etc...). A basic portal class works by simply initializing the smarty object with the appropriate vars, and calling: smarty->display("tps:portal.tpl"). [NOTE: 'tps' is my custom resource loader name, more on that in a moment] More complicated portals can perform additional initializations, manipulations, database operations, etc in PHP before calling portal.tpl. A custom (i.e.. derived) portal class does not even have to call portal.tpl. It could print the output directly, without a template, or it could call one or more differently named smarty templates. The magic all happens in the custom resource loader plugin, 'tps'. The plugin loads templates through a ResourceLoader class that lives statically outside the smarty object. This ResourceLoader class maintains a hierarchy of directories which are checked sequentially for any requested template name. When the portal server dispatches a request to a portal class, it "pushes" the portal's directory onto the top of the loader hierarchy. Then, when a portal says "$smarty->display('tps:portal.tpl')", the portal's local directory is checked, before checking the portal server's "common" directory, before finally checking the root directory of the application. It is this custom loader that then allowed me to then write a user-customizable portal for displaying a user's home page. The base implementation of this showhomepage portal class provides a "generic" homepage display for every user (based on information in the database). Individual users can then create templates in their home directories that will override different sub-sections of the "generic" homepage. A user could even override the master template for the showhomepage portal, thus replacing the entire page (not just a subsection). The showhomepage portal simply finds the user's home directory, and pushes it into the ResourceLoader hierarchy during the initialization phase of the portal. (it pops the home dir back off the hierarchy during the cleanup phase). Therefore, every attempt to load a template from within the showhomepage portal, checks the user's home directory for an overriding template *before* loading the base template from the showhomepage portal directory. For example, take the following directory structure: / home/ lee/ homepage_description.tpl bob/ <some other stuff in bob's home dir> jane/ portal.tpl portals/ showhomepage/ homepage_header.tpl homepage_description.tpl homepage_navbar.tpl portal.tpl someotherportal/ ... if '/portals/showhomepage/portal.tpl' looks something like this: <HTML> <HEAD> <TITLE> homepage for user: {$user.name} </TITLE> </HEAD> <BODY> {include file='tps:homepage_header.tpl'} <BR> {include file='tps:homepage_navbar.tpl'} <BR> {include file='tps:homepage_description.tpl'} </BODY> </HTML> Then when we display bob's homepage, the server will use the templates located in /portals/showhomepage/. But when we display lee's homepage, the server finds that homepage_description.tpl is overridden in lee's home directory (/home/lee/), and it will use that template instead of the default version located in /portals/showhomepage/. There was one major snag in implementing this, which I have worked around by modifying the resource-plugin architecture of smarty. My resource plugin's hierarchical loading allows different files with the same name to be loaded/compiled/displayed without the template_dir actually being changed. The problem with this (as you stated) is that the compiled version of /foo/portal.tpl will overwrite the compiled version of /bar/portal.tpl if they are both loaded as 'portal.tpl'. To solve this problem, I modified the resource plugin architecture to allow the resource plugin to provide the 'compile_id' to smarty when the resource is loaded. If compile_id is not specified manually by a call to display( name, [cache_id, [compile_id]] ), then the resource plugin will be queried for a compile id. My plugin then simply derives the compile id from the full path to the template that was actually loaded. ...and all works out. This all works quite well right now. I am well pleased with how easily my users can override templates to produce custom pages, without touching code, or screwing up the default templates. Also, since people typically only override templates for sub-sections, I can add new sub-sections to the default main template and see the changes reflected, even on pages with customized sub-sections. Thanks for reading this far. If you're interested, I'd be happy to share my work with you. I plan to submit my resource loader, and the necessary changes to the smarty architecture, back to Monte for inclusion in the smarty distrib at some point. I have simply not had the time to produce the diffs against the CVS tree, and document things the way they should be. I won't have that kind of time until I finish my current project anyway. Since it's not directly relevant, I'm not going to cover my "new" class-based resource plugin architecture here. If you're interested in that, drop me a mail, and I'll forward you a summary that I wrote previously to Monte. thanks, and good luck, -Lee > -----Original Message----- > From: Matthias Pigulla [mailto:mp@webfactory.de] > Sent: Tuesday, November 19, 2002 6:27 PM > To: 'smarty-dev@lists.php.net' > Subject: [SMARTY-DEV] Integrating Smarty with Content > Management System, > questions and design issues (long posting) > > > Hello everyone, > > I am involved in the development of a PHP based content > management system. > We have already got a stable engine for merging content and design at > runtime and do some caching of the results. However, our > templates still > require some PHP coding experience, maybe even too tricky for (coding > unexperienced) web designers >:). > > Thus, we chose to integrate a template engine which can be > extended to fit > our needs and which requires no PHP coding in the templates at all. Of > course, this implies that we have to be able to hide our API > calls in some > custom tags. > > Currently, I am trying to make a feasibility study on > integrating Smarty > with our system. I have played around quite a bit now and have some > questions. Maybe you have had similar problems and would like > to share your > experiences, solutions or workarounds. > > Should we find that Smarty is our best option, we would of > course contribute > our modifications or extensions to the project. > > 1. Template "inheritance" > > By now, we've implemented templates as PHP classes. > Basically, there's a > base class (lets say, "Template") which provides a "draw" > method, which, in > turn, calls methods like $this->head(), $this->navigation(), > $this->mainContent()... Each of these is responsible for outputting a > certain part of the page, however, this subdivision is > completely up to you. > > This allows you to subclass your base template to override a > specific part > of your pages. Say you would like to change your navigation > style, just > subclass your base template and override the navigation() > method. You just > have to implement the "different" parts with no unnecessary code > replication. > > Do you see any good way to implement such behavior with > Smarty? We could > have different templates for the default implementation of > different parts > (head, navigation, ...) and glue these together with the > include directive. > However, each of your templates would have to include these > subtemplates in > the appropriate place; you also could not simply add a new > part by adding a > method to the base class and calling it from the draw() > method. Instead, you > would have to change _all_ templates and add the "new" > include directive. > (Including the "new" part from an already included default > file breaks for > templates overriding this default file!) > > 2. Applying modifiers on (custom) functions > > We would need to implement some custom functions for > encapsulating our API > calls in Smarty tags. Lets assume we have a custom "tag" used > as follows: > > <h1>{cmscontent field="title"}</h1> > <p>{cmscontent field="bodytext"}</p> > > Now I'd like to give the designer the ability to use Smarty modifiers. > However, this is not possible with functions. This would > require a change of > the parsing process (I assume in _compile_tag in > Smarty_Compiler around line > 236), as {myfunc|modifier} is interpreted as a custom function > "myfunc|modifier" and {myfunc | modifier} is interpreted as a function > myfunc with (invalid) arguments. Even more difficult, modifiers are > functions and are supposed to take their input as argument > and return it > modified; custom functions are required to write out their output at > runtime. So I do not see a (direct) way to make this work. > > The problem is that some of the API calls are quite > expensive, so I do not > want to get everything in advance and assign it to template vars. A > workaround would be something like > ...{assigncmscontent field="title" > var="vtitle"}{$vtitle|upper}, i. e. make > the function assign a variable and use this later on. I > consider this kind > of cumbersome. > > Any ideas on this? Maybe I misunderstood something and should > not use a > custom function for this task? > > 3. (Custom) block functions with block code depending on their result > > As far as I can see, the current implementation of custom > block functions > does not allow your block function to set up variables that > can be used at > run time from within the block code. > > I would like to hide some kind of "iterator" concept from our > API inside the > block function, e. g. > <ul>{iteratenavigation rootid="123" mode="preorder" > var="navelement"} > <li>{$navelement.text}</li> > <!-- or even -->{cmscontent var=$navelement field="text"} > {/iteratenavigation}</ul> > > The problem is that the code inside the block and its variables are > evaluated first and the output is passed to the block > function at runtime, > so that there is no way of assigning variables in the block > function and > using them in the block. There is another posting related to this: > > http://marc.theaimsgroup.com/?l=smarty-general&m=103729427302672&w=29 > > What was the design motivation for this? > > I would like to insert code at the compile phase, that is > replacing the > block tags with code which is executed the same time the > block content is > interpreted. This way we could encapsulate iterating in the > block functions, > not only looping over or processing already-evaluated code. > > I presume I would have to subclass Smarty_Compiler, as it > cannot (does not) > map a {foo}...{/foo} pattern to compiler functions. However, > as the compiler > function interface is inappropriate anyway, the (better?) > idea is to replace > _compile_block_tag and make changes in the last few lines, i. > e. do not > write out the code to execute the block function at runtime, > but instead > call the block function at compile time and have it insert > real do/while > constructs into the compiled template. > > Of course, this might break compatibility? Where? Any > solutions, ideas? > > [Glad to see you're still with me :)] Maybe I was not always > clear enough > with my thoughts or have uttered stupid thoughts? However, > let me know, as I > would be happy with every reply I get :) Btw, the line > numbers I gave may be > incorrect, as I just saw that there's a new release. > > Thanks in advance, > Matthias > > -- > Smarty Development Mailing List (http://smarty.php.net/) > To unsubscribe, visit: http://www.php.net/unsub.php >

« previous php.smarty.dev (#340) next »