<h3 dir="auto">The problem</h3>
<p dir="auto">In my opinion, the current solution to <a href="http://symfony.com/doc/current/cookbook/email/email.html" rel="nofollow">send emails with Symfony</a> is too "low level". (Related article: <a href="http://christoph-hautzinger.de/send-mails-in-symfony/" rel="nofollow">Send mails in Symfony in a way that does not suck!</a>)</p>
<p dir="auto">In my experience, most Symfony applications want to render a Twig template when sending an email. That's why FOSUserBundle includes the <a href="https://github.com/FriendsOfSymfony/FOSUserBundle/blob/92d5624f2a29e74d8fd9d2a94d7ed7998a8c2809/Mailer/TwigSwiftMailer.php">TwigSwiftMailer</a> and that's why looking for <code class="notranslate">Swift_Message::newInstance</code> in GitHub gives you <a href="https://github.com/search?p=1&amp;q=Swift_Message%3A%3AnewInstance&amp;ref=searchresults&amp;type=Code&amp;utf8=%E2%9C%93">thousands of results</a> with lots of shortcut methods to render a template.</p>
<h3 dir="auto">The solution</h3>
<p dir="auto">I propose to create a new <code class="notranslate">renderAndSend()</code> method for the <code class="notranslate">mailer</code> service. It would work as follows:</p>
<ol dir="auto">
<li>Simplest case (fixed 'to' and 'from' email addresses):</li>
</ol>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('mailer')-&gt;renderAndSend('email/notification.html.twig', array(
    'event' =&gt; $event,
));"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'mailer'</span>)-&gt;<span class="pl-en">renderAndSend</span>(<span class="pl-s">'email/notification.html.twig'</span>, <span class="pl-en">array</span>(
    <span class="pl-s">'event'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>event</span>,
));</pre></div>
<div class="highlight highlight-text-html-twig notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{# app/Resources/views/email/notification.html.twig #}
{% block to 'user@example.com' %}
{% block from 'alerts@example.com' %}
{% block subject %}[{{ 'now'|date(d/m/Y H:i:s) }}] {{ event.title[:128] }}{% endblock %}

{% block body %}
    ...
    &lt;div&gt;{{ event.content }}&lt;/div&gt;
    ...
{% endblock %}"><pre class="notranslate"><span class="pl-c"><span class="pl-c">{#</span> app/Resources/views/email/notification.html.twig <span class="pl-c">#}</span></span>
{% <span class="pl-k">block</span> <span class="pl-smi">to</span> <span class="pl-s"><span class="pl-pds">'</span>user@example.com<span class="pl-pds">'</span></span> %}
{% <span class="pl-k">block</span> <span class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>alerts@example.com<span class="pl-pds">'</span></span> %}
{% <span class="pl-k">block</span> <span class="pl-smi">subject</span> %}[{{ <span class="pl-s"><span class="pl-pds">'</span>now<span class="pl-pds">'</span></span><span class="pl-k">|</span><span class="pl-c1">date</span>(d/m/Y <span class="pl-smi">H</span>:<span class="pl-smi">i</span>:<span class="pl-smi">s</span>) }}] {{ <span class="pl-smi">event</span>.<span class="pl-smi">title</span>[:<span class="pl-c1">128</span>] }}{% <span class="pl-k">endblock</span> %}

{% <span class="pl-k">block</span> <span class="pl-smi">body</span> %}
    ...
    &lt;<span class="pl-ent">div</span>&gt;{{ <span class="pl-smi">event</span>.<span class="pl-smi">content</span> }}&lt;/<span class="pl-ent">div</span>&gt;
    ...
{% <span class="pl-k">endblock</span> %}</pre></div>
<ol start="2" dir="auto">
<li><code class="notranslate">{% block body %}</code> is a shortcut to set the HTML content. If you need different contents for HTML and TXT, use these blocks instead:</li>
</ol>
<div class="highlight highlight-text-html-twig notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{% block body_html %} ... {% endblock %}
{% block body_text %} ... {% endblock %}"><pre class="notranslate">{% <span class="pl-k">block</span> <span class="pl-smi">body_html</span> %} ... {% <span class="pl-k">endblock</span> %}
{% <span class="pl-k">block</span> <span class="pl-smi">body_text</span> %} ... {% <span class="pl-k">endblock</span> %}</pre></div>
<ol start="3" dir="auto">
<li>When the <code class="notranslate">$to</code> or <code class="notranslate">$from</code> email addresses are dynamic, you can pass them in the parameters array:</li>
</ol>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('mailer')-&gt;renderAndSend('email/notification.html.twig', array(
    'event' =&gt; $event,
    'to' =&gt; 'user@example.com',
    'from' =&gt; 'alerts@example.com',
));"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'mailer'</span>)-&gt;<span class="pl-en">renderAndSend</span>(<span class="pl-s">'email/notification.html.twig'</span>, <span class="pl-en">array</span>(
    <span class="pl-s">'event'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>event</span>,
    <span class="pl-s">'to'</span> =&gt; <span class="pl-s">'user@example.com'</span>,
    <span class="pl-s">'from'</span> =&gt; <span class="pl-s">'alerts@example.com'</span>,
));</pre></div>
<p dir="auto">Then you don't need to define the <code class="notranslate">to</code> and <code class="notranslate">from</code> blocks:</p>
<div class="highlight highlight-text-html-twig notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{# app/Resources/views/email/notification.html.twig #}
{% block subject %}[{{ 'now'|date(d/m/Y H:i:s) }}] {{ event.title[:128] }}{% endblock %}

{% block body %}
    ...
{% endblock %}"><pre class="notranslate"><span class="pl-c"><span class="pl-c">{#</span> app/Resources/views/email/notification.html.twig <span class="pl-c">#}</span></span>
{% <span class="pl-k">block</span> <span class="pl-smi">subject</span> %}[{{ <span class="pl-s"><span class="pl-pds">'</span>now<span class="pl-pds">'</span></span><span class="pl-k">|</span><span class="pl-c1">date</span>(d/m/Y <span class="pl-smi">H</span>:<span class="pl-smi">i</span>:<span class="pl-smi">s</span>) }}] {{ <span class="pl-smi">event</span>.<span class="pl-smi">title</span>[:<span class="pl-c1">128</span>] }}{% <span class="pl-k">endblock</span> %}

{% <span class="pl-k">block</span> <span class="pl-smi">body</span> %}
    ...
{% <span class="pl-k">endblock</span> %}</pre></div>
<ol start="4" dir="auto">
<li>These are the special Twig blocks:</li>
</ol>
<div class="highlight highlight-text-html-twig notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{% block to %} ... {% endblock %}
{% block from %} ... {% endblock %}
{% block subject %} ... {% endblock %}

{% block body %} ... {% endblock %}

{% block body_html %} ... {% endblock %}
{% block body_text %} ... {% endblock %}"><pre class="notranslate">{% <span class="pl-k">block</span> <span class="pl-smi">to</span> %} ... {% <span class="pl-k">endblock</span> %}
{% <span class="pl-k">block</span> <span class="pl-k">from</span> %} ... {% <span class="pl-k">endblock</span> %}
{% <span class="pl-k">block</span> <span class="pl-smi">subject</span> %} ... {% <span class="pl-k">endblock</span> %}

{% <span class="pl-k">block</span> <span class="pl-smi">body</span> %} ... {% <span class="pl-k">endblock</span> %}

{% <span class="pl-k">block</span> <span class="pl-smi">body_html</span> %} ... {% <span class="pl-k">endblock</span> %}
{% <span class="pl-k">block</span> <span class="pl-smi">body_text</span> %} ... {% <span class="pl-k">endblock</span> %}</pre></div>
<ol start="5" dir="auto">
<li>If both the Twig block and the parameter are given, the block is used. This allows to use the following:</li>
</ol>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('mailer')-&gt;renderAndSend('email/notification.html.twig', array(
    'event' =&gt; $event,
    'to' =&gt; 'user@example.com',
    'user' =&gt; $user,
));"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'mailer'</span>)-&gt;<span class="pl-en">renderAndSend</span>(<span class="pl-s">'email/notification.html.twig'</span>, <span class="pl-en">array</span>(
    <span class="pl-s">'event'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>event</span>,
    <span class="pl-s">'to'</span> =&gt; <span class="pl-s">'user@example.com'</span>,
    <span class="pl-s">'user'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>user</span>,
));</pre></div>
<div class="highlight highlight-text-html-twig notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{# app/Resources/views/email/notification.html.twig #}
{% block to %}{{ user.name }} &lt;{{ to }}&gt;{% endblock %}
..."><pre class="notranslate"><span class="pl-c"><span class="pl-c">{#</span> app/Resources/views/email/notification.html.twig <span class="pl-c">#}</span></span>
{% <span class="pl-k">block</span> <span class="pl-smi">to</span> %}{{ <span class="pl-smi">user</span>.<span class="pl-smi">name</span> }} <span class="pl-ii">&lt;</span>{{ <span class="pl-smi">to</span> }}&gt;{% <span class="pl-k">endblock</span> %}
...</pre></div>
<ol start="6" dir="auto">
<li>The <code class="notranslate">sendAndRender()</code> method would define a third optional parameter to set the file attachments:</li>
</ol>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('mailer')-&gt;renderAndSend(
    'email/notification.html.twig',
    array('event' =&gt; $event),
    array(
        $this-&gt;container-&gt;getParameter('log_dir').'/traces/'.$event-&gt;getId(),
        $this-&gt;container-&gt;getParameter('log_dir').'/console_output/'.$event-&gt;getId(),
    )
);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'mailer'</span>)-&gt;<span class="pl-en">renderAndSend</span>(
    <span class="pl-s">'email/notification.html.twig'</span>,
    <span class="pl-en">array</span>(<span class="pl-s">'event'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>event</span>),
    <span class="pl-en">array</span>(
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">container</span>-&gt;<span class="pl-en">getParameter</span>(<span class="pl-s">'log_dir'</span>).<span class="pl-s">'/traces/'</span>.<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getId</span>(),
        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">container</span>-&gt;<span class="pl-en">getParameter</span>(<span class="pl-s">'log_dir'</span>).<span class="pl-s">'/console_output/'</span>.<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getId</span>(),
    )
);</pre></div>
<p dir="auto">If the attachments array is associative, the keys are used as the file names:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;get('mailer')-&gt;renderAndSend(
    'email/notification.html.twig',
    array('event' =&gt; $event),
    array(
        'trace.txt'  =&gt; $this-&gt;container-&gt;getParameter('log_dir').'/traces/'.$event-&gt;getId(),
        'output.txt' =&gt; $this-&gt;container-&gt;getParameter('log_dir').'/console_output/'.$event-&gt;getId(),
    )
);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'mailer'</span>)-&gt;<span class="pl-en">renderAndSend</span>(
    <span class="pl-s">'email/notification.html.twig'</span>,
    <span class="pl-en">array</span>(<span class="pl-s">'event'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>event</span>),
    <span class="pl-en">array</span>(
        <span class="pl-s">'trace.txt'</span>  =&gt; <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">container</span>-&gt;<span class="pl-en">getParameter</span>(<span class="pl-s">'log_dir'</span>).<span class="pl-s">'/traces/'</span>.<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getId</span>(),
        <span class="pl-s">'output.txt'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">container</span>-&gt;<span class="pl-en">getParameter</span>(<span class="pl-s">'log_dir'</span>).<span class="pl-s">'/console_output/'</span>.<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getId</span>(),
    )
);</pre></div>
<p dir="auto">I see on all Symfony projects a "Mailer" class with this kind of function:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public function sendMessage($object, $fromEmail, $toEmail, $template, array $parameters = [])
    {
        try {
            $message = \Swift_Message::newInstance()
                -&gt;setSubject($object)
                -&gt;setFrom($fromEmail)
                -&gt;setTo($toEmail)
                -&gt;setBody($this-&gt;engine-&gt;render($template, $parameters), 'text/html')
            ;
            $this-&gt;mailer-&gt;send($message, $failedRecipients);
            if (!empty($failedRecipients)) {
                $this-&gt;logger-&gt;error('Mail was send but some recipients failed', [
                    'failed_recipients' =&gt; implode(' - ', $failedRecipients)
                ]);
            }
        } catch (\Swift_SwiftException $e) {
            $this-&gt;logger-&gt;error('Unable to send email', ['exception' =&gt; $e]);
        }
    }"><pre class="notranslate"><span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">sendMessage</span>(<span class="pl-s1"><span class="pl-c1">$</span>object</span>, <span class="pl-s1"><span class="pl-c1">$</span>fromEmail</span>, <span class="pl-s1"><span class="pl-c1">$</span>toEmail</span>, <span class="pl-s1"><span class="pl-c1">$</span>template</span>, <span class="pl-smi">array</span> <span class="pl-s1"><span class="pl-c1">$</span>parameters</span> = [])
    {
        <span class="pl-k">try</span> {
            <span class="pl-s1"><span class="pl-c1">$</span>message</span> = \<span class="pl-v">Swift_Message</span>::<span class="pl-en">newInstance</span>()
                -&gt;<span class="pl-en">setSubject</span>(<span class="pl-s1"><span class="pl-c1">$</span>object</span>)
                -&gt;<span class="pl-en">setFrom</span>(<span class="pl-s1"><span class="pl-c1">$</span>fromEmail</span>)
                -&gt;<span class="pl-en">setTo</span>(<span class="pl-s1"><span class="pl-c1">$</span>toEmail</span>)
                -&gt;<span class="pl-en">setBody</span>(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">engine</span>-&gt;<span class="pl-en">render</span>(<span class="pl-s1"><span class="pl-c1">$</span>template</span>, <span class="pl-s1"><span class="pl-c1">$</span>parameters</span>), <span class="pl-s">'text/html'</span>)
            ;
            <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">mailer</span>-&gt;<span class="pl-en">send</span>(<span class="pl-s1"><span class="pl-c1">$</span>message</span>, <span class="pl-s1"><span class="pl-c1">$</span>failedRecipients</span>);
            <span class="pl-k">if</span> (!empty(<span class="pl-s1"><span class="pl-c1">$</span>failedRecipients</span>)) {
                <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">logger</span>-&gt;<span class="pl-en">error</span>(<span class="pl-s">'Mail was send but some recipients failed'</span>, [
                    <span class="pl-s">'failed_recipients'</span> =&gt; implode(<span class="pl-s">' - '</span>, <span class="pl-s1"><span class="pl-c1">$</span>failedRecipients</span>)
                ]);
            }
        } <span class="pl-k">catch</span> (<span class="pl-smi"><span class="pl-smi">\<span class="pl-v">Swift_SwiftException</span></span></span> <span class="pl-s1"><span class="pl-c1">$</span>e</span>) {
            <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">logger</span>-&gt;<span class="pl-en">error</span>(<span class="pl-s">'Unable to send email'</span>, [<span class="pl-s">'exception'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>e</span>]);
        }
    }</pre></div>
<p dir="auto">This is weird to implement his own mailer when we already have a mailer service in Symfony framework.</p>
<p dir="auto">I propose to create a mailer service (we need to discuss on the name: should we replace the actual <code class="notranslate">mailer</code> ? create a <code class="notranslate">sf_mailer</code> service ?) with templating engine or only Twig (Symfony 3) and logger as dependency and - at least:</p>
<ul dir="auto">
<li>a function to send HTML email from twig template</li>
<li>a function to send txt emails</li>
</ul>
<p dir="auto">I'm in favor of a class which extends the Swiftmailer instance and add some functions and with a soft dependency on logger. This way:</p>
<ul dir="auto">
<li>we can keep <code class="notranslate">mailer</code> as service id</li>
<li>if we add logger as a dependency, we have more informations (new implementation)</li>
<li>the legacy implementations work (no BC)</li>
</ul>
<p dir="auto">What do you think ?</p>