Forum Moderators: coopster

Message Too Old, No Replies

Whats the best way to output data from a database using php?

Example... Classes vs includes vs whatever else

         

megaman732

8:10 pm on Aug 14, 2009 (gmt 0)

10+ Year Member



Im relatively new to php, and im working on a project using a mysql database. The project consists of users being able to write posts, which are then shown in a list format. The problem is, the posts are shown in different locations on the site, like the index (main) page, and the users profile page. Similar to twitter if your confused. My question is, what is the best way to display the posts? currently I'm using a class that i created. It has functions to retrieve posts from the database, save them all in a multidimensional array. Then another function in the class formats the entire list using foreach, and then returns the formatted HTML list of posts. All i have to do is echo what is returned. But i read somewhere that its bad practice to write functions (especially class functions) that output html. What would be the best way to do this, without having to rewrite the same code on every page the posts are shown. Is it really bad practice to use html in functions?

Example...
a profile page looks something like this.

<?php
require('class.php');
require('header.php');

$profile = new Profile();
$userProfile = $profile->GetUserProfile($userID);
echo $userProfile;

$class = new Posts();
$posts = $class->GetUserPosts($userID);
echo $posts;

require('footer.php');
?>

And the main page looks something like this

<?php

$class = new Posts();
$posts = $class->GetAllPosts();
echo $posts;

?>
where the profile class would take a user id and output the users profile, already formatted in html.
And the posts class has functions to return a determined number of posts already formatted in an html list
Thanks

innoscape

11:48 am on Aug 17, 2009 (gmt 0)

10+ Year Member



I am far from a developer but, after 15 years of making sites for friends, relatives and a few small businesses that needed help I have learned how to get by. Here is what I do.

Try making "templates". Call the templates under specific conditions. They are pre populated with whatever you require. (eg. where user=someuser if have posts get posts how many sort asc)

Sounds like your requirements are similar to what WordPress offers. You can call users posts anywhere with a single line. Hit the support forums there for more info. May want to give it a look see. "wordpress.org". You can have it up and running in less than five minutes, and it is free. I use it as a cms for many sites right now on my shared server. Saves A LOT of time and easy to customize. There is even a user profile plugin that is easy to mod to suit your needs. You can also post/edit remotely using windows live writer or via email.

Just a suggestion.

As far as using html in functions, I see no issues if the functions can only be called via your script. WordPress uses functions in the templates as well. I use them frequently to create menus and for "switches".

You may be able to get "inspiration" from snippets of code from WordPress to use in your app as well.

Hope my reply helps a little..

CyBerAliEn

5:56 pm on Aug 17, 2009 (gmt 0)

10+ Year Member



I think your approach is fairly solid!

First off... the idea of outputting HTML is "bad" is false. At some point, you need PHP to output HTML. You have a good approach, where your functions "handle" data. But what I would recommend is maybe just creating some helper functions to handle your output... ie:


function buildPosts()
{
$class = new Posts();
$posts = $class->GetUserPosts($userID);
echo $posts;
}

Then in your HTML file (or such), all you gotta do is:

<?php buildPosts(); ?>

Where you want your posts to appear. But that is my opinion.

As noted above... the idea of "templating" your design is always a great idea. Say you have tons of pages such as profiles, posts, members, groups, etc etc (example). And they all look like this:


<?php
require('class.php');
require('header.php');

$profile = new Profile();
$userProfile = $profile->GetUserProfile($userID);
echo $userProfile;

$class = new Posts();
$posts = $class->GetUserPosts($userID);
echo $posts;

require('footer.php');
?>

You repeat a lot of the same code (ie, including the header/footer/etc). After years of design and development you'll eventually loath repeating the same chunk of crap, because one day you'll need to update it, and when it is spread over multiple files, multiple time, multiple places... it is a pain to manage.

Though your approach of putting the header/etc all in their own files/functions is a great approach! You should also consider creating a "template" or "shell" page where all that is setup for you, and then the "content" area is filled in dynamically.

Example:

template.php

<?php
require('class.php');
require('header.php');
/*Start of Content Area*/
if (file_exists($thisFile)) { include("{$thisFile}"); }
else { echo "<p>Page not found.</p>"; }
/*End of Content Area*/
require('footer.php');
?>

Then just setup your code to include the template and set the 'thisFile'. How you do this part, is a bit trickier.

What I've learned to do:
Setup URL rewriting on the server and have it pass all requests (for files that do not exist) to a single PHP script (ie: '/go.php'). This file then analyzes the request URI to determine what to do. It then sets up whatever files I need by simply including files as needed. Look into URL rewriting, it is pretty powerful when you combine it with PHP in terms of structuring your site and files!

Note that it is a lot of work to setup; but can ultimately save you TONS of time when it comes to managing or making a simple content-based, or even dynamic, website.

andrewsmd

8:12 pm on Aug 17, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



CyBerAliEn is right. Having php output html is not "bad" it could just create a headache later down the road when you have to update something, say adding a <br> in a spot you have that outputted hundreds of times throughout your website. You have to edit every single page. Most of the time you will miss some and then you end up with inconsistencies in your website. Use templates, either smarty or the IT.php file that comes with pear. It's in the HTML/Template folder of your ext I think. If you want some sample code the the templating system for it.php I can post some.

megaman732

9:51 pm on Aug 17, 2009 (gmt 0)

10+ Year Member



@andrewsmd, a sample would be great, thank you.

Also, I read about having separate template files, using while, for, and foreach loops. i also heard of using placeholders within the template and then str_replace to replace the placeholders with the actual data. I dont mean to be needy, but if somebody could post an example of how this would be done using foreach, and the best way to do it. Like, where would I do the str_replace, within each loop, or afterwards?

andrewsmd

10:10 pm on Aug 17, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I emailed you an example. Let me know if you have any questions.

megaman732

6:48 am on Aug 19, 2009 (gmt 0)

10+ Year Member



thanks a lot for the detailed example you emailed me andrewsmd, it was truly helpful. I am a tad bit confused as to whats going on behind the scenes in your template class. It seems like yours is way more complex than mines.
Mine is simply this

class Template{

function Display($file, $info = array()){
ob_start();
include($file);
$output = ob_get_clean();
return $output;
}

}

I'm also a little confused as to whats going on within your template files. currently, I have all of mine split up in different parts like this...

user_profile.php
<div>
<h2><?php echo $info['name']; ?></h2>
<p><?php echo $info['description']; ?></h2>
</div>

user_articles.php
<?php foreach($info as $post){ ?>
<div>
<h2><?php echo $post['title']; ?></h2>
<p><?php echo $post['body']; ?></h2>
</div>
<?php } ?>

and then my actual page might look something like this...

require('header.php');
require('template.class.php');

$user_id = isset($_GET['uid']) ? $_GET['uid'] : 0;
$user_info = Get_User_Info($user_id);
$user_articles = Get_User_Articles($user_id);

$template = new Template();
$user_profile = $template->Display('user_profile.php', $user_info);
$user_articles = $template->Display('user_articles.php', $user_articles);

echo $user_profile;
echo $user_articles;

require('footer.php');

I saw in your example you didnt include your header, or footer, and you used the show() function only once. Am i doing something wrong, or rather is there a better way i could do this. I have a feeling that your class builds the template on the fly, instead of simply getting and displaying the template.

I know CyBerAlien mentioned URL rewriting and also mentioned using templates and URL rewriting, which I think I've heard of and dabbled in when I created an Error class to handle 404 errors, but how would that help with templates exactly, i dont see the connection

andrewsmd

2:04 pm on Aug 19, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



You only use the show function once no matter what. If you want to add or remove things from the page they have to be fired on a server side action (i.e. a JS postback or form submit). I wondered if you would have trouble with that. I do a lot with ajax and hidden divs in that page to make it look like you're never posting back. If you want to see what that production page looks like I'll email you a link. Here is a much simpler example to help you out.

Call this test.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<body>
<form name="myForm" method="post">
<!-- You only show() things once. To show in different spots you have to use the
begin and end blocks like so. Then you add a variable -->
<!-- BEGIN Block1 -->
{initialMessage}{msg1}<br />{msg2}<br />
<!-- END Block1 -->
Please click one<br />
<input type="submit" name="messageOne" value="Set Message One" /><br />
<input type="submit" name="messageTwo" value="Set Message Two" /><br /><br /><br />

<!-- Here is a little more complex example -->
<!-- BEGIN table -->
<table>
<tr>
<td>Row 1 column 1
</td>
<td>Row 1 column 2
</td>
</tr>
<tr>
<td>Row 2 column 1
</td>
<td>Row 2 column 2
</td>
</tr>
<tr>
<td>{thirdRow}
</td>
<td>Row 3 column 2
</td>
</tr>
<!-- Here we are just adding another row to the table and not the whole thing -->
<!-- BEGIN newRow -->
<tr>
<td>{tableMessage}
</td>
<td>{secondTableMessage}
</td>
</tr>
<!-- END newRow -->
</table>
<!-- END table -->
<input type="submit" name="table" value="Add the Table" /><br />
<input type="submit" name="addRow" value="Add a row to the table" /><br />
<input type="submit" name="reset" value="Reset the table" /><br />
</form>
</body>
</html>

And call this test.php
<?php
require_once("HTML/Template/IT.php");
$template = new HTML_Template_IT("./");
$template->loadTemplatefile("test.html");

//start a session just read through to see why
session_start();

//initialize this if it hasn't already been set
if(!(isset($_SESSION['counter']))){
$_SESSION['counter'] = 1;
}//if !isset

//first off we have to set the initial message
//notice I set it to nothing. You don't have to do that
//its just that you have to set at least one variable
//to load the page. So if your page looks correct on the load
//then just set one to nothing. If you want to load data on the page
//load from the db then just set your information in it
$template->setCurrentBlock("Block1");
$template->setVariable("initialMessage", "");
$template->parseCurrentBlock();

//if they click the message one button
//then we will set message one
if(isset($_POST["messageOne"])){

$template->setCurrentBlock("Block1");
$template->setVariable("msg1", "You clicked message one.");
$template->parseCurrentBlock();

}//if isset messageOne

//if they click the message two button
//then we will set message two
if(isset($_POST["messageTwo"])){

$template->setCurrentBlock("Block1");
$template->setVariable("msg2", "You clicked message two.");
$template->parseCurrentBlock();

}//if isset messageOne

//if they clicked add a table we will add it
if(isset($_POST['table'])){

$template->setCurrentBlock("table");
$template->setVariable("thirdRow", "Row 3 Column 1");
$template->parseCurrentBlock();

}//if isset table

//if they clicked add a row we will keep track
//of how many times they have clicked it with a
//session varaible to add all of the rows
//you will notice in the table that the
//third row first column is missing becase
//we don't set that but since this block
//is nested in the table block it still sets
//that whole block
if(isset($_POST['addRow'])){

$tempCount = 1;

while($tempCount <= $_SESSION['counter']){

$rowCount = $tempCount + 3;
$template->setCurrentBlock("newRow");
$template->setVariable("tableMessage", "Row $rowCount Column 1");
$template->setVariable("secondTableMessage", "Row $rowCount Column 2");
$template->parseCurrentBlock();

$tempCount++;
}//while

//add one to the counter
$_SESSION['counter'] = $_SESSION['counter'] + 1;

}//if isset addRow

if(isset($_POST['reset'])){

$_SESSION['counter'] = 1;

//load the original table
$template->setCurrentBlock("table");
$template->setVariable("thirdRow", "Row 3 Column 1");
$template->parseCurrentBlock();

}//if isset reset

//notice we only put this once
$template->show();
?>

megaman732

5:36 pm on Aug 19, 2009 (gmt 0)

10+ Year Member



Oh...So i'm guessing you're using placeholders in your template, and then possibly a str_replace after you setVariable. Sounds good. Thanks once again for the help, I appreciate it.

Another question I have is about the forms. When I first started learning PHP, I read it was best to simply use the same page to display and handle the form. I recently read that Its best to separate the two (and any other piece of html from php code), but I can't seem to understand why that would be the best for a form. It seems that just handling the code in PHP above, closing the php braces, and then displaying the html form would be best. Any ideas (and small examples) would help. thanks again to andrewsmd

andrewsmd

5:55 pm on Aug 19, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



The only reason you would want to separate php from html is because it makes it easier to debug. Let's say that you are using session variables to remember what a user types in a textbox after they click submit. If you have it all in one line it looks like this
<input type="text" name="someVar" value="<?php echo($_SESSION['someVar']); ?>" />
It just makes it hard to debug. Using templates the same line looks like
<input type="text" name="someVar" value="{value}" />
Now if you had a lot of these then when you populate them with php, it's all in the same spot of code instead of something like
<input type="text" name="someVar" value="<?php echo($_SESSION['someVar']); ?>" />
<input type="text" name="someVar" value="<?php echo($_SESSION['someVar']); ?>" />
<input type="text" name="someVar" value="<?php echo($_SESSION['someVar']); ?>" />. If your page or site is going to grow, it could cause debugging nightmares later on. However, if your page is relatively static and is not going to change much then go ahead and keep them in the same page. I would recommend trying to keep them separate though, this is coming from experience. The things is, it may not seem that hard to read to you now, as your developing, but wait a month and try to read through it again. What you have read about keeping everything separate was probably written by the same kind of programmers who think you have to create objects and classes for everything. There are just some times when there is no need for it. You just need to decide what you like best. The problem is you are new, and we are just trying to help you avoid some problems that you only discover through experience.

megaman732

5:52 pm on Aug 20, 2009 (gmt 0)

10+ Year Member



I understand what your saying...

Lets say i have a class like so...

class Template{

$private $output = '';

public function Load_Template($template){
ob_start();
include($template);
$this->output = ob_get_clean();
}

public function Replace($data){
$this->output = str_replace(array_keys($data), array_values($data), $this->output);
}
public function Display($add_footer = true){
echo $this->output;
}
}

This would work for a simple Template like...

<div>{username}</div>

But what would be the best way to do it with loops in my template. Lets say something like

<ul>
<li>{username}</li>//Loop this line for each user
</ul>

Im working on a Replace function that will replace my placeholders with their values, which would work just fine with regular templates, but im comfused about how I would do that with loops.... As usual, thanks for even reading

andrewsmd

8:43 pm on Aug 20, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



If you have the templating system working then something like this.

Here is the html

<!-- BEGIN loopBlock -->
<ul>
<li>{loopMessage}</li>
</ul>
<!-- END loopBlock -->

and here is the php
//i just did a simple forr loop
//however, wherever you get your username
//loop blocks just put this code in that loop
//instead of my static for
$template->setCurrentBlock("loopBlock");

for($i = 0; $i < 10; $i++){

$template->setVariable("loopMessage", "Item $i");
$template->parseCurrentBlock();
}//for

Hope that helps let me know if you need anything else. If you paste the loop of how you get your usernames I could help you out more.

megaman732

9:25 pm on Aug 20, 2009 (gmt 0)

10+ Year Member



I guess whats confusing me personally is how exactly your parsing the <!--BEGIN--> and <!--END--> loops within your template class...
I have a class method that takes a zip code and range and returns all users within that range.

So $this->Get_Local_Users('08901', 5); will return something like this

$users = array(0 => array('name' => 'john', 'age' => 12, 'zip' => '08873'), 1 => array('name' => 'billy', 'age' => 15, 'zip' => '08854'));

you get the point right? the arrays look like this...

$users[0]['name'] == john
$users[0]['age'] == 12
$users[0]['zip'] == 08873
$users[1]['name'] == billy

so, what i have right now is this

<ul>
<?php foreach ($users as $user): ?>
<li><?= $user['name']; ?> - <?= $user['age']; ?></li>
<?php endforeach; ?>
</ul>

you mentioned that this can get real messy, but I dont understand how I would be able to do it "easily" any other way. I looked at a few template engines, and I saw that smarty creates there own language to parse the templates, and savant just uses php as its own template language (just as i did above).

If I could understand how to handle loops, I could easily just use placeholders like {username} within my templates.

Thanks again

andrewsmd

9:40 pm on Aug 20, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Just use exactly what I posted. Here is an example. Using the same html I posted earlier
$users = array(0 => array('name' => 'john', 'age' => 12, 'zip' => '08873'), 1 => array('name' => 'billy', 'age' => 15, 'zip' => '08854'),
2 => array('name' => 'Bill', 'age' => 12, 'zip' => '08873'), 3 => array('name' => 'Joe', 'age' => 15, 'zip' => '08854'));
$template->setCurrentBlock("loopBlock");

foreach($users as $user){

$template->setVariable("loopMessage", "Name: {$user['name']} - Age: {$user['age']} - Zip: {$user['zip']} ");
$template->parseCurrentBlock();
}//for

megaman732

9:54 pm on Aug 20, 2009 (gmt 0)

10+ Year Member



I get everything your saying.....but what i dont get is what exactly the setCurrentBlock, setVariable, and other member functions are doing. Thats whats confusing me. I understand completely what your doing, but I'm not getting how its working. Are you using a seperate template engine, or is this your own engine, becuase I dont know whats happening within the class.

For example, I've seen people mention doing this with their templates

<h1>{name}</h1>

and then something like this to fill in the {name} placeholder.

$data['{name}'] = 'Michael';

$output = str_replace(array_keys($data), array_values($data), $output);

Then the final output would read

<h1>Michael</h1>

But, I dont get how to do this within loops, and I'm not understanding whats happening within your class functions, unless your using a 3rd party template engine?

megaman732

10:24 pm on Aug 20, 2009 (gmt 0)

10+ Year Member



Heres my template class...

class Template{

$private $output = '';

public function Load_Template($template){
ob_start();
include($template);
$this->output = ob_get_clean();
}

public function Replace($data){
$this->output = str_replace(array_keys($data), array_values($data), $this->output);
}
public function Display($add_footer = true){
echo $this->output;
}
}

Heres my user profile template. user_profile.php...

<div>
<h1>{name}</h1>
<ul>
<li>{age}</li>
<li>{zip}</li>
</ul>
</div>

Heres how I call it...

$user = array('{name}' => 'john', '{age}' => 12, '{zip}' => '08873');

$template = new Template;
$template->Load_Template('user_profile.php');
$template->Replace($user);
$template->Display();

Everything above i understand completely, but I dont really know how I would do this using loops, unless I did something like....

user_list.php

<li>
<h1>{name}</h1>
<b>{age}</b>
<b>{zip}</b>
</li>

call it like this...

$users = array(0 => array('name' => 'john', 'age' => 12, 'zip' => '08873'), 1 => array('name' => 'billy', 'age' => 15, 'zip' => '08854'),
2 => array('name' => 'Bill', 'age' => 12, 'zip' => '08873'), 3 => array('name' => 'Joe', 'age' => 15, 'zip' => '08854'));

echo '<ul>';

foreach($users as $user){
$template->Load_Template('user_template.php');
$template->Replace($user);
$template->Display();
}

echo '</ul>';

I just dont understand what exactly is happening within your template engine... If you've already answered this question and I just missed it, I apologize for wasting your time.

andrewsmd

4:18 am on Aug 21, 2009 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm using the template class that comes installed with pear. I didn't build it myself. If you go to your php folder then the pear folder there should be a folder called html then a folder called template and the IT.php file is there. If you are using a shared server see if you have pear installed. If so do a google on the IT.php file. You should be able to download it and it requires pear to work. Then you have to reference it. Look at the email I sent you. You will notice a require_once("Html/template/it.php"); that is the template file I am using. Sorry for not being more clear. Post back if you need any more help. You're not wasting my time. I was in your position once. Don't feel bad, we will get it figure out.

megaman732

1:07 pm on Aug 21, 2009 (gmt 0)

10+ Year Member



Thanks a lot... It all makes sense now. I'll be sure to ask you if I have any questions later