...

View Full Version : Running a loop through an array



thesavior
03-23-2007, 06:48 AM
Okay, heres my issue.

In my template, I have the code:



{nav_item:start}
<li>{nav_item:name}</li>
{nav_item:end}

Which is being sent to my code as:

nav_item("start");
nav_item("name");
nav_item("end");

What I want to do, is make a nav_item function that recognizes the start and end as a loop.

So in my function I can have an array:



$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);


And have my function return as its final product:



<li><a href="http://www.mydomain.com">Home</a></li>
<li><a href="http://www.mydomain.com/forums">Forums</a></li>


How do I go about parsing this loop? I know that I wasnt completely clear in describing this, so if you don't understand something, please ask.

iLLin
03-23-2007, 08:01 AM
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);

foreach($navlinks as $key => $value) {
$html .= '<li><a href="'.$value.'">'.$key.'</a></li>';
}

$assign_your_template("nav", $html);



Not sure what templating system your using but this is one method through php code. If you using smarty you can loop through arrays like so.



$navlinks = array(
array("name" => "Home", "link" => "http://www.mydomain.com"),
array("name" => "Forums", "link" => "http://www.mydomain.com/forums")
);

$smarty->assign("nav", $navlinks);





{section name=menu loop=$nav}
{strip}
<li><a href="{$nav[menu].link}">{$nav[menu].name}</a></li>
{/strip}
{/section}

thesavior
03-23-2007, 03:57 PM
I am not using smarty, Its something custom, I need some way to make that loop through there with the template code I already have. I know its going to be more difficult, but thats what I need to happen, now how do I go about doing that?

GJay
03-23-2007, 05:21 PM
you can't really do that, I think you're going to have to do it in a different way.

aedrin
03-23-2007, 06:15 PM
you can't really do that, I think you're going to have to do it in a different way.

What you mean is, it is possible. But it's hard.

The way Smarty works is it takes Smarty codes and transforms them to PHP.

This allows them to do fancier stuff like that.

If you're executing code as you parse it, I think you'll find that it will be a lot harder.

What you could possibly do. ob_start() allows nesting. So, once you see nav_item:start, start another ob_start layer. Then once you find nav_item:end, collect the data from the output buffer. Parse out your template code from the output you collected. Then do a simple foreach and you're done.

Actually, that's really easy. :P

EDIT: This is a good idea for my own templates ;)

iLLin
03-23-2007, 06:16 PM
Cant really help without knowing how the template engine acts. Since you want it in the template. You will have to do through php and just add in the menu as a variable to the template. Or Create a separate menu.tpl and just loop through that in php and then put all the output in one variable for display. Just a thought to keep code/display separate.

aedrin
03-23-2007, 06:19 PM
Just a thought to keep code/display separate.

This is the problem that I find often, people want to separate code and HTML 100%. This is not logical. There's no reason for it.

What you want to separate is business logic. Database queries, calculations. You want to generate the data in 1 file. Then display it in another. Hence Model and View. HTML should never pass through the Controller, and data is not flat, so you have to generate the HTML through code in the template.

iLLin
03-23-2007, 06:29 PM
This is the problem that I find often, people want to separate code and HTML 100%. This is not logical. There's no reason for it.

What you want to separate is business logic. Database queries, calculations. You want to generate the data in 1 file. Then display it in another. Hence Model and View. HTML should never pass through the Controller, and data is not flat, so you have to generate the HTML through code in the template.

Yea I agree to a point, sometimes its more trouble to figure out a way to do this when you can just put in php and be done in 2mins. That is what I used to do and it gets messy after a while. I then started using smarty and keeping everything separate, sure you have to get creative at times like it seems he has to do, but I think its worth it and much more cleaner. Now obviously there will be things you really can't do and using templates with the nice { } your still using "code" to a point. So I agree with the "idea" keeping things separate, but know you can't do this a 100% of the time. To each his own :)

thesavior
03-24-2007, 01:46 AM
What you could possibly do. ob_start() allows nesting. So, once you see nav_item:start, start another ob_start layer. Then once you find nav_item:end, collect the data from the output buffer. Parse out your template code from the output you collected. Then do a simple foreach and you're done.


That sounds great in theory, but how would that actually work?

The way my template thing is working for the navbar, is that between the start and end should be repeated for every instance in the array.

maybe something in theory, dont know the code for it, but something like:



{nav_item:start:}
<li>{nav_item:name:}</li>
{nav_item:end:}

when nav_item("start"); is called, it does a preg match to find everything between the {nav_item:start:} the {nav_item:end:} Then the function strips out everything before the start and everything after the end lines and loops the array through the rest. But how would you go about writing this actually?

GJay
03-24-2007, 02:52 AM
why not just have a 'navitems()' function in your template that displays the navigation? You're making it awfully complicated.

thesavior
03-24-2007, 09:19 AM
okay, how can I do it to make it less complicated, but keep the basic structure the same?

thesavior
03-25-2007, 08:45 PM
*bump*

The template file tells the function how to lay out the navigation, the function just fills it. That is what Im trying to do, so I need a code or something that will run through the array and populate the template.

iLLin
03-25-2007, 09:25 PM
Paste up how you handle templates. IE how you define variables and such and then output to the template

thesavior
03-25-2007, 10:34 PM
This is my template parsing class:



class tpl_parse
{
public $superclass; // Superclass usage in this func
public $tplname; // What template to use
public $pagename; //What page to load
public $tpl; //Stores template info.
public $variable = array(); // Stores information about the variable in use

function run()
{
$this->tpl = file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/pages/".$this->pagename.".tpl"); // Load the tpl file into the variable
while(preg_match("/{(.*)\:(.*)\:\}/", $this->tpl, $this->variable)) // Search for variables
// Call function $this->variable[1] with attribute $this->variable[2]
{
$func_name=$this->variable[1];
if (method_exists($this, $func_name))
{
$this->$func_name($this->variable[2]);
}
else
{
$this->superclass->err->errno = 3;
$this->superclass->err->throwerr();
}
}
echo $this->tpl;
}
function file_include($tplfile)
{
$pos = strpos($tplfile, ".."); // Make sure people dont go up directories
if ($pos === false) // Does this string exist?
{
$this->tpl = str_replace($this->variable[0],file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/".$tplfile.".tpl"), $this->tpl); // Include another tpl
}
else
{
$this->superclass->err->errno = 2;
$this->superclass->err->throwerr();
}
}
function config($configcall)
{
$this->tpl = str_replace($this->variable[0], $this->superclass->config[$configcall], $this->tpl); // Include another tpl
}
function lang($langvar)
{
$this->tpl = str_replace($this->variable[0], $this->superclass->lang->text[$langvar], $this->tpl); // Include another tpl
}
function nav_item($var)
{
$this->tpl = str_replace($this->variable[0], "", $this->tpl);
}
}

called like this:



$superclass->tpl->tplname = "default";
$superclass->tpl->pagename = "index";
$superclass->tpl->run();


index.tpl looks like this:



{file_include:header:}
{file_include:top_search:}
{file_include:forum_group:}
{file_include:stats:}
{file_include:footer:}

each of which are parsed by the file_include() function in my class.

anything else you need?

iLLin
03-25-2007, 10:48 PM
How are you assigning variables? Only with functions?

thesavior
03-26-2007, 01:07 AM
I think so, Im not exactly sure what your question is asking.

iLLin
03-26-2007, 01:20 AM
Ok for example:

$assign("test", "This is a test");

now in your template, you have:

{test}

Which will output:

This is a test

Thats what I mean by how are you assigning your variables/values.

thesavior
03-26-2007, 01:43 AM
i dont need to do anything like that.

The only time something like that is needed is the language function which is already working.

In my template calls for include kind of things:

{file_include:header:}

which is telling the function file_include to include header.tpl

Does this answer your question?

iLLin
03-26-2007, 01:56 AM
This is stupid but try this:



function nav_item()
{
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);

foreach($navlinks as $key => $value) {
$html .= '<li><a href="'.$value.'">'.$key.'</a></li>';
}


$this->tpl = str_replace($this->variable[0], $html, $this->tpl);
}

thesavior
03-26-2007, 03:17 AM
that wont solve my issue because the point is to have the html defined in the template files, not in the php code. That is the issue.

Im changing my template file to be like this:



{nav_item:start:}
<li><a href="{nav_item:url:}">{nav_item:name:}</a></li>
{nav_item:end:}


So this should be easier. Im still stuck on how to actually parse this.

Inigoesdr
03-26-2007, 04:12 AM
Combining the HTML and template code like that is just going to cause problems. You should have a syntax for your template code to follow and generate the HTML with the class when it's parsed. I would do something like this:
{nav_item:start:}
{nav_item:Home:http://url.com}
{nav_item:Contact:http://url.com/contact/}
{nav_item:end:}
Or omit "nav_item:" altogether.
You could also use something like this:

{nav:start}
{nav_item:http://url.com}Home :: Site{/nav_item}
{nav:end}

thesavior
03-26-2007, 05:57 AM
I cant do that either because the items are going to be dynamic from the database. i have to do it the way I originally planned, because that is the only way I see to be able to work. I know its going to be messy, but it has to be done.

I dont know the code for each of these steps, but I know what the steps are:

1) on nav_item("start"), find all text between {nav_item:start:} and {nav_item:end:}.

2) foreach in the array, replace {nav_item:name:} in the text found above with <a href="urlfromarray">namefromarray</a>

3) replacing all of the text in #1 from {nav_item:start:} to {nav_item:end:} with the code created in number 2.


the array being:



$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);


something like:



function nav_item($var)
{
if ($var == "start")
{
preg_match("someregexhere", $this->tpl, $navtext);
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);
foreach($navlinks as $key => $value)
{
$html[] .= '<a href="'.$value.'">'.$key.'</a>';
}
foreach($html as $navpush)
{
$navfinal .= str_replace($this->variable[1], $navpush, $this->tpl);
}
}
$this->tpl = str_replace($this->variable[0], $navfinal, $this->tpl);
}


There are tons of things in the above code, but at least its a start. The issues I see are the regex, and iterating the links through the tpl variable.

iLLin
03-26-2007, 04:39 PM
Well you can either make this complicated and do array_push and try to start your code over in your template thus creating a loop within your template. Or you can build your menu in php and just output the final HTML or you can cheat and setup a menu.tpl and do this:



//menu.tpl
<li><a href="{nav_item:url}">{nav_item:name}</a></li>
//end menu.tpl


Then setup your class file like so.



class tpl_parse
{
public $superclass; // Superclass usage in this func
public $tplname; // What template to use
public $pagename; //What page to load
public $tpl; //Stores template info.
public $variable = array(); // Stores information about the variable in use
public $url; //added for nav
public $name; //added for nav

function run()
{
$this->tpl = file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/pages/".$this->pagename.".tpl"); // Load the tpl file into the variable
while(preg_match("/{(.*)\:(.*)\:\}/", $this->tpl, $this->variable)) // Search for variables
// Call function $this->variable[1] with attribute $this->variable[2]
{
$func_name=$this->variable[1];
if (method_exists($this, $func_name))
{
$this->$func_name($this->variable[2]);
}
else
{
$this->superclass->err->errno = 3;
$this->superclass->err->throwerr();
}
}
echo $this->tpl;
}
function file_include($tplfile)
{
$pos = strpos($tplfile, ".."); // Make sure people dont go up directories
if ($pos === false) // Does this string exist?
{
$this->tpl = str_replace($this->variable[0],file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/".$tplfile.".tpl"), $this->tpl); // Include another tpl
}
else
{
$this->superclass->err->errno = 2;
$this->superclass->err->throwerr();
}
}
function config($configcall)
{
$this->tpl = str_replace($this->variable[0], $this->superclass->config[$configcall], $this->tpl); // Include another tpl
}
function lang($langvar)
{
$this->tpl = str_replace($this->variable[0], $this->superclass->lang->text[$langvar], $this->tpl); // Include another tpl
}

function navigation($tplfile)
{
//get navigation values from db put in array
$nav_array = ("home" => "url", "test" => "test_url");
foreach($nav_array as $key => $value) {
$this->tpl = file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/".$tplfile.".tpl"); // Include nav tpl
while(preg_match("/{(.*)\:(.*)\:\}/", $this->tpl, $this->variable)) // Search for variables
// Call function $this->variable[1] with attribute $this->variable[2]
{
if($this->variable[2] == "url") $replace = $this->url;
if($this->variable[2] == "name") $replace = $this->name;

$this->tpl = str_replace($this->variable[0], $replace, $this->tpl);
}
echo $this->tpl;
}
}
}


Then call in your template file like so.



{navigation:nav_tpl:}


Have no idea if that will work or not but should get you thinking a little bit more.

aedrin
03-26-2007, 05:32 PM
That sounds great in theory, but how would that actually work?

Usually this can be said when a theory is given. But I gave the actual method of doing it.

I understand what you're trying to do, and I think it is the most elegant method.

I won't code this for you, as it wouldn't help you.


What you could possibly do. ob_start() allows nesting. So, once you see nav_item:start, start another ob_start layer. Then once you find nav_item:end, collect the data from the output buffer. Parse out your template code from the output you collected. Then do a simple foreach and you're done.

It can actually be simpler.

You said:


{nav_item:start:}
<li><a href="{nav_item:url:}">{nav_item:name:}</a></li>
{nav_item:end:}


My suggestion:



{navigation}
<li><a href="{nav_item:url:}">{nav_item:name:}</a></li>
{/navigation}





regex for {navigation} ... content ... {/navigation}
for each Link in database result
content = regex match
replace template codes in content with that for current Link
echo content
end for


Quite easy.

thesavior
03-27-2007, 01:08 AM
argh, that makes sense but im so lost on where to go with that. I know that you dont want to provide me with everything because otherwise I wont learn, but I need something to get me going. Especially the regex, cause that is the hardest for me. I thought the regex would be:

\{nav_item:start:\}(.*)\{nav_item:end:\}

but that doesn't work.

I'm really trying hard to not act ungrateful, because I really am glad and happy with the responses ive been getting. Everything that you guys say gets me thinking in a different way, and is providing me with a much better outlook on the issue. I think with just a little bit of sample code for me to work from for this, I think I can get it.

*edit* btw, 1000'th post!

iLLin
03-27-2007, 06:03 AM
I cant do that either because the items are going to be dynamic from the database. i have to do it the way I originally planned, because that is the only way I see to be able to work. I know its going to be messy, but it has to be done.

I provided you with alternate examples but your stuck on your one way. Good Luck...

thesavior
03-27-2007, 06:12 AM
iLLin, I dont mean to put it down, but all your ways either required the navigation itself to be in a seperate file from the rest of the nav.tpl, or to have a static output, cant have either of those.

I currently believe http://codingforums.com/showpost.php?p=549966&postcount=24

aedrin's way is the best so far.

The issue Im having is doing that, I dont need different ideas on how to do things, i need help on actually doing it.

iLLin
03-27-2007, 06:29 AM
iLLin, I dont mean to put it down, but all your ways either required the navigation itself to be in a seperate file from the rest of the nav.tpl, or to have a static output, cant have either of those.

I currently believe http://codingforums.com/showpost.php?p=549966&postcount=24

aedrin's way is the best so far.

The issue Im having is doing that, I dont need different ideas on how to do things, i need help on actually doing it.

Not sure what you mean but everything I showed you is dynamic...

thesavior
03-27-2007, 06:31 AM
okay, I really dont want to argue this with you. If you can, can you please help me with my actual question of how to code it, not other ways to do it? Im confused on the regex that would search between those two lines.

iLLin
03-27-2007, 06:34 AM
Did you even look at the navigation function? It's very similar to what your trying to achieve.

iLLin
03-27-2007, 06:47 AM
I like aedrin's way a little better, there pretty much the same except he calls the template file once and holds the content in a variable the echo's the changes through the loop, where I call the template file every time and echo the changes. Basically the same thing only I think his will be a ton faster.

thesavior
03-27-2007, 06:53 AM
I did look at your nav function, but the issues i posted with it are that it only would work once, because once it is replaced, it wont show up again to allow it to be replaced again. I also still need to figure out the regex to find the string between the two strings.

and thank you for agreeing that it should be done the way aedrin did it. Im still having the issue of actually doing it.

iLLin
03-27-2007, 07:02 AM
I'm not too bright on regex so I would do this :)



$tplfile = 'nav.tpl';
//get navigation values from db put in array
$nav_array = ("home" => "url", "test" => "test_url");

//call template once
$this->tpl = file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/".$tplfile.".tpl"); // Include nav tpl
foreach($nav_array as $key => $value) {

$vars = array("nav_item:name", "nav_item:url")
$values = array($key, value);
$content = str_replace($vars, $values, $this->tpl);

echo $content;
}

iLLin
03-27-2007, 07:03 AM
And I don't know why it would only show one link when you used the other one, its basically the same thing.

thesavior
03-27-2007, 07:29 AM
that doesnt work, because it doesnt create an entire new href tag and link for every item in the array. Im glad we are getting somewhere though, this was the point where I was going crazy trying to figure out how to get this to work. What im thinking needs to happen, is the code needs to find the line

<li><a href="{nav_item:url:}">{nav_item:name:}<a/></li>

in the tpl

and then for each item in the array duplicate a new one of the above lines and change it.

once it has gone through the array, it takes the final group of list items, and replaces it back into the tpl file overwriting that entire line. the function cant echo, only replace back into the tpl because after the navbar, the tpl class keeps trying to compile the rest of the functions.

iLLin
03-27-2007, 07:45 AM
Why not in your template you just call the function?

You said you have this in your index.tpl file



{file_include:header:}
{file_include:top_search:}
{file_include:forum_group:}
{file_include:stats:}
{file_include:footer:}


Why not include your navigation like this?
<ul>
{navigation:nav}
</ul>

Then create your nav.tpl file with just the <li>



EDIT: nvr I see why...

iLLin
03-27-2007, 08:14 AM
http://us2.php.net/manual/en/function.strpos.php

There is a function on there they can help you get text within tags.

thesavior
03-27-2007, 03:35 PM
I dont see how strpos will help my cause at all.

aedrin
03-27-2007, 03:55 PM
{navigation}
<li><a href="{nav:url}">{nav:link}</a></li>
{/navigation}




$links = /* however you build your links (array) */
$template = /* your original template content */

$matches = array();

preg_match_all("/\{([^\}]*?)\}([^\{]*?)\{\/([^\}]*?)\}/", $template, $matches);

foreach ($matches as $match) {
switch ($match[1]) { // 0 is original substring, 1 is first group
case 'navigation':
$baseline = $match[2]; // second group from match is the actual HTML that we need
$output = '';
foreach ($links as $link) {
$line = $baseline;
$line = str_replace('{nav:url}', $link['url'], $line);
$line = str_replace('{nav:title}', $link['title'], $line);
$output .= $line;
}
// replace original matched string with new HTML
$template = str_replace($match[0], $output, $template);
break;
}
}

echo $template;


Untested of course.

thesavior
03-28-2007, 01:12 AM
omg, thanks a lot, after working around with the code you just provided, I got it to work. The only issue im facing now is getting my current code to be compatible with your regex. To fix this, all I need to change is from getting the regex:

/\{([^\}]*?)\}([^\{]*?)\{\/([^\}]*?)\}/

from recognizing:

{navigation}

to recognizing:

{navigation::}


For my code, it has to have the two colons, but With the regex, when I add it in, I seem to only corrupt the regex, or just screw something up.

would it be:

/\{([^\}]*?)::\}([^\{]*?)\{\/([^\}]*?)\}/

or
/\{([^\}]*?)\:\:\}([^\{]*?)\{\/([^\}]*?)\}/

or just something different. Im not sure, and im extremely confused.

aedrin
03-28-2007, 04:02 PM
I still don't understand why you want all those colons, when it is much easier to do the way I did it. Then after that you can explode group 1.



/\{([^\}]*?)\:\:\}...


This would be the right one.

But like I said, I recommend using what I gave you for other things too. Just have to make groups 2 and 3 optional so it works for tags without an ending tag.

It's just a lot harder when making a template to always have to put in 1 or 2 colons. It's easy to forget them.

thesavior
03-28-2007, 11:06 PM
the reason i cant do that, is because the way my template system knows what functions to call, it checks for:

functionname:attribute:

so it has to have the colons. Im happy with the way it works, and I feel it is going to stay that way.

*edit*

okay, so after trying to figure out what the problem is that im having right now, is this. $this->tpl contains alot of those tags, so it is taking a really long time to search through all the tags, go through a loop checking for it to be navigation. So I need to change:



function navigation($var)
{
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);
$matches = array();
$output = '';
preg_match_all("/\{([^\}]*?)\:\:\}([^\{]*?)\{\/([^\}]*?)\}/", $this->tpl, $matches);
foreach ($matches as $match)
{
if ($match[1] == "navigation")
{
$baseline = $match[2]; // second group from match is the actual HTML that we need
foreach ($navlinks as $key => $value)
{
$line = $baseline;
$line = str_replace('{navigation:url:}', $key, $line);
$line = str_replace('{navigation:name:}', $value, $line);
$output .= $line;
}
// replace original matched string with new HTML
$this->tpl = str_replace($match[0], $output, $this->tpl);
}
}
}

to something more like:



function navigation($var)
{
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);
$matches = array();
$output = '';
preg_match_all("/\{navigation\:\:\}(.*)\{\/navigation\}/", $this->tpl, $matches);
$baseline = $matches[2]; // second group from match is the actual HTML that we need
foreach ($navlinks as $key => $value)
{
$line = $baseline;
$line = str_replace('{navigation:url:}', $key, $line);
$line = str_replace('{navigation:name:}', $value, $line);
$output .= $line;
}
// replace original matched string with new HTML
$this->tpl = str_replace($matches[0], $output, $this->tpl);
}


however, that regex doesnt work to have navigation in it. Does anyone know the regex to put in the literal navigation in the original search. And why does it need to be preg_match_all and not preg_match? What is the actual difference? I read through the description on php.net, but I couldnt figure it out.


and to make it even more descriptive to speed up the search, why dont we have the regex actuall search for exactly:

{navigation::}(.*){/navigation}

would that be something like:

\{navigation\:\:\}(.*)\{\/navigation\}

and would making this search more precise actually speed things up?

aedrin
03-29-2007, 12:27 AM
preg_match_all() will return every match, instead of just the first one.

thesavior
03-29-2007, 02:22 AM
we only need the first one though, so I will leave it as preg_match because there will only be one instance of {navigation::}.

However, whatabout the regex, and speed there of?

what would be the regex to search specifically between {navigation::} and {/navigation}?

/\{navigation\:\:\}(.*)\{\/navigation\}/

doesnt work, so Im not exactly sure.

aedrin
03-29-2007, 03:53 PM
I can't help you make something that is going to be a bad solution.

Having to make several regexes to do similar things is the first warning sign that things are done wrong.

I suggest you create a single regex, and try to work with that.

iLLin
03-29-2007, 04:15 PM
Why not do it as I suggested by making a separate tpl file for the navigation, then having your {navigation:include} in your file with your ':' and in your function navigation { } call the navigation.tpl file and loop through the results from your db, replace your values in the links continually adding them to $content .= or something. The doing the regular replace my $var[0] with this $content? So {navigation:include} turns into the navigation.tpl looped for however many links you have in your database...

I'm almost positive that will work, but I'm not sure if thats the solution your looking for which is still confusing me.

iLLin
03-29-2007, 04:18 PM
I'm not too bright on regex so I would do this :)



$tplfile = 'nav.tpl';
//get navigation values from db put in array
$nav_array = ("home" => "url", "test" => "test_url");

//call template once
$this->tpl = file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/".$tplfile.".tpl"); // Include nav tpl
foreach($nav_array as $key => $value) {

$vars = array("nav_item:name", "nav_item:url")
$values = array($key, value);
$content = str_replace($vars, $values, $this->tpl);

echo $content;
}


Instead of echoing the content, just keep adding it to a variable. Then work this in there.



$this->tpl = str_replace($match[0], $output, $this->tpl);


And when you call your template file don't use $this->tpl, just use a static variable like $navtpl or something so you don't overwrite your $this->tpl during execution.

thesavior
03-29-2007, 04:33 PM
@aedrin:
I don't have lots of regex's. I have one regex that finds all the function calls, splits it to the other functions. I just happen to need one more here. I dont believe things are being done poorly, and I would be very appreciative if you would still be willing to help out on this.

@iLLin:
I cannot make another separate file just for the navigation line. I am trying to keep the amount of template files down, so that wont work. It needs to be able to work by having the navigation line inside another tpl, like we have been trying to figure out how to do in the previous pages. I appreciate your help, and I'm sorry you do not completely understand what I am trying to make happen.

iLLin
03-29-2007, 05:00 PM
<?php
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);

$content = "{navigation:start}<li>this is a {navigation:url} and I am showing {navigation:name} for example{navigation:end}";
echo $content;

/* Gather all template tags. */
preg_match_all("~{navigation:start}\s*(.*?)\s*{navigation:end}~s", $content, $match);
$content = $match[1][0];

foreach ($navlinks as $key => $value)
{
$line = $content;
$line = str_replace('{navigation:url}', $key, $line);
$line = str_replace('{navigation:name}', $value, $line);
$output .= $line;
}

echo $output;

?>


That worked for me but from looking at what you have it seems your gonna need to do more with your run() function to make this work correctly. There is no need for the preg_match here when you are already running it. As when you initially parse your template it finds all these tags. I think your gonna need to setup some kind of start:end in that fuction to prevent multiple calls as it cycles through ALL {function:attribue} correct? So you could end up running the navigation function 4 times as the start:url:name:end. Or am I completely off base?

iLLin
03-29-2007, 05:39 PM
Try something like this to make it more dynamic and establish a way for you to run loops within your code and not just for the navigation.



function run()
{
$this->tpl = file_get_contents(ROOT_PATH."themes/".$this->tplname."/templates/pages/".$this->pagename.".tpl"); // Load the tpl file into the variable
while(preg_match("/{(.*)\:(.*)\:\}/", $this->tpl, $this->variable)) // Search for variables
// Call function $this->variable[1] with attribute $this->variable[2]
{
$func_name=$this->variable[1];

//skip all occurences within the loop as they were already captured within the function and replaced
if($open == true) continue;
//start loop from start to end
if($this->variable[2] == "start") {
$open = true;
preg_match_all("~{{$this->variable[1]}:start}\s*(.*?)\s*{{$this->variable[1]}:end}~s", $this->tpl, $match);
$content = $match[1][0];

if (method_exists($this, $func_name))
{
$this->$func_name($match, $content);
continue;
} else {
$this->superclass->err->errno = 3;
$this->superclass->err->throwerr();
}

} else if($this->variable[2] == "end") {
$open = false;
continue;
}
//end loop from start to end

if (method_exists($this, $func_name))
{
$this->$func_name($this->variable[2]);
}
else
{
$this->superclass->err->errno = 3;
$this->superclass->err->throwerr();
}
}
echo $this->tpl;
}

function navigation(&$var, $content)
{
$navlinks = array(
"Home" => "http://www.mydomain.com",
"Forums" => "http://www.mydomain.com/forums",
);

foreach ($navlinks as $key => $value)
{
$line = $content;
$line = str_replace('{navigation:url}', $key, $line);
$line = str_replace('{navigation:name}', $value, $line);
$output .= $line;
}
$this->tpl = str_replace($var[0][0], $output, $this->tpl);
}

aedrin
03-29-2007, 09:29 PM
I don't have lots of regex's. I have one regex that finds all the function calls, splits it to the other functions. I just happen to need one more here. I dont believe things are being done poorly, and I would be very appreciative if you would still be willing to help out on this.

This case is only one more, correct.

Next thing you need will be another one.

One measure of quality of code is how well it supports extensions. Can I add in this feature, without having to redo anything else. You want it to be flexible.

What will you do once a template code needs 4 variables? Modify all your code to take in 3 colons? Update every single template out there and add in an extra colon?


Try something like this to make it more dynamic and establish a way for you to run loops within your code and not just for the navigation.

And this is why you don't change the regular expression. As soon as you make it fixed for navigation, you lose all flexibility.

It doesn't sound like you have that much code that already uses this. Use the opportunity to update the design.

You can't tell me that:


{variable::}

Looks better than


{variable}

thesavior
03-30-2007, 02:05 AM
Okay, then starting at the run function, what is your feeling on how I should go about doing this to make it more modular? (Not meant to sound agressive, im just intrested in how this could be better.)

iLLin
03-30-2007, 05:52 AM
Well if you want it to be more modular and don't mind changing some of your base code, I suggest assigning variables instead of creating functions for every single thing you have.



//setup your nav array in the php file and assign it as a variable
$savior_template->assign("navigation", $navigation_array);
//setup your config variable
$savior_template->assign("configuration", $config_value);


Then maybe setup dynamic functions to use in your templates. Such as a "loop" or "include" (like you have).

I really don't see nothing wrong with your dot dot, but wouldn't this be so much easier?

{include file="header.tpl"}
or
{loop name="navigation" value=$navigation}
and
{$configuration}

I dunno, but if you are building this to use for all your apps, then I would recommend looking at other template engines and see how they do certain stuff. I'm not saying copy by all means, but get an idea on how successful templating engines are doing things.

My 2cents (Not worth much but there ya go)

aedrin
03-30-2007, 06:22 PM
I suggest assigning variables instead of creating functions for every single thing you have.


I suggest both ;)

Create generic functions that will loop through, or include/exclude depending on situations.

But then for convenience, add in functions that you know will always be in there. Such as navigation.

The only thing going between the model and the view should be pure data. No markup whatsoever.

thesavior
03-30-2007, 07:24 PM
Hmm, okay. How would assigning variables be used. I cant think of cases where I would need to do that.

This is now something I have never looked at before, so Im completely lost on how I would even go about creating a loop that is read by a function that creates its own loop.

aedrin
03-30-2007, 07:46 PM
{foreach:navigation}
<li><a href="{echo:item:url}">{echo:item:title}</a></li>
{/foreach}


Something along the lines of that, where navigation is an array of arrays, like this:



$navigation = array(
array('url' => 'index.html', 'title' => 'Home'),
array('url' => 'about.html', 'title' => 'About')
);


So you have to set up the following constructs:

The foreach tag that will take an array, and loop the contents for each element in the variable that is the first argument (navigation).

The echo tag which will take 1 or 2 arguments. If it is 1, echo out the variable. If it is 2, echo out the variable's (item) index named in the 2nd argument (url, title).

It's a slightly more advanced setup, but it's quite possible.

Of course you can cheat like Smarty and just conver it to PHP code. :)

Which turns your template from interpreted, to compiled. Both have their benefits and downsides.

EDIT:

With this setup, I'd change the tag format to this:



{foreach navigation}
<li><a href="{echo item:url}">{echo item:title}</a></li>
{/foreach}


Easier to read, yet still easily parseable.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum