0

Can anyone help me figure out why the str_replace inside a while loop only replace first row in email template?

Following is the code:

$vehicle=mysql_query("select * from tbl_vehicle where book_id='$booking_Id'");
$i=0;
while($rows=mysql_fetch_array($vehicle)){
    $make = $rows['make'];
    $model = $rows['model'];
    $message = str_replace("[make]",$make,$message);
    $message = str_replace("[model]",$model,$message);
$i++;}

and the html template;

<tr>
<td>Car Make</td>
<td>[make]</td>
</tr>
<tr>
<td>Car Model</td>
<td>[model]</td>

P.S I'm fetching template from Database.

Issue is when one vehicle booked it works fine but if more then one vehicle is booked it only replace first vehicle detail in email.

Thanks for the help.

EDITED; e.g if 1 vehicle booked, in email template it shows
Make: Make-One
Model: Model-One

If 2 vehicles booked, in email template is shows
Make: Make-One
Model: Model-One

but it should show
Make: Make-One
Model: Model-One
Make: Make-Two
Model: Model-Two

Hope this clears the question.

UPDATE; Simon_w solution didn't work, but gives me an idea (i don't know its best approach or not)

while($rows=mysql_fetch_array($vehicle)){
    $make = $rows['make'];
    $model = $rows['model'];
$i++;
    if($i==2) {
    $message = str_replace("[make]",$make,$message);
    $message = str_replace("[model]",$model,$message);
    $message = str_replace("[make2]",$make,$message);
    $message = str_replace("[model2]",$model,$message);

    }
    else {
    $message = str_replace("[make]",$make,$message);
    $message = str_replace("[model]",$model,$message);
    }
}

and in email template

<tr>
<td>Car Make</td>
<td>[make]</td>
</tr>
<tr>
<td>Car Model</td>
<td>[model]</td>

<tr>
<td>Car Make</td>
<td>[make2]</td>
</tr>
<tr>
<td>Car Model</td>
<td>[model2]</td>

So this solves the 2 vehicle issue, both detail showing but if only 1 vehicle booked then in email template [make2] [model2] shows blank so is there any way to use if statement in email template too?

2
  • you realize you can pass an array of search replace params to str_replace right? github.com/r3wt/superuser/blob/master/libs/custom/… Commented Nov 30, 2014 at 20:22
  • 1
    str_replace() will replace all occurrences in a string (i.e. all your placeholders) meaning when you hit the second DB record, there are no placeholders left to replace. You could use preg_replace() with a limit of 1 instead: preg_replace('/\[make\]/', $make, $message, 1) Commented Nov 30, 2014 at 20:23

3 Answers 3

2

This is because you're changing the value of $message. You can either re-load it at the start of the while loop, or store it into a separate variable so that it's not overridden. For example, you could use $body instead of $message, meaning that the contents of $message never changes.

EDIT: With the edit to the original question, I've since changed the code to build up the body of the email, by appending the template to it in each loop.

$body = '';
while($rows=mysql_fetch_array($vehicle)){
    $make = $rows['make'];
    $model = $rows['model'];
    $body .= str_replace(array("[make]", "[model]"),array($make,$model),$message);
    $i++;
}
Sign up to request clarification or add additional context in comments.

1 Comment

@scrowler Only if the question is interpreted in a certain way. With a more straight-forward interpretation, this fixes it. Also, instead of just posting comments telling people they're wrong, how about you post an answer yourself?
1

str_replace() will replace all occurrences in a string (i.e. all your placeholders) meaning when you hit the second DB record, there are no placeholders left to replace. From the str_replace() manual:

This function returns a string or an array with all occurrences of search in subject replaced with the given replace value.

You could use preg_replace() with a limit of 1 instead:

$i = 0;
foreach ($example_db_results as $rows) {
    $make = $rows['make'];
    $model = $rows['model'];
    $message = preg_replace("/\[make\]/", $make, $message, 1);
    $message = preg_replace("/\[model\]/", $model, $message, 1);
    $i++;
}

Example here: https://eval.in/228673

Edit: Following your comment - sounds like a data inconsistency to me... Your template has a fixed number of rows yet your database can have many. You should take the template in for each loop and add it an outer HTML variable so the number of HTML rows and the number of database rows is equal.

In this case, you don't need to limit your replacements because there will only be one of them, so I'll show you an example with your original attempt:

$i = 0;
$output = '';
foreach ($example_db_results as $rows) {
    $make = $rows['make'];
    $model = $rows['model'];
    // Define your temporary template variable
    $temp_template = $template;
    $temp_template = str_replace("[make]", $make, $temp_template);
    $temp_template = str_replace("[model]", $model, $temp_template);
    // Append the temp template to the output variable
    $output .= $temp_template;
    unset($temp_template);
    $i++;
}

As @r3wt mentioned in his comment on your question, you can (and should) pass arrays into str_replace() when doing this. Would be a good idea if the number of placeholders is going to increase.

Example here: https://eval.in/228714

1 Comment

Your solution partially worked, its more like the idea i had but with better approach but end result same, it showed 2 vehicle information but if only 1 vehicle then it shows [make] [model] for 2nd vehicle in template, any idea how to hide them.
0

You should use Regular Expression for better results.

Example:

$template = //email template
$wrapper  = '/\[+(.*?)\]/';
$matches  = array();
preg_match_all($wrapper, $template, $matches);

UPDATE: Apologies for the misunderstanding. I happen to have a dirty class exactly what OP asked for! The only difference is that I used [{()}] for wrapping and used [<loop>] [</loop>] for the elements that might contain multiple values..

there you go ;)

class StringTemplate{

    protected $template;
    protected $tokens;
    protected $wrapper;
    protected $loopTag;
    protected $widgets;
    protected $widgetWrapper;
    protected $loopBlock;
    protected $tokenMissed;
    protected $loopTemplate;


    protected $data;


    public function __construct($template)
   {

        $this->data     = array();
        $this->tokens   = array();
        $this->wrapper = '/\[\{\(+(.*?)\)\}\]/';

        $this->widgetWrapper = '/\[\{\(widget:+(.*?)\)\}\]/';


        $this->loopTag  = 'loop';
        $this->loopWrapper =    '/\[\<'.$this->loopTag.'\>\]+(.*?)\[\<\/'.$this->loopTag.'\>\]/s';

        $this->template = $template;



        if  (preg_match($this->loopWrapper, $this->template, $matches)){

            $this->loopBlock      = $matches[1]; 
            $this->loopTemplate = $matches[0]; // includes [<loop>]

        } 

        if  (preg_match_all($this->wrapper, $this->template, $matches)){

            $tokens = $matches[1];

            foreach ($tokens as $token){

                if (strpos($token, ':')){

                    $tokenDetails = explode(':', $token); 
                    $this->tokens[$tokenDetails[0]][] = $tokenDetails[1];

                } else {

                    $this->tokens[] = $token;

                }

            }

        } 
    }


    public function setData($data){

        $offsets = array();
        $missedToken = false;

        /* loop available    */
        if ($this->loopTemplate){

            if (!isset($data[$this->loopTag][0])){

                $missedToken = true;

            } else {

                $offsets = array_merge(array_keys($data), array_keys($data[$this->loopTag][0]));

            }

        } else {

            $offsets = array_keys($data);

        }

        foreach ($this->tokens as $key=>$token){

            if (is_array($token)){

                foreach ($token as $value){

                    if (!isset($data[$key.':'.$value])){

                        $this->tokenMissed[$key][] = $value;

                    }

                }

            } else if (!isset($data[$token])){

                $this->tokenMissed[] = $token;

            }

        }

        if (!$this->tokenMissed){

            // Loop first..

            $loopOutput = null;

            $output = $this->template;

            if (isset($data[$this->loopTag])){

                foreach ($data[$this->loopTag] as $loop){

                    $loopOutput .= $this->wrapData($this->loopBlock,array_merge($data,$loop));

                }

                $output = str_replace($this->loopTemplate, $loopOutput, $this->template);

            }


            $output = $this->wrapData($output,$data);

            return $output;

        }

        //print_r('<pre>');
        //print_r($data);
        //print_r($output);
        //print_r($this->tokenMissed);
        //print_r($offsets);
        //print_r($this->tokens);
        //print_r($this->loopTemplate);
        //print_r(array_diff($this->tokens, $offsets));
        //print_r($this->template);

        //print_r('</pre>');

        //die;



    }


    public function wrapData($template,$data) {

        $output = $template;

        if  (preg_match_all($this->wrapper, $template, $matches)){

            $tokens   = $matches[1];
            $wrappers = $matches[0];

            $values      =  array(); 

            foreach ($tokens as $token){

                if (isset($data[$token])){

                    $values[]  = $data[$token];
                }
            }

            $output = str_replace($wrappers, $values, $template);
        }

        return $output;

    }



    public function getTokens()
    {
        return $this->tokens;
    }




    public function getMissedTokens()
    {
        return $this->tokenMissed;  
    } 


}

1 Comment

@scrowler: You're right! My apologies. Will update my answer shortly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.