-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Proposal for #31: email component based on SwiftMailer #1049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
SwiftMailer applied as email solution.
Methods 'addText' and 'addHtml' added to \yii\email\BaseMessage.
Email message render functionality added.
|
what about putting the swift mailer into a yii extension and create a composer meta package for yii-mailer that is then provided by the desired backend? |
I do not know… I would like to have some email solution inside the core, not just as extension. But maybe this make sense. |
|
This new design looks promising to me. Below are my feedback: MessageInterface
BaseMailer
BaseMessage
Swift Mailer and Message
|
|
MessageInterface
Well, it has a problem… Swift_Message does not allow such behavior. If I use “Swift_Message::addPart()” another problems appear. If Message::setText() implementation will be following: Multiple invokes of it will add multiple content parts: Also if “Swift_Message::addPart()” is invoked the message will be “multipart alternative” even if it contains only single part.
I have started with such interface, but abandoned it later. It is unlikely someone will use “chaining” style to setup message, probably people will use property interface for it:
What it should return then?
Do you want these be added to the interface? In this case it can become very heavy. BaseMailer
While developing this solution I have think about inconsistences around “view” layer. I think it should be named “ViewContextInterface”, which could handle view name resolving (if it does not has alias via ‘@’) as well as context rendering (see \yii\base\View::render()). I will open separated issue when (and if) I compose a solution for it. Still I don’t want to drop “Mailer::viewResolver” property. I have separated this component to be able changing the way the email templates are searched without extending entire “Mailer” class. Then I just need to configure the mailer property. If I want to switch to another mailer like Amazon SES, I can still keep my ViewResolver without extra efforts.
At the moment we can always use “View::beginContent()”“View::endContent()”. I am not sure about layout: it makes sense for HTML content, but seems useless for subject.
Actually at the moment Mailer component is not mandatory. You may develop a mail solution via MessageInterface, which will NOT require any external mailer. So method ‘Mailer::createMessage()’ seems redundant. BaseMessage
I thought about this, but have to drop it, once I have realized “setBody” and “setText” have problems (see the first entry). Swift Mailer and Message
What about unit tests? Where they should be placed? |
I see. I checked the implementation of both SwiftMailer and Zend Mail. It seems to me we should add
It's fine we don't support this.
It's fine we don't add the getters. I just don't feel good when seeing write-only properties.
It returns a string representation of the message (may just call
The same requirement may exist for non-email view templates. This can be supported using a DB-backed
Adding
We still need to reconsider this because it doesn't fit very well in the process of composing a new message.
The
Good question. Probably |
'SwiftMailer' recomposed into extension.
'swiftmailer' extension unit tests fixed.
…attachContentAsFile()'
Method 'MailerInterface::createMessage()' added.
Interface 'ViewContextInterface' applied to BaseMailer.
…o use options, embed file methods added.
|
@psihius your one-liner example isn't correct and can be rendered in a very ugly way in some clients such as old MS Outlook. Also if you'll send text/plain with HTML it will be still rendered as plain text. If you'll send HTML only some clients will not render it at all (can't find an example now but I've encountered such behavior). |
|
@psihius, you can setup single HTML body using following code: Yii::$app->mail->message()
->from('from@domain')
->to('to@domain.com')
->subject('subject')
->html('Transaction <b>#XXXXX</b> was rejected...')
->send(); |
|
@samdark As I said, in some cases I just don't care - I know where it goes and will render fine no matter how I screw it up (well, except using some weird encoding) :) @klimov-paul ahh.., ok. That solves it all. Thanks. No more questions on this part. Although my brain just produced an interesting question: what happens if: Yii::$app->mail->message()
->from('from@domain')
->to('to@domain.com')
->subject('subject')
->html('Transaction <b>#XXXXX</b> was rejected...')
->renderHtml('some/template')
->send();or the html() and renderHtml are done in reverse order (same goes for text/renderText)? The latter call takes priority? |
|
@psihius, here is clarification:
Then there are 3 matching render* methods, which fill message body by view render result, applying layout:
Any “render*” method is a wrapper around corresponding setter method, so last call always take precedence over previous Yii::$app->mail->message()
->from('from@domain')
->to('to@domain.com')
->subject('subject')
->html('Transaction <b>#XXXXX</b> was rejected...')
->renderHtml('conatct', [...]) // override previous line
->send();However if you set both “html” and “text” the message will become “multipart alternative” containing both plain text and HTML body: Yii::$app->mail->message()
->from('from@domain')
->to('to@domain.com')
->subject('subject')
->html('Transaction <b>#XXXXX</b> was rejected...')
->text('You need HTML support client to view this message') // message becomes “multipart alternative”
->send(); |
|
Looks fine to me. |
You can just call @klimov-paul There are several problems with the latest design:
I still think we should support |
current implementation of View will unset all registered css, js and meta tags in |
Indeed 6 methods, as well as 2 different layouts – emails are not so simple as you may think. HTML body and plain text body are different values, each of them needs a rendering, so it is “*2”, then you insists on the ability to render both html and text at once, so we end up with 6 methods.
I always thought the “yii/mail” layer should do nothing about rendering views, but you have insistent it should. Now you are telling rendering inside “yii/mail” gone too far... Sorry I can’t feel the line, when it became this way. // Simple mail sending:
Yii::$app->message()
->from('from@domain.com')
->to('to@domain.com')
->subject('subject')
->text('content')
->send();
// Mail composition:
$mail = new ContactMail(['form' => $form]);
$mail->send();
I can rename them to “composeXyz()”.
Syntax $message->body(null, $text);At the beginning I was thinking about “htmlBody()/textBody()”, but looking at them I thought “Body” word is redundant. In email message context there could not be other ‘text’ or ‘html’.
“message” is shorter name, which matches the method functionality. What about method “yii\base\Widget::widget()”? As far as I know “widget” is a noun, or it is not?
That is why I have created “renderBody()” – it allows to render both “html” and “text” views at once using the same parameters: $message->renderBody(['text'=>'text/view', 'html'=>'html/view'], ['param1'=>'value1']);At the moment solution provides flexible interface: you can render only HTML, only plain text or both, just as you wish.
And why is that? |
Such as |
Can you tell me then, why neither SwiftMailer nor ZendEmail do NOT provide mail template solution build in? |
|
@klimov-paul I thought the answer to that question is obvious, no? Zend has loose coupling between components, so you have to render the template somehow, get it's rendered contents as a string and pass it to the mail component. Yii was always different in that regard, it does not do decoupled components just for the sake of it, it provides an API that if you may need to replace the standard one, it's easy to do. So I do not see why would you even ask this question. Because it's comfortable that way to use out of the box. UPD. Actually, I don't use Zend_Mail directly, I've created a helper, that has 2 public methods for sending e-mails: one as a view template, second as text and just pass params to it. Because, well, it's kind'a pita to write every time to render HTML body, then TEXT body, them set up all the headers, then select the proper transport based on the OS (Win/*nix), select the settings for the mailer for current project and finally send the e-mail :) |
|
@klimov-paul both ZF and Symfony approach is to prefer theory (decoupling) over practice (simpler API where possible). |
|
Personally I've got used to rendering views seaparately as I've used Zend Mailer w/ 1.1 but having API shortcut will be handy, I think. |
It's not an excuse to say our API has to be complex because email is complex. In my proposal, we would only need two methods: one for setting body directly, the other for building body via views (in Mailer, not Message). This is very clear, I think.
There's some confusion here. I'm saying
It's not totally unacceptable, if we all agree HTML body is needed in most cases. In rare cases when only text body is needed, such ugliness is tolerable, I think.
I'd say
You have a point here, but there's a subtle difference between widget and mail. For mail, it is Mailer that creates a message, while for widget it is the widget class itself and it is used in a context of echoing. Another thing is that widget has the variant |
Method 'yii\mail\MailerInterface::compose()' reworked allowing rendering message body.
…tml' to 'htmlBody'
|
Requested changes applied. Hopefully, I have not forgotten anything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no more renderText.
|
Looks great to me! Thank you for making the changes. You may go ahead and merge it. |
Proposal for #31: email component based on SwiftMailer
|
Thanks! |
Proposal for #31: email component based on SwiftMailer.
Migrated from #1011