Odesílání emailů podle šablony
- David Grudl
- Nette Core | 8228
Častým úkolem webových aplikací je rozesílat potvrzující a notifikační emaily. K tomu se výborně hodí šablonovací podpora Nette.
Dnes už existuje šikovnější řešení pomocí Nette\Mail – viz info
Mějme běžný presenter, který do šablony registruje nějaké filtry a helpery:
abstract class BasePresenter extends Presenter
{
protected function createTemplate()
{
$template = parent::createTemplate();
$template->registerHelper('money', array($this, 'moneyHelper'));
$template->registerHelper('texy', array($this, 'texyHelper'));
...
return $template;
}
}
Z něj podědíme presenter Checkout
, který zpracuje
objednávku zboží a po jejím dokončení odešle zákazníkovi email.
Šablonu emailu uložíme do adresáře emails
ve složce šablon a
bude se jmenovat např. checkout-confirmation.phtml
.
class CheckoutPresenter extends BasePresenter
{
public function actionSend()
{
... // zpracování objednávky
$email = $this->createTemplate();
$email->setFile(Environment::expand('%templatesDir%/emails/checkout-confirmation.phtml'));
$email->name = $name; // naplníme nějaká data
...
$mailer = new Zend_Mail();
$mailer->setBodyText((string) $email); // vyrenderujeme šablonu
$mailer->setFrom('somebody@example.com', 'Some Sender');
$mailer->addTo('somebody_else@example.com', 'Some Recipient');
$mailer->setSubject('Potvrzení objednávky');
$mailer->send();
}
}
Mohli bychom jít i dál a chtít do šablony umístit i hlavičky
From
a Subject
. To lze řešit tak, že šabloně
předáme objekt, který ona naplní:
class CheckoutPresenter extends BasePresenter
{
public function actionSend()
{
... // zpracování objednávky
$email = $this->createTemplate();
$email->setFile(Environment::expand('%templatesDir%/emails/checkout-confirmation.phtml'));
$email->name = $name; // naplníme nějaká data
$email->headers = (object) NULL;
...
$body = (string) $email; // vyrenderujeme šablonu už nyní
$mailer = new Zend_Mail();
$mailer->setBodyText($body);
$mailer->setFrom($email->headers->from);
$mailer->addTo('somebody_else@example.com', 'Some Recipient');
$mailer->setSubject($email->headers->subject);
$mailer->send();
}
}
Šablona emailu by pak vypadala nějak takto:
{**
* Checkout notification
* @param stdClass $headers
* @param string $name
*}
{? $headers->from = '"Super Giga Eshop" <info@example.com>'; }
{? $headers->subject = "Potvrzení objednávky"; }
Dobrý den {$name},
vaše objednávka byla přijata.
Čest!
František Novák
Super Giga Eshop
Pokud budeme v šabloně generovat URL, je potřeba zajistit, aby byly
absolutní. K tomu účelu si můžeme například zaregistrovat filtr, který
před každý odkaz začínající znakem /
vloží
http://example.com
.
- romansklenar
- Člen | 655
Úplně jednoduše: všechny je smazat a načítat si celou knihovnu autoloaderem ;)
- edke
- Člen | 198
Zdar David,
mam problem na ktory som natrafil, ked som sa snazil implementovat tento tvoj sikovny snipplet. V nasledujucom kode:
final class FrontEnd_SandboxPresenter extends FrontEnd_BasePresenter
{
public function actionDefault()
{
$email= $this->createTemplate();
$email->setFile(Environment::expand('%templatesDir%/emails/activation.phtml'));
$email->name = 'xxx';
$email->headers = (object) null;
#alternativa 1
$body = (string) $email; // vyrenderujeme šablonu už nyní
#alternativa 2
ob_start();
$email->render();
$body= ob_get_clean();
Ak pouzijem alternativu 2, vsetko bezi ako treba. Ale ak pouzijem alternativu 1, po tomto riadku je problem s output bufferingom, nic (ani koncovy zdrojak, ani exceptions) uz sa do prehliadaca nevypise.
- kravčo
- Člen | 721
edke napsal(a):
Ak pouzijem alternativu 2, vsetko bezi ako treba. Ale ak pouzijem alternativu 1, po tomto riadku je problem s output bufferingom, nic (ani koncovy zdrojak, ani exceptions) uz sa do prehliadaca nevypise.
Vyskúšaj alternatívu 1'
$body = $email->__toString(TRUE);
Robí to to isté ako (string) $email
, akurát môže
vyhadzovať výnimky. Keďže PHP nedovoľuje vyhodiť výnimku z automaticky
zavolanej magickej funkcie __toString()
, táto v Nette štandardne
výnimky nevyhadzuje a vypisuje tuším prípadnú zachytenú výnimku ako
E_USER_WARNING. V prípade, ak ju zavoláš priamo, výnimky nemajú problém a
parameter TRUE
zabezpečí, že sa výnimka nezachytí ale pustí
ďalej. viď Nette\Template::__toString()
Editoval kravco (4. 2. 2009 13:48)
- edke
- Člen | 198
kravco wrote:
Vyskúšaj alternatívu 1'$body = $email->__toString(TRUE);
Robí to to isté ako
(string) $email
, akurát môže vyhadzovať výnimky. Keďže PHP nedovoľuje vyhodiť výnimku z automaticky zavolanej magickej funkcie__toString()
, táto v Nette štandardne výnimky nevyhadzuje a vypisuje tuším prípadnú zachytenú výnimku ako E_USER_WARNING. V prípade, ak ju zavoláš priamo, výnimky nemajú problém a parameterTRUE
zabezpečí, že sa výnimka nezachytí ale pustí ďalej. viďNette\Template::__toString()
Jo, super, ked to zavolam takto priamo cez magic metodu, tak to ide ako ma. Co sa bude diat, ak pri vykonani sablony nastane chyba alebo exception ?
- Ondřej Mirtes
- Člen | 1536
Super návod a elegance na n-tou! :)
Dokázal jsem to implementovat, jen mám takový mini-pidi problém:
Části kódu:
$mail->setSubject(Environment::getConfig('mail')->subject);
config.ini:
mail.subject = "Nová zpráva z kontaktního formuláře"
A nevím, jestli se někde něco přetypovává nebo escapuje, ale v e-mailu mi vyleze:
Novázpráva z kontaktníhoformuláře
Jako mailer používám tuto třídu:
http://www.phpguru.org/…meMail5.html
- Ondrej
- Člen | 110
LastHunter napsal(a):
Jako mailer používám tuto třídu:
http://www.phpguru.org/…meMail5.html
htmlMimeMail5 ma problemy s cestinou v subjektu
- Ondřej Mirtes
- Člen | 1536
Jod, Ondrej: Jojo, koukam, Nette za to fakt nemuze :( Takze zkusim ten Zend_Mail, diky.
- Tomik
- Nette Evangelist | 485
V případě, že by Zend_Mail z nějakého důvodu nevyhovoval, doporučuju http://swiftmailer.org/ …
- amsys
- Člen | 20
Zdravím,
potřeboval jsem odesílat trošku osobnější maily a tak jsem si splácal jeden pomocnej objekt tímto způsobem, jsem na správné cestě?
<?php
require LIBS_DIR . '/Zend/Mail.php';
class TemplateMail extends Control {
private $t_name = '';
private $identity;
private $from;
private $plain = '';
private $delayed = false;
public function __construct($template_name, IIdentity $user_identity, $delayed = false) {
$this->t_name = $template_name;
$this->identity = $user_identity;
$this->delayed = $delayed;
}
protected function createTemplate(){
$template = parent::createTemplate();
$template->setFile(Environment::expand('%templatesDir%/Mail/') . $this->t_name . '_'. $this->identity->getLanguage() . '.phtml');
$template->registerFilter('Nette\Templates\CurlyBracketsFilter::invoke');
$template->options = (object) NULL;
return $template;
}
public function getOutput(){
return (string) $this->template;
}
public function getPlainOutput(){
if (empty($this->plain)) {
// http://www.talkincode.com/convert-html-to-ascii-with-php-143.html
$s = preg_replace(array(
'/\n/', // strip all newlines
'/<a\s+.*?href="?mailto:([^\" >?]*)"?[^>]*>(.*?)<\/a>/i', // emails
'/<a\s+.*?href="?([^\" >]*)"?[^>]*>(.*?)<\/a>/i', // links
'@<(b|h)r[^>]*>@i', // br and hr tags
'@<p[^>]*>@i', // paragraphs
'@<div[^>]*>(.*)</div>@i', // boxes
'@<b[^>]*>(.*?)</b>@i', // bold tags
'@<strong[^>]*>(.*?)</strong>@i', // bold tags
'@<i[^>]*>(.*?)</i>@i', // italic tags
'@<em[^>]*>(.*?)</em>@i', // italic tags
),array(
'',
'$1',
'$2 ($1)',
"\n",
"\n\n",
'$1',
'*$1*',
'*$1*',
'_$1_',
'_$1_'
), $this->getOutput());
// decode any entities
$s = strtr($s,array_flip(get_html_translation_table(HTML_ENTITIES)));
// decode numbered entities
$s = preg_replace('/&#(\d+);/e','chr(str_replace(";","",str_replace("&#","","$0")))',$s);
// strip any remaining HTML tags
$this->plain = strip_tags($s);
}
// return the string
return $this->plain;
}
public function render()
{
$this->template->render();
}
public function send()
{
$mail = new Zend_Mail('utf-8');
$mail->setType(Zend_Mime::MULTIPART_RELATED);
// embed logo
$img = $mail->createAttachment(file_get_contents(WWW_DIR . '/images/logo-mail.gif'));
$img->id = md5(uniqid(time()));
$img->type = 'image/gif';
$img->disposition = Zend_Mime::DISPOSITION_INLINE;
$img->encoding = Zend_Mime::ENCODING_BASE64;
$img->filename = 'logo.gif';
$this->template->logo_cid = $img->id;
$mail->setBodyHtml($this->getOutput(), NULL, Zend_Mime::MULTIPART_RELATED);
$mail->setBodyText($this->getPlainOutput());
$mail->setFrom(
isset($this->template->admin_email) ? $this->template->admin_email : $this->template->options->email,
isset($this->template->admin_name) ? $this->template->admin_name : $this->template->options->name
);
$mail->addTo($this->identity->getEmail(), $this->identity->getFullname());
$mail->setSubject($this->template->options->subject);
if ($this->delayed) {
$filename = Environment::expand('%spoolDir%/mail/') . uniqid();
$fh = fopen($filename, 'w');
fwrite($fh, serialize($mail));
fclose($fh);
} else {
$mail->send();
}
}
}
?>
Editoval amsys (9. 4. 2009 21:16)
- R2D2
- Člen | 22
inline/embedded (či jak to chceš nazvat) přílohy samozřejmě thunderbird zobrazí správně v kódu, když jsou dobře poslané.
Se Zendem jsem je ale nikdy neposílal, jen s phpmailerem … zkontroluj v tom thunderbirdu zdrojovej kód, co to tam ve skutečnosti teda napíše (hlavička přílohy), nic konkrétního mě ale nenapadá
- amsys
- Člen | 20
Se Zendem jsem je ale nikdy neposílal, jen s phpmailerem … zkontroluj v tom thunderbirdu zdrojovej kód, co to tam ve skutečnosti teda napíše (hlavička přílohy), nic konkrétního mě ale nenapadá
Thunderbird má ten obrázek se stejným boundary jako je ta html zpráva, Zend_Mail ničim jiným se to neliší takže teď už jenom přijít na to jak to spojit dohromady :-)
Už jsem na to přišel a upravil jsem kód v předchozím příspěvku
Až to budete také řešit tak zkoukněte tohle:
* http://www.anty.info/…l-obstacles/
* http://www.sitepoint.com/…newsletters/
Editoval amsys (9. 4. 2009 21:18)
- amsys
- Člen | 20
Tak ještě to neni úplně ideální, thunderbird a webmaily to sice zobrazí, ale je tu jedna vyjímka v náhledu mailu u KMailu kde se ten obrázek nezobrazí (pokud mail otevřu tak už ano), ale mám jeden mail který jsem generoval přes Zend_Mail kdysi dávno kde se to povedlo, snažil jsem se najít rozdíly a historii souboru, ale bylo to marné hledání. Třeba za týden se na to přijde .-)
- amsys
- Člen | 20
phx napsal(a):
Jakozto uzivatel TB bych spise rek, ze prilohy (obrazky) se zobrazi v/pod mailem kdyz je mail v HTML. Pokud jde o Plain text tak jsou dole ve specialnim okenku.
Poslední kód co jsem vkládal zobrazuje obrázky korektně v TB, Squirrelmail je taky ok po otevření html zprávy, u Outlooku zatím netuším, pouze vím, že v KMailu to zlobí a zobrazí se když ten mail otevřu a to korektně, ne mezi přilohami)
Protože se to nakonec nějak zobrazí tak to prozatím neřeším a k tomuto problému se vrátím až dohákuju problémy mnohem důležitější, jinak ten přístup který jsem zvolil html->plain by možná šel překopat na texy->html a bylo by to možná hezčí