As you may know, I'm in charge of providing a standard way to send emails within symfony 1.2. I'd like to get your feedback and ideas about the way the SwiftMailer library goodness should be integrated. Currently I've just played with Swift and nothing has really been started by my side, so everything is still possible =)
To me, the main important thing if to be able to use a different mailer dependending of the environment: maybe in dev mode you doesn't want to send real email, but just want to generate them and store them on your local filesystem (or anywhere you want), as the sfEmailPlugin does. That would be possible using the factories.yml file, like this:
My main concern here is that it will be hard to bundle it as a plugin, because I do not know any way to hook/override the factories configuration in symfony within a plugin context... Maybe a smart approach would be to provide base abstract classes in the symfony core, and the plugin would provide a Swift based implementation? I don't know, but it looks like if not possible, we'll must implement this feature in the core...
I was also thinking about creating dedicated message classes (as Swift already does) but with native symfony-based mail contents generation available, to ease mail objects reuse. Maybe something like this (rough draft):
$email = new sfEmail(); // then $email->setHtmlContentResolver(sfEmail::FROM_ACTION, array('mymodule', 'myaction')); // or $email->setTextContentResolver(sfEmail::FROM_PARTIAL, array('mymodule', 'mypartial')); // or $email->setTextContentResolver(sfEmail::FROM_COMPONENT, array('mymodule', 'mycomponent')); // or $email->setTextContentResolver(sfEmail::FROM_STRING, 'hello world'); // and maybe $email->setContentResolver('text/html', sfEmail::FROM_STRING, 'hello world'); // why not $email->setContentResolver('application/pdf', sfEmail::FROM_FILE, '/tmp/toto.pdf'); // there could be also sfEmail::FROM_CALLBACK and so on // then $email->send();
It's a bit verbose, eh? Maybe we can also use dedicated classes for email templates, eg. sfEmailTemplateAction, sfEmailTemplateComponent, etc., but maybe it's a bit bloated by design for the purpose of just sending emails? Swift has native support fr template, maybe just extending it with these sfEmail templates classes could be a solution?
For custom reuseable emails, we should be able to easily create dedicated email classes:
class myEmail extends sfEmail { public function configure() { // configure the email here, eg. $this->setSubject('Hello there'); }
}
These classes would be stored in a new lib/email directory by default, maybe with a stub BaseEmail class available for user level generic implementation purpose: sfEmail < BaseEmail < myEmail.
At the end, I would like to be able do do things like that:
class fooActions extends sfActions { public function executeBuy($request) { if ($request->isMethod('post)) { $email = new myEmail(); $email->addTo('t...@toto.com'); $email->setVars(array($request->getAttribute('stuff'))); $email->send(); } }
}
So here are my actual thoughts. What are your ideas and suggestions?
public function __set($key, $value) { sfSwiftMailerVars::getInstance()->set($key, $value);
return parent::__set($key, $value); }
}
<?php class sfSwiftMailerVars extends sfParameterHolder { static $instance = null;
public static function getInstance() { if (!self::$instance) { self::$instance = new sfSwiftMailerVars(); }
return self::$instance; }
}
With that setup, I extend all my module actions from sfSwiftMailerActions and I call $this->sendEmail('module', 'action'); The module which is responsible for holding the e-mails also extends from the same class and the preExecute() exists so that any variables set inside of the module which invokes the email will be available to the email action/template as well. I accomplish this by overriding __set() of sfActions. I realize this is an absolutely horrid hack but it works for my project. I am not sure if we could implement something like this cleanly without it being in the core. Here is an example.
class contact_usActions extends sfSwiftMailerActions { public function executeIndex() { $this->send_to = 'jonw...@gmail.com'; $this->sendEmail('mailer', 'contact_us'); }
}
class mailerActions extends sfSwiftMailerActions { public function executeContact_us() { $this->subject = 'test'; $this->recipients = $this->send_to; }
If you don't specify a variable in your mailer action named 'message' and 'swift' that contain instances of Swift_Message and Swift they will just be automatically created for you with some defaults.
- Jon
On Mon, Sep 15, 2008 at 7:09 AM, Nicolas Perriault <nperria...@gmail.com>wrote:
> As you may know, I'm in charge of providing a standard way to send > emails within symfony 1.2. I'd like to get your feedback and ideas > about the way the SwiftMailer library goodness should be integrated. > Currently I've just played with Swift and nothing has really been > started by my side, so everything is still possible =)
> To me, the main important thing if to be able to use a different > mailer dependending of the environment: maybe in dev mode you doesn't > want to send real email, but just want to generate them and store them > on your local filesystem (or anywhere you want), as the sfEmailPlugin > does. That would be possible using the factories.yml file, like this:
> My main concern here is that it will be hard to bundle it as a plugin, > because I do not know any way to hook/override the factories > configuration in symfony within a plugin context... Maybe a smart > approach would be to provide base abstract classes in the symfony > core, and the plugin would provide a Swift based implementation? I > don't know, but it looks like if not possible, we'll must implement > this feature in the core...
> I was also thinking about creating dedicated message classes (as Swift > already does) but with native symfony-based mail contents generation > available, to ease mail objects reuse. Maybe something like this > (rough draft):
> $email = new sfEmail(); > // then > $email->setHtmlContentResolver(sfEmail::FROM_ACTION, array('mymodule', > 'myaction')); > // or > $email->setTextContentResolver(sfEmail::FROM_PARTIAL, > array('mymodule', 'mypartial')); > // or > $email->setTextContentResolver(sfEmail::FROM_COMPONENT, > array('mymodule', 'mycomponent')); > // or > $email->setTextContentResolver(sfEmail::FROM_STRING, 'hello world'); > // and maybe > $email->setContentResolver('text/html', sfEmail::FROM_STRING, 'hello > world'); > // why not > $email->setContentResolver('application/pdf', sfEmail::FROM_FILE, > '/tmp/toto.pdf'); > // there could be also sfEmail::FROM_CALLBACK and so on > // then > $email->send();
> It's a bit verbose, eh? Maybe we can also use dedicated classes for > email templates, eg. sfEmailTemplateAction, sfEmailTemplateComponent, > etc., but maybe it's a bit bloated by design for the purpose of just > sending emails? Swift has native support fr template, maybe just > extending it with these sfEmail templates classes could be a solution?
> For custom reuseable emails, we should be able to easily create > dedicated email classes:
> class myEmail extends sfEmail > { > public function configure() > { > // configure the email here, eg. > $this->setSubject('Hello there'); > } > }
> These classes would be stored in a new lib/email directory by default, > maybe with a stub BaseEmail class available for user level generic > implementation purpose: sfEmail < BaseEmail < myEmail.
> At the end, I would like to be able do do things like that:
> class fooActions extends sfActions > { > public function executeBuy($request) > { > if ($request->isMethod('post)) > { > $email = new myEmail(); > $email->addTo('t...@toto.com'); > $email->setVars(array($request->getAttribute('stuff'))); > $email->send(); > } > } > }
> So here are my actual thoughts. What are your ideas and suggestions?
My assumptions : sf1.0 has a nice way to handle mail : easy to use and quickly understanble by everyone. Agree ?
This feature has been remove form sf1.1, as I suppose something was wrong with it. ( ... ?)
I think sending emails should be very easy.
$this = Controller
$this->sendMail(array( 'mailClass' => 'sfMail', 'action' => 'module/mail', 'template' => 'my_template', 'layout' => ''email_layout', 'vars' => array( 'invoice' => $invoice, 'file' => new sfFile(), // can generate a unique identifier for the file ); ));
of course some of this value could be guessed by the sendMail or defined in a yml file.
As the sendMail is not welcome anymore in the controller, a mail class (let's say sfMailComponent) can register to listen [1] to the controller.method_not_found event, and retrieve 'sendMail' call.
On Mon, Sep 15, 2008 at 3:17 PM, Thomas Rabaix <thomas.rab...@gmail.com> wrote: > My assumptions : sf1.0 has a nice way to handle mail : easy to use and > quickly understanble by everyone. Agree ?
I don't like that much the "all-in-the-controller" approach, even if it's true that you can move the code into another protected method, even in a parent stub class (actually that's what I've done in symfonians.net, but I'm not happy with it, even if it works).
> $this = Controller
> $this->sendMail(array( > 'mailClass' => 'sfMail', > 'action' => 'module/mail', > 'template' => 'my_template', > 'layout' => ''email_layout', > 'vars' => array( > 'invoice' => $invoice, > 'file' => new sfFile(), // can generate a unique identifier for the file > ); > ));
> of course some of this value could be guessed by the sendMail or > defined in a yml file.
With what I've in mind, mail sending in your action would be quite similar to:
$email = new InvoiceEmail(); $email->setVars(array('invoice' => $invoice)); // may be $email->addVar('invoice', $invoice); $email->send();
And in InvoiceEmail.class.php
class InvoiceEmail extends BaseEmail { public function configure() { $this->setHtmlContentResolver(sfEmail::FROM_ACTION, array('mail', 'invoice')); $this->setTextContentResolver(sfEmail::FROM_ACTION, array('mail', 'invoice'), array('sf_format' => 'txt')); // notice the sf_format trick // or $this->contentResolvers['text/html'] = new sfEmailTemplateAction('mail', 'module'); ?
$invoice = $this->getVar('invoice');
$this->setSubject(sprintf('Your order #%s', $invoice->getOrderNumber())); $this->addTo($invoice->getCustomer()->getEmail()); $this->addBCC('sales_cont...@mycompany.com'); $this->attachFile($invoice->asPDF(), 'invoice.pdf', 'application/pdf'); }
}
Two templates should then exist in the mail module templates folder, invoiceSuccess.php and invoiceSuccess.txt.php. Okay, I don't like the set*ContentResolver method calls but I guess that's why I'm asking for better ideas here ;-)
The idea here is to move the email logic from the controllers to something apart, as what has been done with forms.
> As the sendMail is not welcome anymore in the controller, a mail class > (let's say sfMailComponent) can register to listen [1] to the > controller.method_not_found event, and retrieve 'sendMail' call.
Way hacky, isn't it? I don't know. People, express yourself :-)
+1 for putting this in factories.yml and extending sfContext to accept custom factories from plugins and the project.
It's my understanding the 2.0 service container will be flexible in this way (per Fabien's presentation at sf camp), so this change to sfContext would also serve forward compatibility.
> I don't like that much the "all-in-the-controller" approach, even if > it's true that you can move the code into another protected method, > even in a parent stub class (actually that's what I've done in > symfonians.net, but I'm not happy with it, even if it works).
>> $this = Controller
>> $this->sendMail(array( >> 'mailClass' => 'sfMail', >> 'action' => 'module/mail', >> 'template' => 'my_template', >> 'layout' => ''email_layout', >> 'vars' => array( >> 'invoice' => $invoice, >> 'file' => new sfFile(), // can generate a unique identifier for >> the file >> ); >> ));
>> of course some of this value could be guessed by the sendMail or >> defined in a yml file.
> With what I've in mind, mail sending in your action would be quite > similar to:
> $email = new InvoiceEmail(); > $email->setVars(array('invoice' => $invoice)); > // may be $email->addVar('invoice', $invoice); > $email->send();
> And in InvoiceEmail.class.php
> class InvoiceEmail extends BaseEmail > { > public function configure() > { > $this->setHtmlContentResolver(sfEmail::FROM_ACTION, array('mail', > 'invoice')); > $this->setTextContentResolver(sfEmail::FROM_ACTION, array('mail', > 'invoice'), array('sf_format' => 'txt')); // notice the sf_format > trick > // or $this->contentResolvers['text/html'] = new > sfEmailTemplateAction('mail', 'module'); ?
> Two templates should then exist in the mail module templates folder, > invoiceSuccess.php and invoiceSuccess.txt.php. Okay, I don't like the > set*ContentResolver method calls but I guess that's why I'm asking for > better ideas here ;-)
> The idea here is to move the email logic from the controllers to > something apart, as what has been done with forms.
>> As the sendMail is not welcome anymore in the controller, a mail >> class >> (let's say sfMailComponent) can register to listen [1] to the >> controller.method_not_found event, and retrieve 'sendMail' call.
> Way hacky, isn't it? I don't know. People, express yourself :-)
I am almost ok with this method, but ... - the set*ContentResolver are ... quite complicated, can it be more magic, or optional ? - this should me an sfMail class which handle array as constructor. This will allow to quickly build an email. - how can we access to an image reference inside the template ?
one more thing : The send method should take an optional argument to set the default mail gateway. (think like database connection definition)
> +1 for putting this in factories.yml and extending sfContext to accept > custom factories from plugins and the project.
> It's my understanding the 2.0 service container will be flexible in > this way (per Fabien's presentation at sf camp), so this change to > sfContext would also serve forward compatibility.
> Kris
> On Sep 15, 2008, at 8:25 AM, "Nicolas Perriault" > <nperria...@gmail.com> wrote:
>> On Mon, Sep 15, 2008 at 3:17 PM, Thomas Rabaix <thomas.rab...@gmail.com >> > wrote:
>>> My assumptions : sf1.0 has a nice way to handle mail : easy to use >>> and >>> quickly understanble by everyone. Agree ?
>> I don't like that much the "all-in-the-controller" approach, even if >> it's true that you can move the code into another protected method, >> even in a parent stub class (actually that's what I've done in >> symfonians.net, but I'm not happy with it, even if it works).
>>> $this = Controller
>>> $this->sendMail(array( >>> 'mailClass' => 'sfMail', >>> 'action' => 'module/mail', >>> 'template' => 'my_template', >>> 'layout' => ''email_layout', >>> 'vars' => array( >>> 'invoice' => $invoice, >>> 'file' => new sfFile(), // can generate a unique identifier for >>> the file >>> ); >>> ));
>>> of course some of this value could be guessed by the sendMail or >>> defined in a yml file.
>> With what I've in mind, mail sending in your action would be quite >> similar to:
>> $email = new InvoiceEmail(); >> $email->setVars(array('invoice' => $invoice)); >> // may be $email->addVar('invoice', $invoice); >> $email->send();
>> And in InvoiceEmail.class.php
>> class InvoiceEmail extends BaseEmail >> { >> public function configure() >> { >> $this->setHtmlContentResolver(sfEmail::FROM_ACTION, array('mail', >> 'invoice')); >> $this->setTextContentResolver(sfEmail::FROM_ACTION, array('mail', >> 'invoice'), array('sf_format' => 'txt')); // notice the sf_format >> trick >> // or $this->contentResolvers['text/html'] = new >> sfEmailTemplateAction('mail', 'module'); ?
>> Two templates should then exist in the mail module templates folder, >> invoiceSuccess.php and invoiceSuccess.txt.php. Okay, I don't like the >> set*ContentResolver method calls but I guess that's why I'm asking for >> better ideas here ;-)
>> The idea here is to move the email logic from the controllers to >> something apart, as what has been done with forms.
>>> As the sendMail is not welcome anymore in the controller, a mail >>> class >>> (let's say sfMailComponent) can register to listen [1] to the >>> controller.method_not_found event, and retrieve 'sendMail' call.
>> Way hacky, isn't it? I don't know. People, express yourself :-)
On Mon, Sep 15, 2008 at 5:59 PM, Thomas Rabaix <thomas.rab...@gmail.com> wrote: > I am almost ok with this method, but ... > - the set*ContentResolver are ... quite complicated, can it be more > magic, or optional ?
Please make suggestions :P Maybe François can help here?
> - this should me an sfMail class which handle array as constructor. > This will allow to quickly build an email.
Yep, why not
> - how can we access to an image reference inside the template ?
So using a symfony template, just pass the image attachment to the template and echo it as the source of the image tag (like the 1.0 way)
> one more thing : > The send method should take an optional argument to set the default > mail gateway. (think like database connection definition)
Yes, good idea indeed. We should be able to define an optional pool of mailers in the factories.yml config. I saw something similar in the original SwiftMailer lib, I'll check it out.
I already shared my plugin with you, but I'll still share my thoughts for the rest of you. I actually think, the way Swift works is great and I would not move away too far from its API, otherwise you restrict its usage too much.
I would prefer the following API for sending mails:
$mailer = new sfMailer(new Swift_Connection_SMTP('smtp.host.tld')); // or using YAML $mailer = new sfMailer(new sfConfiguredMailConnection('connnection_label')); // or even easier $mailer = new sfMailer('connection_label'); // or, if the connection is named 'default' $mailer = new sfMailer();
Here you still have the flexibility of using any of the Swift connections, and still you can use a very short syntax if the connection details are configured in a YAML file. Note that sfMailer should extend Swift to inherit all its capabilities.
Sending a mail would be easy as well:
$message = new sfMail('My subject', 'My body'); // or using a partial as body $message = new sfMail('My subject', 'module/partial_name'); // or sending an HTML mail $message = new sfMail('My subject', 'module/partial_name', 'text/html');
Once again, $message extends Swift_Message to inherit all capabilities. For everything that is non-symfony specific, users can read the good documentation of Swift. Its API is very easy to use anyway, so I think introducing a different API here makes no sense.
if ('dev' === $env) { $mailer = new sfMailer(new Swift_Connection_Native()); } else if ('prod' === $env) { $mailer = new sfMailer(new Swift_Connection_SMTP('smtp.host.tld')); }
It's gonna be hard to maintain... ;-)
> // or using YAML > $mailer = new sfMailer(new sfConfiguredMailConnection('connnection_label')); > // or even easier > $mailer = new sfMailer('connection_label'); > // or, if the connection is named 'default' > $mailer = new sfMailer();
Yes, but I think beeing able to configure this in the factories.yml would be really interesting, and easy. Also, I find a bit strange to define a mailer directly in a controller, I would personnaly prefer if you just set the connection name you want to use only if you need to:
$message = new sfEmail(); $message->send(); // will use the default connection for current env // or $message->useMailer('connection_label'); // yeah, this one's defined in the factories.yml file $message->send();
> Here you still have the flexibility of using any of the Swift > connections, and still you can use a very short syntax if the > connection details are configured in a YAML file.
The YAML configuration array will be extracted from the factories.yml, as the example I posted in my first message shows :-)
> Note that sfMailer should extend Swift to inherit all its capabilities.
That's what I'm currently planning to do, but this will end by strongly coupling Swift and symfony. I don't see any danger right now, though.
> Sending a mail would be easy as well:
> $message = new sfMail('My subject', 'My body'); > // or using a partial as body > $message = new sfMail('My subject', 'module/partial_name'); > // or sending an HTML mail > $message = new sfMail('My subject', 'module/partial_name', 'text/html');
Well, how can you use the same action to handle both html and plain text templates with your last example? How do you use a component or partial template as a mail template source? If all is magic, how are you defining the template names to load? Maybe I'm missing something? Maybe I'm wrong here, but I think users should be able to send emails whith any template source, including symfony ones but others as well:
- using a module/action and its associated templates (and have email layout handling) - using a component - using a partial - defining a callback - using a simple string as a source - using file? - etc?
If everybody agree that every single developer will ever only have the need to send emails using actions rendering as templates, well, fine... But I'm not so sure. What do others think?
> $mailer->send($message, 'recipi...@domain.tld', '...@home.tld'); > Once again, $message extends Swift_Message to inherit all > capabilities. For everything that is non-symfony specific, users can > read the good documentation of Swift. Its API is very easy to use > anyway, so I think introducing a different API here makes no sense.
I agree. But the tricky part to me is to offer users a simple but abstract way to define what will gonna be the source used for templates, and right now Swift doesn't provide the feature... Or maybe am I missing something?
One more time, this is the right time for making a decision (symfony 1.2 is planned for october), so let's discuss all together :)
Thanks for the extensive and interesting reply. I'll try to answer your questions below.
>> $mailer = new sfMailer(new Swift_Connection_SMTP('smtp.host.tld'));
> Here, how can you manage the environment? Like this?
You don't. Passing a native Swift connection is just an option of the many I provided as example.
As shown in my example, sfConfiguredMailConnection would take care of reading a YAML file, the factories.yml or whatever else to gain information about a given named connection. This way you can use multiple mail connections in the same environment, different connections in different environments or whatever you like.
Substituting the constructed object by its connection name or using the 'default' connection at all would result in a very short syntax. Still you would be able to use all of Swift's features, which is really important IMO if you don't want to enter maintenance hell.
> Also, I find a bit strange to define a mailer directly in a controller, I would personnaly prefer if > you just set the connection name you want to use only if you need to.
I agree a bit. One solution would be to provide a global sfMailer like it's done with the sfDatabaseManager. On the other hand,
$mailer = new sfMailer();
really is not much to write, pretty straight forward IMO and gives you a lot of the flexibility provided by Swift. I'm not sure whether dropping that flexibility would compensate for the cost of this line.
>> Note that sfMailer should extend Swift to inherit all its capabilities.
> That's what I'm currently planning to do, but this will end by > strongly coupling Swift and symfony. I don't see any danger right now, > though.
Do you think that's a bad point? I actually don't. Swift will be bundled with symfony anyway, and it's a really feature rich library as I've seen so far. I'd say go for it.
If the need should arise to make the plugin compatible with other mail libraries (which I doubt) there'd still remain the possibility to rename it to sfSwiftMailerPlugin and create a plugin with the same API for that library (as with sfPropelPlugin vs. sfDoctrinePlugin).
> Well, how can you use the same action to handle both html and plain > text templates with your last example?
Swift does provide that opportunity. You can just use two different partials for it.
Usually this is done using message parts, but you could either inherit your own message part class:
> How do you use a component or > partial template as a mail template source? If all is magic, how are > you defining the template names to load? Maybe I'm missing something? > Maybe I'm wrong here, but I think users should be able to send emails > whith any template source, including symfony ones but others as well:
Regarding components/actions/partials: I think, mails itself usually don't have any logic attached to them. The only logic evolves around sending the mail. That's why I used partials for mail contents. You can store them in any module (or in the global templates dir), so modifying them together with the other templates is easy. You can have any format in the partial, pass variables and have loops in them. I consider using components or actions for that a bad practice.
> - using a module/action and its associated templates (and have email > layout handling) > - using a component
See above. A valid point is the layout stuff, but is this needed in emails?
> - defining a callback
By allowing defining a callback as the second argument of sfMail.
$message = new sfMail('My subject', array('class', 'callback'));
> - using file?
By allowing a file as the second argument of sfMail.
$message = new sfMail('My subject', '/my/file/path');
If that's too much magic, create different subclasses of sfMail or let the application developer define his own subclasses. Since sfMailer::send() allows sending of any class inheriting Swift_Message, you don't have any limits really.
Don't get me wrong, I totally agree about making sending mails easy. I just would like to avoid making everything that the plugin developer doesn't think of unnecessarily complex.
What is your use case for creating the complex template logic (layouting, action templates...) that you are proposing?
I suggest that you create a wiki page where you describe possible use cases of this plugin together with possible implementations (similar to Francois' DDD :-)
On Tue, Sep 16, 2008 at 8:42 PM, Bernhard Schussek <bschus...@gmail.com> wrote: > I suggest that you create a wiki page where you describe possible use > cases of this plugin together with possible implementations (similar > to Francois' DDD :-)
$email = new sfEmail();
// then
$email->setHtmlTemplate(new sfEmailTemplateAction('mymodule','myaction');
// or
$email->setTextTemplate(new sfEmailTemplatePartial('mymodule', 'mypartial');
// or
$email->setTextTemplate(new sfEmailTemplateComponent('mymodule',
'mycomponent');
// or
$email->setTextTemplate(new sfEmailTemplateString('hello world'));
// and maybe
$email->setTemplate('text/html', new sfEmailTemplateString('hello world'));
// why not
$email->setTemplate('application/pdf', new
sfEmailTemplateFile('/tmp/toto.pdf');
// there could be also sfEmailTemplateCallback() and so on
// then
$email->send();
Is powerful but quite verbose. A slightly less verbose way would be to
use class constants, with sensible defaults. Plus the method names
(`setTextTemplate`) are less familiar than names referring to email
parts like `setBody()`:
$email = new sfEmail();
// then
$email->setHtmlBody('mymodule/myaction', sfEmail::ACTION);
// or
$email->setBody('mymodule/mypartial', sfEmail::PARTIAL);
// or
$email->setBody('mymodule/mycomponent', sfEmail::COMPONENT);
// or
$email->setBody('hello world'); // sfEmail::STRING is default
// and maybe
$email->setBodyPart('text/html', '<p>hello world</p>', sfEmail::STRING);
// why not
$email->setBodyPart('application/pdf', '/tmp/toto.pdf', sfEmail::FILE);
// there could be also sfEmail::CALLBACK and so on
But in the long run, this is still too much verbose, since it's longer
to learn and write than something much more natural:
$email = new sfEmail();
// then
$email->setHtmlBody(get_action('mymodule/myaction')); // well,
get_action() is still to be written...
// or
$email->setBody(get_partial('mymodule/mypartial'));
// or
$email->setBody(get_component('mymodule/mycomponent'));
// or
$email->setBody('hello world');
// and maybe
$email->setBodyPart('text/html', '<p>hello world</p>');
// why not
$email->setBodyPart('application/pdf', file_get_contents('/tmp/toto.pdf'));
// there could be also call_user_func() and so on
So my question is: Shouldn't we work on a reusable syntax for
get_partial() that is more OO and that could be learnt once and
injected everywhere, rather than introducing many new classes that
will only contain a few lines of code but that may prove longer to
learn?
My 2c.
François
2008/9/17 Nicolas Perriault <nperria...@gmail.com>:
> On Tue, Sep 16, 2008 at 8:42 PM, Bernhard Schussek <bschus...@gmail.com> wrote:
>> I suggest that you create a wiki page where you describe possible use
>> cases of this plugin together with possible implementations (similar
>> to Francois' DDD :-)
Also, I really think a good email utility should natively allow for
easy "rich content", so I think that an `embedded_image()` helper
similar to the one exposed in
http://www.symfony-project.org/snippets/snippet/27 is a good addition.
> $email = new sfEmail();
> // then
> $email->setHtmlTemplate(new sfEmailTemplateAction('mymodule','myaction');
> // or
> $email->setTextTemplate(new sfEmailTemplatePartial('mymodule', 'mypartial');
> // or
> $email->setTextTemplate(new sfEmailTemplateComponent('mymodule',
> 'mycomponent');
> // or
> $email->setTextTemplate(new sfEmailTemplateString('hello world'));
> // and maybe
> $email->setTemplate('text/html', new sfEmailTemplateString('hello world'));
> // why not
> $email->setTemplate('application/pdf', new
> sfEmailTemplateFile('/tmp/toto.pdf');
> // there could be also sfEmailTemplateCallback() and so on
> // then
> $email->send();
> Is powerful but quite verbose. A slightly less verbose way would be to
> use class constants, with sensible defaults. Plus the method names
> (`setTextTemplate`) are less familiar than names referring to email
> parts like `setBody()`:
> $email = new sfEmail();
> // then
> $email->setHtmlBody('mymodule/myaction', sfEmail::ACTION);
> // or
> $email->setBody('mymodule/mypartial', sfEmail::PARTIAL);
> // or
> $email->setBody('mymodule/mycomponent', sfEmail::COMPONENT);
> // or
> $email->setBody('hello world'); // sfEmail::STRING is default
> // and maybe
> $email->setBodyPart('text/html', '<p>hello world</p>', sfEmail::STRING);
> // why not
> $email->setBodyPart('application/pdf', '/tmp/toto.pdf', sfEmail::FILE);
> // there could be also sfEmail::CALLBACK and so on
> But in the long run, this is still too much verbose, since it's longer
> to learn and write than something much more natural:
> $email = new sfEmail();
> // then
> $email->setHtmlBody(get_action('mymodule/myaction')); // well,
> get_action() is still to be written...
> // or
> $email->setBody(get_partial('mymodule/mypartial'));
> // or
> $email->setBody(get_component('mymodule/mycomponent'));
> // or
> $email->setBody('hello world');
> // and maybe
> $email->setBodyPart('text/html', '<p>hello world</p>');
> // why not
> $email->setBodyPart('application/pdf', file_get_contents('/tmp/toto.pdf'));
> // there could be also call_user_func() and so on
> So my question is: Shouldn't we work on a reusable syntax for
> get_partial() that is more OO and that could be learnt once and
> injected everywhere, rather than introducing many new classes that
> will only contain a few lines of code but that may prove longer to
> learn?
> My 2c.
> François
> 2008/9/17 Nicolas Perriault <nperria...@gmail.com>:
>> On Tue, Sep 16, 2008 at 8:42 PM, Bernhard Schussek <bschus...@gmail.com> wrote:
>>> I suggest that you create a wiki page where you describe possible use
>>> cases of this plugin together with possible implementations (similar
>>> to Francois' DDD :-)
<francois.zanino...@symfony-project.com> wrote: > $email = new sfEmail(); > // then > $email->setHtmlBody(get_action('mymodule/myaction')); // well, > get_action() is still to be written... > // or > $email->setBody(get_partial('mymodule/mypartial')); > // or > $email->setBody(get_component('mymodule/mycomponent')); > // or > $email->setBody('hello world'); > // and maybe > $email->setBodyPart('text/html', '<p>hello world</p>'); > // why not > $email->setBodyPart('application/pdf', file_get_contents('/tmp/toto.pdf')); > // there could be also call_user_func() and so on
My proposal of (see wiki page):
$email->setTemplate('text/html', new sfEmailTemplateAction('mail', 'foo'));
is quite similar, isn't it? And it will allow to set vars for the whole email class and to having to pass them every time like this:
> So my question is: Shouldn't we work on a reusable syntax for > get_partial() that is more OO and that could be learnt once and > injected everywhere, rather than introducing many new classes that > will only contain a few lines of code but that may prove longer to > learn?
The sfEmailTemplateBase derived class would share the same really simple interface, but you might be right there, I don't know. What do others think? Express yourselves!
2008/9/17 Nicolas Perriault <nperria...@gmail.com>:
> And it will allow to set vars for the > whole email class and to having to pass them every time like this:
Not sure I get you.
Does in mean that you define one single action (mail/foo), and symfony (1.1) uses its multiformat support to render both a text and an HTML part? That might be possible if the templates dir of the mail module looked like:
That wasn't explicit in your RFC, so I'm not sure.
My proposal of using the already available partial/component helpers (or an OO equivalent) decouples the sfMail class from the symfony view layer and relies on current knowledge, without introducing new class constants or classes. If the tradeoff is to have to define explicitely both parts and set a variable before to give html and text parts the same array of arguments (that's two lines of code), that's a price I'm willing to pay.
You are already going way further than what Django or Rails propose. I think KISS is the objective here.
On Wed, Sep 17, 2008 at 10:58 AM, Francois Zaninotto
<francois.zanino...@symfony-project.com> wrote: >> And it will allow to set vars for the >> whole email class and to having to pass them every time like this:
> Not sure I get you.
I mean, how do you pass dynamic fields to your partials? The get_partial() functions accept an array of variables to render the template:
function get_partial($templateName, $vars = array());
This is important to me because you usually will want to use the same vars for both the text/plain and the text/html parts (and why not also for a dynamically generated application/pdf one)?
> Does in mean that you define one single action (mail/foo), and symfony > (1.1) uses its multiformat support to render both a text and an HTML > part? That might be possible if the templates dir of the mail module > looked like:
Yes, that's what I proposed in my first message, but I don't know if it's not a bit too much magic?
> My proposal of using the already available partial/component helpers > (or an OO equivalent) decouples the sfMail class from the symfony view > layer and relies on current knowledge, without introducing new class > constants or classes. > If the tradeoff is to have to define explicitely > both parts and set a variable before to give html and text parts the > same array of arguments (that's two lines of code), that's a price I'm > willing to pay. > You are already going way further than what Django or Rails propose. I > think KISS is the objective here.
If everybody agree on this, we can just provide the environment handling system using the factories.yml file, and let the users choose their way to retrieve body contents, using get_partial() and use use the existing SwiftMailer API...