- This topic is empty.
-
AuthorPosts
-
May 16, 2014 at 11:27 am #170402
shaneisme
ParticipantAbout a year ago I started down the road of building my first full-stack PHP site for learning purposes (but also a project at work).
I had a decent OO version up and running, but I wanted to do a full MVC reorganization since the codebase (even though it’s only about halfway done) is becoming difficult to maintain. I’ve learned a lot over the last year, but I’m still trying to figure out the templating / view portion.
My main stipulation is that I’ve got to write everything custom without the need for template engines – I will probably switch to a template engine in the future, I just need to learn how to do it.
Currently, my model and controller are talking back and forth very well and I’m getting raw data from the database without an issue.
I have a “master” template file that loads up the basics and styles around the content, as well as loading different modules if they’re into the “admin” section of the site.
I’ve read a couple tutorials on custom templating:
http://www.gabrielemittica.com/cont/guide/how-you-can-create-a-light-and-useful-template-engine-for-php/10/1.html
http://www.rasmuslarsson.se/2013/05/a-template-engine-in-php/I get how to pass info from my controller over to the view – say build a table with data from the DB. But what I don’t get is how to bring my master template into things.
Does anyone have any reading material to point me in the right direction? Or know of an open template engine that I can study? Am I making sense?
If you need more precise details or code examples just let me know what you need, I’m not sure what to provide exactly.
May 16, 2014 at 2:31 pm #170410__
ParticipantI use the master template as a “view controller,” of sorts. Once all of the data models are prepared, I have a method that brings them all into a local scope and then include the template (typically wrapped in an output buffer).
For example (oversimplified pseudocode):
controller.php:
<?php $page = new webpage( "/route/to/page" ); $table = new table( $data ); $article = new article( $content ); $model = array( 'page'=>$page,'table'=>$table,'article'=>$article ); $output = viewTemplate( ""path/to/template.php",$model ); print $output; exit;
viewTemplate function:
function viewTemplate( $template,array $model=[] ){ // bring data models into local scope extract( $model,EXTR_PREFIX_ALL,"__" ); ob_start(); require $template; return ob_get_clean(); }
template.php:
<!doctype html> <html> <head> <title><?= $__page->title ?></title> <meta charset=utf-8> </head> <body> <h1><?= $__page->title ?></h1> <?= $__article->asHTML() ?> <?= $__table->asHTML() ?> </body> </html>
May 16, 2014 at 2:47 pm #170412__
ParticipantAfter reading that, it seems basically similar to what I’m doing. Have you already tried this approach? did you run into problems with it?
I will probably switch to a template engine in the future
Honestly, I would discourage that. PHP is already a templating language (in fact, all of its “legitimate” programming capabilities are basically tacked on as an afterthought).
Add any special methods you find a need for, sure.
But a full-blown TL (e.g., Smarty) only adds execution time and a second learning curve.May 16, 2014 at 2:48 pm #170413shaneisme
ParticipantOK – I understand how that works on a high level. Let’s take it one deeper.
Say you had your
template.php
, but you wanted to bring in the HTML to build a table fromcompany_table.php
or something. So basically, a template within a template.At that point, would you just use the
viewTemplate
function again? If so, how would I insert thecompany_table.php
code which, say, has aforeach
loop to build up the table?May 16, 2014 at 2:50 pm #170414shaneisme
ParticipantAfter reading that, it seems basically similar to what I’m doing. Have you already tried this approach? did you run into problems with it?
I think I understand that example + yours, but I needed more functionality (I think, see post above), unless I’m not seeing it yet.
May 16, 2014 at 3:43 pm #170416__
ParticipantSo basically, a template within a template.
I have a “viewable” interface for data models that can be used inside templates. This not only makes certain that every model has its “viewAsHTML (JSON, whatever)” method, but also provides a framework where they can be nested.
Basically, the controller only need choose the top-level template. This template has no need to know how its data models find the data they need: they handle their own templating. An
article
may simply wrap its contents in<article>
tags, or it might be composed ofheader
,author
,body
,pullquote
,figure
, and/orfooter
objects, each responsible for their own templates.I might have a template directory set up more-or-less like so:
views/ general/ article.html table.html current-theme/ current-page/ article.html article.html
Each “viewable” model might look for their template (in order):
- in the current page + current theme directory
- in the current theme directory
- in the “general” directory
- I usually include a default template in the class itself as a fallback (though this is almost always the one that is actually used).
Under this design, using my example above:
$article
would use thecurrent-theme/current-page/article.html
template$table
would use thegeneral/table.html
template- if we added another model to the page (say,
$pageViewCounter
), it would use its “fallback” template.
Each “viewable” model returns its contents, as a string, to the viewable that called it. Eventually, it reaches the top-level template and becomes part of the finished HTML markup.
how would I insert the company_table.php code which, say, has a foreach loop to build up the table?
The specific logic of how to do display the data is handled by the particular model (or the model’s template). Because each component template is called from inside a class method, it will already have access to the model via
$this
. Atable
template, for example, could simply look like this:<?php foreach( $this->data as $tr ){ $tds = []; foreach( $tr as $td ){ $tds[] = "<td>$td</td>"; } $trs = "<tr>".implode( "",$tds )."</tr>"; } // remember, we are inside an output buffer print "<table>".implode( "",$trs )."</table>";
May 16, 2014 at 4:11 pm #170417shaneisme
ParticipantEach “viewable” model returns its contents, as a string, to the viewable that called it. Eventually, it reaches the top-level template and becomes part of the finished HTML markup.
OK – this is the part I think I’m having difficulty wrapping my head around.
My current config does the following:
1) Via front-controller, client builds controller based on URI
2) Controller talks to model to get data to build view client is requesting
3) Controller passes data to viewIn my understanding, this is a classical MVC setup (correct me if I’m wrong).
In your example, you’re going to have the master template being called by the controller and passing the data through to it. What step out of my simplified list above should all the compiling of different templates occur?
My original understanding would be that it occurs after the controller gets its data from the model and then the controller itself would call each template and put them together.
May 16, 2014 at 4:33 pm #170418__
ParticipantThe controller doesn’t have to do everything. Think about leadership in the real world: you figure out what needs to be done, and then tell your workers (who specialize in their individual tasks) to do this, or that. Good leaders don’t micromanage: you tell specialist X that you need a Y, and they make you one. You don’t make the specialist scoot over and follow the step-by-step Y instructions while you read them off one at a time.
The controller figures out what the user wants, makes sure everything needed is available, and then sets things in motion. Then it’s done: it gets to just sit back and wait until it is given the finished HTML, and print it.
Views should have access to the models directly. Instead of asking the Controller for a from X, b from X, and c from X, it just asks for X.
In other words, the Controller doesn’t get data from the Model and pass it the the View, it gets the Model itself and passes it to the View. Simple.
1) front-controller chooses Controller based on client’s request.
2) controller decides on a View and gathers required Models*.
3) controller passes the models to the view, which returns the rendered data.
4) controller prints the rendered view and exits.* note; this is only the models required bythe View: if those models are made from other models, it’s their own business and no one else needs to know.
May 16, 2014 at 4:42 pm #170419shaneisme
ParticipantOK… I’m going to think on this a while. I have another project I’m working on before I continue on this project, so I’ll check in later.
Thanks for your thoughts @traq, appreciate it.
More later.
May 16, 2014 at 4:48 pm #170421__
ParticipantNB; the “nesting” I am describing might be more correctly called “m (modular) MVC” or “H (hierarchical) MVC” —and, sure, whatever, I don’t care to be drawn into that debate— but the C in MVC was never meant to micromanage.
May 16, 2014 at 4:51 pm #170422__
ParticipantOK… I’m going to think on this a while. …appreciate it.
No problem. I’m in the process of rewriting my framework, so I don’t have much finished code to share. It took me a while to nail all this down, and my last version had a few wrong turns in it (which is a big reason behind the rewrite).
August 8, 2014 at 3:12 pm #178350shaneisme
ParticipantI’m just now getting back into my project, but either A) I’ve forgotten something, or B) something broke while I was working on other things…
Please school me if necessary.
In my core controller, I’m setting a PDO DB object and saving it under a public variable
$db
.In the children controllers, I extend the core controller. However,
$this->db
comes upNULL
in the child now. I could have swore that was working when I left it…Very basic example just to illustrate what I mean:
controller\core:
`
namespace controller;
use PDO;class core {
protected $variable = null;public function __construct() {
$this->variable = 1;
}}
`
controller\example\test:
`
namespace controller\example;
use controller\core as Controller;class test extends Controller {
public function __construct() {
var_dump($this->db);
}}
`
I expect thevar_dump
to return1
, however it’s returningNULL
.What’s wrong?
P.S. – sorry for the formatting, I can’t get it working for some reason.
August 8, 2014 at 4:42 pm #178355shaneisme
ParticipantAh hah, ok… that’s what I missed.
Now I’ve got some sort of database connection loop going on which I’m fairly certain I can debug.
Thanks!
September 16, 2014 at 1:51 pm #183294shaneisme
ParticipantQuestion – if I had hard-coded HTML emails, I should store this among my model correct?
I was almost going to just put them in my template folder… but then I thought this is technically data to be fetched with some variables set inside the controller… like I would a SQL call.
September 16, 2014 at 2:30 pm #183305shaneisme
ParticipantAt this point in the project, my model consists mostly of SQL calls which accept parameters and variables from the controller.
The controller handles user inputs, sends them to the model and when it has what it needs it passes everything to the view. The view will return HTML given the parameters given to it by the controller.
First, I’m thinking about that correctly?
If that’s true, then setting it up as a view (and pulling reusable HTML templates) and then emailing that final HTML is basically the same thing, from what you’re saying, as outputting to a browser.
If all that sounds right, I know what I need to do!
-
AuthorPosts
- The forum ‘Back End’ is closed to new topics and replies.