![Smarty Forum Index Smarty Forum Index](templates/smarty/images/phpbb2_logo.gif) |
Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon. |
|
View previous topic :: View next topic |
Author |
Message |
sweatje Smarty Regular
Joined: 17 Apr 2003 Posts: 70 Location: Bettendorf, Iowa, USA
|
Posted: Thu Jun 05, 2003 4:00 pm Post subject: Industrial Strength MVC |
|
|
Industrial Strength MVC is the sample article for the June issue of PHP|Architect.
It is available for download here .
The article covers the development of a simple database application using Phrame MVC framework and Smarty for views. In particular, I implemented the "Factory Pattern" to couple view objects with Smarty. Code downloads are available on the same page as the article.
Feel free to comment on this thread, or on the php|a discussion board here.
I hope you find it useful. _________________ Jason
jsweat_php AT yahoo DOT com |
|
Back to top |
|
mohrt Administrator
Joined: 16 Apr 2003 Posts: 7368 Location: Lincoln Nebraska, USA
|
Posted: Thu Jun 05, 2003 6:15 pm Post subject: |
|
|
Very impressive article, well done!
I like the detail you go into about moving part of the model to the database. You might get a few die-hard MySQL users raising an eyebrow to PostgreSQL after they read this
Monte |
|
Back to top |
|
sweatje Smarty Regular
Joined: 17 Apr 2003 Posts: 70 Location: Bettendorf, Iowa, USA
|
Posted: Thu Jun 05, 2003 6:26 pm Post subject: |
|
|
Now that MySQL has taken over SAPdb, they wont need to raise an eyebrow right
Unless of course they end up producing a database with the ease of use and installation of SAPdb with the features of MySQL ![Shocked](images/smiles/icon_eek.gif) _________________ Jason
jsweat_php AT yahoo DOT com |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Fri Jun 06, 2003 3:03 am Post subject: |
|
|
I only had time to look over the article. Again, it looks like another well done piece, Jason. Especially useful to those who are trying to rachet up their technical skills as you highlight several important professional-level considerations in system design. I must say--I sure do like PHP|a !
A few comments:
1) Instead of bringing up the issue of MVC for web-apps, I will instead notice that many of the classes only abstract data, which in general, I feel is better kept out of the code base. For example, I would have wrote LinksMap in such a way that it retrieves its data model from the database (or a configuration file) instead of having it defined in the class itself. For myself, I like doing things like class loading etc with generic static classes. In the end, I have far fewer classes to manage and my verbs stay the same. I realize you are presenting introductory material so that you have to remain a bit more canonical, but I wonder how far you would go with such a design in a production system. More aptly, at what point do you find MVC becomes too cumbersome for web apps?
2) Any word on the PostgreSQL native Windows port? It looked promising when I looked at it a few months ago but I haven't checked on it since. I have had bad luck using the Cygwin port which I feel is unusable in a Windows based production environment. I am VERY GLAD that you focused on business logic in the database. I think that in general, putting business rules in the DB is a marvelous soultion (even if not highly portable) and often is the Right Thing To Do. I will continue to think of MySQL as low-grade until it gets views, triggers and stored procedures.
Again, very well done! I look forward to your future articles!! |
|
Back to top |
|
sweatje Smarty Regular
Joined: 17 Apr 2003 Posts: 70 Location: Bettendorf, Iowa, USA
|
Posted: Fri Jun 06, 2003 3:53 am Post subject: |
|
|
boots wrote: | I wonder how far you would go with such a design in a production system. More aptly, at what point do you find MVC becomes too cumbersome for web apps? |
I have been very happy using MVC for most of the apps I write. The point at which I think the pattern might be strained (at least my brain would be strained on figuring out how to continue using the pattern) would be if I were to try to write an application large enough to warrent some kind of a modular/plug-in oriented architecture (like Smarty ) For the kinds of applications I have been writing (intranet based application, usually for data mining or data maintenance) it has worked well.
boots wrote: | Any word on the PostgreSQL native Windows port? It looked promising when I looked at it a few months ago but I haven't checked on it since. I have had bad luck using the Cygwin port which I feel is unusable in a Windows based production environment. |
I am actually not much of a Postgres guru (see this), this was my first use of it! I wanted to expose people to the kinds of things I do, but an article based on Oracle would not be very accessable. Postgres certainly was up to what I wanted to show, but I was doing all my development on a RedHat Linux box. I used to be pretty gung hoe MS (Microsoft Certified Profesional and all that) but after a few years of working under Unix and Linux, and am not inclined to us Windows for my DB or web servers now (still forced to do some ASP on IIS at work, but I detest the lack of Smarty there - in ASP that is ).
boots wrote: | Again, very well done! I look forward to your future articles!! |
Well, I have been devoting way TOO much time to articles lately (and books for companies that decide to go bankrupt two weeks after publication ), and my daughter just graduated from kindergarten yesterday, so I think I will take a little break from writing for a while. Maybe another inspiration will come along.... _________________ Jason
jsweat_php AT yahoo DOT com |
|
Back to top |
|
sweatje Smarty Regular
Joined: 17 Apr 2003 Posts: 70 Location: Bettendorf, Iowa, USA
|
Posted: Mon Jun 09, 2003 1:15 pm Post subject: |
|
|
boots wrote: | I will instead notice that many of the classes only abstract data, which in general, I feel is better kept out of the code base. For example, I would have wrote LinksMap in such a way that it retrieves its data model from the database (or a configuration file) instead of having it defined in the class itself. For myself, I like doing things like class loading etc with generic static classes. In the end, I have far fewer classes to manage and my verbs stay the same. |
In the original Phrame examples, the arrays output from LinksMap's method were just configured as an array in a configuration file. In Struts, this same configuration is done via an XML configuration file. I ended up adopting this approach for a couple of reasons:
1) the dimensionality of the array was kind of ugly, i.e. assosiative arrays of assosiative arrays of assosiative arrays. Most additions we just copy & paste operations, but is was easy to screw up
leading to 2) the _AddForm, _AddMapping and _AddForward methods do data validation to make sure you are constructing a valid configuration array as you are constructing the mapping via the methods.
3) when I was manipulating the mappings as an array, I found there were many "defaults" I was using over and over, with the method options, I was able to code the "defaults" as default parameters to the methods.
I guess it all boils down to personal preference in the end for the mapping configurations (BTW, I discussed this in more depth originally in the "Introduction to MVC Using PHP" article in the May PHP|Architect, but since this is subscription only, perhaps you did not read it?)
Your overall point may apply more to Model classes accessing data from a database in general. I have some examples where I have done this that may lean toward what you were talking about. Here is an example "model" base class and one extended from it:
[php:1:62f87b19c2]<?php
class Model
{
var $_aoConn; //database connection
var $_aoData; //custom data caching object
function Model(&$poData, &$poConn)
{
$this->_aoData = &$poData;
$this->_aoConn = &$poConn;
}
function _SetPckgFunct($psDataIdent, $psPckgFunct, $paBind=false, $pbEmptyError=true)
{
$o_rs = $this->_aoConn->ExecuteCursor(
"begin commit; DBMS_TRANSACTION.READ_ONLY; :cur := $psPckgFunct; DBMS_TRANSACTION.COMMIT; end;"
,'cur'
,$paBind
);
if (!$o_rs === false) {
if ($o_rs->EOF) {
if ($pbEmptyError) {
trigger_error("The refresh call for $psDataIdent returned no results from $psPckgFunct.");
return false;
} else {
return array();
}
} else {
$a_list = $o_rs->GetArray();
$this->_aoData->SetCacheData($psDataIdent, $a_list);
return $a_list;
}
} else {
trigger_error("The refresh call for $psDataIdent cause a database error in $psPckgFunct.");
return false;
}
}
function _GetPckgFunct($psDataIdent, $psPckgFunct, $paBind=false, $pbEmptyError=true)
{
if ($this->_aoData->IsCacheIdent($psDataIdent)) {
$a_ret = $this->_aoData->GetCachedData($psDataIdent);
} else {
$a_ret = $this->_SetPckgFunct($psDataIdent, $psPckgFunct, $paBind, $pbEmptyError);
}
return $a_ret;
}
function _GetPckgFunctNoCache($psPckgFunct, $paBind=false, $pbEmptyError=true)
{
$o_rs = $this->_aoConn->ExecuteCursor(
"begin :cur := $psPckgFunct; end;"
,'cur'
,$paBind
);
if (!$o_rs === false) {
if ($o_rs->EOF) {
if ($pbEmptyError) {
trigger_error("The refresh call for $psPckgFunct returned no results.");
return false;
} else {
return array();
}
} else {
return $o_rs->GetArray();
}
} else {
trigger_error("The refresh call for $psPckgFunct caused a database error.");
return false;
}
}
}
?>[/php:1:62f87b19c2]
and the extened model class:
[php:1:62f87b19c2]<?php
define('ORDER_DEPOT_PLANT_MONTH', 'depot_web.GetOrdersByPlantMonth(:PLANT, :TYPE, :CUST, :MONTHS)');
//other class constants...
class Order extends Model {
function Order()
{
global $go_conn;
global $go_data;
$this->Model($go_data, $go_conn);
}
/**
* retrieve list of plant/product catalog combinations
* @return array
*/
function GetDepotPlant($psPlant, $piMonths=12)
{
$a_bind = array(
'PLANT' => $psPlant
, 'TYPE' => 'D'
, 'CUST' => 'Y'
, 'MONTHS' => (int)$piMonths
);
$s_cache_id = 'dp'.md5(serialize($a_bind));
return $this->_GetPckgFunct($s_cache_id, ORDER_DEPOT_PLANT_MONTH, $a_bind);
}
function RefreshDepotPlant($psPlant, $piMonths=12)
{
$a_bind = array(
'PLANT' => $psPlant
, 'TYPE' => 'D'
, 'CUST' => 'Y'
, 'MONTHS' => (int)$piMonths
);
$s_cache_id = 'dp'.md5(serialize($a_bind));
return $this->_SetPckgFunct($s_cache_id, ORDER_DEPOT_PLANT_MONTH, $a_bind);
}
// many other methods related to this model
}
?>[/php:1:62f87b19c2]
So most of the "logic" regarding what to return is coded in the database procedures. Each function to retrieve data just packages up the bind variables and feeds them to the appropriate DB function. Is this closer to what you were hinting at? _________________ Jason
jsweat_php AT yahoo DOT com |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Jun 10, 2003 4:19 am Post subject: |
|
|
Hi sweatje!
I enjoyed your comments. I must admit I am a fan of front-controllers and I think that the material in your MVC framework meets a wide variety of application needs. (yeah, I'm a subscriber!)
I am a bit of an experimenter, though, so please take my comments as being from someone who is trying to poke around to see what there is to see For example, right now I'm interested in understanding more about the underlying strategies of the MVC pattern (I did read GoF) to see how it may apply to a strategy that I am trying to evolve.
I find myself using a lot of hybrid approaches. Often I will start with a modeled object framework only to find that as I abstract data out of my classes (and into a data model) I can typically generalize my implementation into static and stateless classes -- leaving me with pure functionality fed by pure data (that's a dirty lie as I have never gotten to happy or usable state of total separation).
Concerning PHP, I strive to implement my functionality with a minimum of functions and particularly a minimum of subclassing. Typically the functions allow me to build objects and or array hierarchies at runtime; with a few conventions there is a callback interface into the created objects. This way the application structure is not tied directly to the object model but instead to the data model. I find it very satisfying to define an object configuration as data and see it realized at runtime. Happily, because its all data, I have the opportunity to process my model (both at 'debug time' and runtime) to ensure consistency and security before actually instantiating it. Perhaps because of my background or just a cruel twist of fate, I find it easier to repeat designs that are based on data rather than code.
The reason I mention all of this is that it occurs to me that the general processing pattern that evolves is still conceptually similar to MVC -- or at least an abstracted front-controller. A key difference, I think, is that the rules for processing the data are also themselves data. I suggest that here the static classes represent the (abstracted) controller which actually builds the model from the object definitions and also assigns the views which inturn are also determined by information in the data model. Would you say there is any merit in that comparison? As I said, I'm trying to copy good ideas, so if there is a connection here I'm hoping that it might lead to some brilliant insight for me so that I can improve my implementation framework
So far, I find that heavy use of this style of coding can lead to fatigue in terms of dealing with the abstraction layers because so much is done indirectly. Worse, sometimes things that should be insanely simple are insanely inelegant. I sometimes wonder if i should bother at all because it does require extra runtime processing or at least poses certain trade-offs. Currently I'm researching ways to leverage serialization to reduce runtime by restarting objects in specific, pre validated states--a quasi "soft compiled object"?
In the end, this style results in code that has much more in common with an abstracted modular design than it does OOP. Interestingly, once the code is fully implemented it rarely if ever needs changing as most updates can be done via data model changes. Because the functions are generalized and stateless, they are readily available to insert into other projects.
I admit I don't rely too heavily on these techniques my mainstay projects as I find that they have many inherent issues despite the advantadges I assign to them.
Sorry to go so far off topic of your article discussion and also to not provide any code samples! Oh, one fine day
Greetings. |
|
Back to top |
|
mohrt Administrator
Joined: 16 Apr 2003 Posts: 7368 Location: Lincoln Nebraska, USA
|
Posted: Tue Jun 10, 2003 2:59 pm Post subject: |
|
|
I've often thought about building a framework that works on the same principles as Smarty. Typically, the more abstraction you add to a framework the slower it becomes. So why not have the framework compile PHP code that is non-abstracted, ugly but blazing fast code?
To accompany the compiling idea, you can also utilize Apache tricks to simplify and improve things. Example:
http://my.domain.com/article/123abc
Notice the clean URL, no .php files, no query string arguments. We can accomplish this by setting up Apache to alias everything through a controller script:
Code: |
# httpd.conf settings, first map static content
AliasMatch /images/ /path/to/docs/images/
AliasMatch /static/ /path/to/docs/static/
# map everything else through the controller
AliasMatch ^ /path/to/docs/controller.php
|
Then in the controller you decide if /article/123abc has been "compiled" yet. If not, compile it, otherwise, execute the compiled code.
Another option to make things even quicker (something I've pondered for Smarty too) would be to skip the controller completely and directly execute the compiled code. If it has not been compiled yet, Apache does the chore of redirecting to the controller with an ErrorDocument handler:
http://my.domain.com/article/123abc.php
Code: |
# httpd.conf setting, redirect nonexisting files
ErrorDocument 404 /path/to/docs/controller.php
|
The controller would compile 123abc.php and execute it. Then the next hit would avoid the controller completely, saving the abstraction. You can still leave .php off of the URL with Apache Force-type tricks, for those that like super tidy URLs
Well, that should get some wheels turning...
Monte |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Jun 10, 2003 10:47 pm Post subject: |
|
|
Monte, get out of my head!!
I have some similar ideas in progress, though you always seem to go beyond what I have already thought about. For now I've been focusing on admin chores (like creating new environment hierarchies based on templates that include Apache .htaccess and httpd.conf code segments, default scripts, automatic resource mappings and library loading etc.
I was also looking at using SHM (and Pear::Stream_SHM) to keep relevant data in shared memory and pondering the parsing portions of Smarty re-visioned as a generic module that emitted bytecodes instead of source code.
I think that the upcoming User Streams support in PHP is going to impact Smarty quite a lot. In that regards, I think it is important that Smarty continue to modularize the parsing and compiling units so that they are completely separable from the rest of the codebase.
[EDIT: You probably wouldn't need the Smarty framework at all if PHP allowed for user modifiable syntax -- ie. if it exposed portions of the lexer. BUT PHP Stream filters do a similar thing--they allow you to modify the stream during access.]
I like the idea of Smarty as an application generator instead of Smarty as a template engine, but I daresay there is some heresay there.
I can't wait until I have another block of time so that I can consider your outline a little further! Thanks for the ideas!
Last edited by boots on Wed Aug 06, 2003 9:34 pm; edited 1 time in total |
|
Back to top |
|
sweatje Smarty Regular
Joined: 17 Apr 2003 Posts: 70 Location: Bettendorf, Iowa, USA
|
Posted: Fri Jun 13, 2003 3:45 am Post subject: |
|
|
mohrt wrote: | Well, that should get some wheels turning... |
More like mind bending! Left me speachless for days
Did you have some thoughts about what your abstract language would look like? Some kind of a superset of the current PHP syntax?
The first thing that came to my mind was one of my pet peeves: having to declare a variable for an iterator. In my "ideal" language there would be an iteration syntax that would work more like a javascript with () { } where it just knows what you are working on inside the curly braces. Is this sort of thing what you had in mind, or am I completly off base ![Rolling Eyes](images/smiles/icon_rolleyes.gif) _________________ Jason
jsweat_php AT yahoo DOT com |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Fri Jun 13, 2003 5:24 am Post subject: |
|
|
mohrt wrote: | Another option to make things even quicker (something I've pondered for Smarty too) would be to skip the controller completely and directly execute the compiled code. If it has not been compiled yet, Apache does the chore of redirecting to the controller with an ErrorDocument handler:
http://my.domain.com/article/123abc.php
Code: |
# httpd.conf setting, redirect nonexisting files
ErrorDocument 404 /path/to/docs/controller.php
|
The controller would compile 123abc.php and execute it. Then the next hit would avoid the controller completely, saving the abstraction. You can still leave .php off of the URL with Apache Force-type tricks, for those that like super tidy URLs
|
Modify Smarty to write the cache/compile file (as appropriate) of the outermost template file to the webroot with a small inline at the head that does an filemtime on its source templates? Or use rewrite mod to map requests directly into cached/compiled/generation dirs?
EDIT: A bit off-topic, has anyone checked out Moto? Its a macro language that compiles down to a Apache C module. I mention it as it seems to be a more radical form of optimization. |
|
Back to top |
|
ted Smarty n00b
Joined: 08 Jul 2003 Posts: 1
|
Posted: Tue Jul 08, 2003 10:27 pm Post subject: |
|
|
mohrt wrote: | I've often thought about building a framework that works on the same principles as Smarty. Typically, the more abstraction you add to a framework the slower it becomes. So why not have the framework compile PHP code that is non-abstracted, ugly but blazing fast code? |
I think that Smarty's code compiling paradigm won't fit easily atop a development framework.
The code that Smarty compiles doesn't have to do much -- it prints variables, loops through loops, and calls PHP functions. I think Smarty is able to do such a great job due to the simple requirements of the compiled code it produces.
The complexity involved in a smarty-like development framework is much greater, perhaps too great to be coded.
That notwithstanding, if someone can figure out a way to abstract GOF patterns in such a way that's extremely flexible yet can be compiled into fast non-abstracted PHP, well, it'd be really, really cool
Hey Monte - for an alternative to using the ErrorDocument directive, see http://www.engelschall.com/pw/apache/rewriteguide/#ToC33 |
|
Back to top |
|
messju Administrator
![](images/avatars/12071285133e9f19a697762.png)
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
Posted: Wed Aug 06, 2003 9:30 pm Post subject: |
|
|
for people who are interested in putting parts of the business-logic into stored procedures in the rdbms to the modell: there is plphp ( http://www.commandprompt.com/entry.lxp?lxpe=260 ) to write you stored procedures for postgresql in php. it looks very interesting to me. |
|
Back to top |
|
sweatje Smarty Regular
Joined: 17 Apr 2003 Posts: 70 Location: Bettendorf, Iowa, USA
|
Posted: Wed Aug 06, 2003 9:40 pm Post subject: |
|
|
I saw that too. I was curious that none of the examples showed any SQL interaction. You could see how a PHP developer could use it for familar syntax for doing formatting operations, but I will need to investigate more to see how tightly it is coupled with SQL itself. _________________ Jason
jsweat_php AT yahoo DOT com |
|
Back to top |
|
messju Administrator
![](images/avatars/12071285133e9f19a697762.png)
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
Posted: Wed Aug 06, 2003 9:44 pm Post subject: |
|
|
yes it looks like. one recent post on the plphp mailing list was:
"Currently plphp only supports function level stuff... eg you can't do
NEW/OLD record type stuff.
This is on the list to fix as we are also trying to fix it in plPerl."
it is very young overall. |
|
Back to top |
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|
|