Question

I'm having a problem with the latest major release of SwiftMailer where the decorator plugin will only replace the placeholder in the message for the first email address in the list, and then use the same data on all following emails - regardless of email address.

For instance if...

$replacements[test@test.com] = array('{firstname}'=>'Jeff', '{age}'=>'32');
$replacements[example@example.com] = array('{firstname}'=>'Mary', '{age}'=>'86');

The first email might say... "Hi Jeff, you are 32". And then the second email should say "Hi Mary, you are 86". But instead, the second email is identical to the first. Any ideas? I appreciate any and all help on this... Thanks.

This is my php code

foreach ($result as $user) {
  $replacements[$user['Email']] = array(
    '{FirstName}'=>$user['FirstName'],
    '{LastName}'=>$user['LastName']
  );
}

$mailer = Swift_Mailer::newInstance($transport);
$decorator = new Swift_Plugins_DecoratorPlugin($replacements);
$mailer->registerPlugin($decorator);


$message = Swift_Message::newInstance()
  ->setSubject('Important notice for {FirstName}')
  ->setFrom(array('john@doe.com' => 'John Doe'))
  ->setBody(
    "Hello {FirstName}, we have reset your password to {LastName}\n" .
   "Please log in and change it at your earliest convenience."
  )
  ;
foreach ($result as $user) {
$message->setTo($user['Email']);
}

// Create a message
//$template = file_get_contents('../html/full_width.html');  
//->setBody($template, 'text/html', 'utf-8'); 
//->addPart('Dear {FirstName} {LastName},This is testing mail.', 'text/plain', 'utf-8');
  // Send the message



// Pass a variable name to the send() method
if (!$numSent=$mailer->send($message,$failures))
{
  echo "Failures:";
  print_r($failures);
}
else
printf("Sent %d messages\n", $numSent);

And this is the plugin code

<?php

/*
 * This file is part of SwiftMailer.
 * (c) 2004-2009 Chris Corbyn
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */


/**
 * Allows customization of Messages on-the-fly.
 * 
 * @package Swift
 * @subpackage Plugins
 * 
 * @author Chris Corbyn
 * @author Fabien Potencier
 */
class Swift_Plugins_DecoratorPlugin
  implements Swift_Events_SendListener, Swift_Plugins_Decorator_Replacements
{

  /** The replacement map */
  private $_replacements;

  /** The body as it was before replacements */
  private $_orginalBody;

  /** The original headers of the message, before replacements */
  private $_originalHeaders = array();

  /** Bodies of children before they are replaced */
  private $_originalChildBodies = array();

  /** The Message that was last replaced */
  private $_lastMessage;

  /**
   * Create a new DecoratorPlugin with $replacements.
   * 
   * The $replacements can either be an associative array, or an implementation
   * of {@link Swift_Plugins_Decorator_Replacements}.
   * 
   * When using an array, it should be of the form:
   * <code>
   * $replacements = array(
   *  "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"),
   *  "address2@domain.tld" => array("{a}" => "x", "{c}" => "y")
   * )
   * </code>
   * 
   * When using an instance of {@link Swift_Plugins_Decorator_Replacements},
   * the object should return just the array of replacements for the address
   * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}.
   * 
   * @param mixed $replacements
   */
  public function __construct($replacements)
  {
    if (!($replacements instanceof Swift_Plugins_Decorator_Replacements))
    {
      $this->_replacements = (array) $replacements;
    }
    else
    {
      $this->_replacements = $replacements;
    }
  }

  /**
   * Invoked immediately before the Message is sent.
   * 
   * @param Swift_Events_SendEvent $evt
   */
  public function beforeSendPerformed(Swift_Events_SendEvent $evt)
  {
    $message = $evt->getMessage();
    $this->_restoreMessage($message);
    $to = array_keys($message->getTo());
    $address = array_shift($to);
    if ($replacements = $this->getReplacementsFor($address))
    {
      $body = $message->getBody();
      $search = array_keys($replacements);
      $replace = array_values($replacements);
      $bodyReplaced = str_replace(
        $search, $replace, $body
        );
      if ($body != $bodyReplaced)
      {
        $this->_originalBody = $body;
        $message->setBody($bodyReplaced);
      }

      foreach ($message->getHeaders()->getAll() as $header)
      {
        $body = $header->getFieldBodyModel();
        $count = 0;
        if (is_array($body))
        {
          $bodyReplaced = array();
          foreach ($body as $key => $value)
          {
            $count1 = 0;
            $count2 = 0;
            $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key;
            $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value;
            $bodyReplaced[$key] = $value;

            if (!$count && ($count1 || $count2))
            {
              $count = 1;
            }
          }
        }
        else
        {
          $bodyReplaced = str_replace($search, $replace, $body, $count);
        }

        if ($count)
        {
          $this->_originalHeaders[$header->getFieldName()] = $body;
          $header->setFieldBodyModel($bodyReplaced);
        }
      }

      $children = (array) $message->getChildren();
      foreach ($children as $child)
      {
        list($type, ) = sscanf($child->getContentType(), '%[^/]/%s');
        if ('text' == $type)
        {
          $body = $child->getBody();
          $bodyReplaced = str_replace(
            $search, $replace, $body
            );
          if ($body != $bodyReplaced)
          {
            $child->setBody($bodyReplaced);
            $this->_originalChildBodies[$child->getId()] = $body;
          }
        }
      }
      $this->_lastMessage = $message;
    }
  }

  /**
   * Find a map of replacements for the address.
   * 
   * If this plugin was provided with a delegate instance of
   * {@link Swift_Plugins_Decorator_Replacements} then the call will be
   * delegated to it.  Otherwise, it will attempt to find the replacements
   * from the array provided in the constructor.
   * 
   * If no replacements can be found, an empty value (NULL) is returned.
   * 
   * @param string $address
   * 
   * @return array
   */
  public function getReplacementsFor($address)
  {
    if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements)
    {
      return $this->_replacements->getReplacementsFor($address);
    }
    else
    {
      return isset($this->_replacements[$address])
        ? $this->_replacements[$address]
        : null
        ;
    }
  }

  /**
   * Invoked immediately after the Message is sent.
   * 
   * @param Swift_Events_SendEvent $evt
   */
  public function sendPerformed(Swift_Events_SendEvent $evt)
  {
    $this->_restoreMessage($evt->getMessage());
  }

  // -- Private methods

  /** Restore a changed message back to its original state */
  private function _restoreMessage(Swift_Mime_Message $message)
  {
    if ($this->_lastMessage === $message)
    {
      if (isset($this->_originalBody))
      {
        $message->setBody($this->_originalBody);
        $this->_originalBody = null;
      }
      if (!empty($this->_originalHeaders))
      {
        foreach ($message->getHeaders()->getAll() as $header)
        {
          $body = $header->getFieldBodyModel();
          if (array_key_exists($header->getFieldName(), $this->_originalHeaders))
          {
            $header->setFieldBodyModel($this->_originalHeaders[$header->getFieldName()]);
          }
        }
        $this->_originalHeaders = array();
      }
      if (!empty($this->_originalChildBodies))
      {
        $children = (array) $message->getChildren();
        foreach ($children as $child)
        {
          $id = $child->getId();
          if (array_key_exists($id, $this->_originalChildBodies))
          {
            $child->setBody($this->_originalChildBodies[$id]);
          }
        }
        $this->_originalChildBodies = array();
      }
      $this->_lastMessage = null;
    }
  }

}

another related plugin page

<?php

/*
 * This file is part of SwiftMailer.
 * (c) 2004-2009 Chris Corbyn
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Allows customization of Messages on-the-fly.
 * 
 * @package Swift
 * @subpackage Plugins
 * 
 * @author Chris Corbyn
 */
interface Swift_Plugins_Decorator_Replacements
{

  /**
   * Return the array of replacements for $address.
   * 
   * This method is invoked once for every single recipient of a message.
   * 
   * If no replacements can be found, an empty value (NULL) should be returned
   * and no replacements will then be made on the message.
   * 
   * @param string $address
   * 
   * @return array
   */
  public function getReplacementsFor($address);

}

Reference: 1. swift mailer plugin page: http://swiftmailer.org/docs/plugins.html (Using the Decorator Plugin)

  1. Disscuss on official forum 1. https://github.com/swiftmailer/swiftmailer/issues/101

  2. Disscuss on official forum 2. https://github.com/swiftmailer/swiftmailer/issues/161

Was it helpful?

Solution

Shouldn't the send() method be called as part of the loop? Otherwise you are just iterating through all the users, and the send() will only be called on the last element of the user array.

foreach ($result as $user) {

    $message->setTo($user['Email']);

    // Pass a variable name to the send() method
    if (!$numSent=$mailer->send($message,$failures)) { 
        echo "Failures:";
        print_r($failures);
    }
}

Seems odd that calling code is responsible for calling the beforeSendPerformed() method, but if that's the case, I would add it before the send() within the foreach loop.

-- Update --

Try this:

$mailer = Swift_Mailer::newInstance($transport);

$message = Swift_Message::newInstance()
  ->setSubject('Important notice for {FirstName}')
  ->setFrom(array('john@doe.com' => 'John Doe'));

foreach ($result as $user) {

    $message->setTo($user['Email']);

    $message->setBody(setBody(sprintf("Hello %s, we have reset your password to %s\n" . "Please log in and change it at your earliest convenience.", $user['FirstName'], $user['LastName']);

    if (!$numSent=$mailer->send($message,$failures)) {
        $failures[] = $failures;
    }
}

if (isset($failures)) {
    var_dump($failures);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top