2

I'm still learning on how to setup Swiftmailer as a service, I believe I have a working solution but need some help on how to call this in the controller.

How do I call this service in my controller? (service code, original code before service and service.yml below)

Edit: I am trying to call it like so:

$emailManager = $this->container->get('email_manager');

$content = $emailManager->sendMail($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText);

But am getting a undefined variable error:

Notice: Undefined variable: subject in /.../DefaultController.php line 58

EmailManager service

namespace Acme\EmailBundle\Service;

use Symfony\Component\HttpFoundation\RequestStack;


class EmailManager
{
private $request;
private $mailer;

public function __construct(RequestStack $requestStack, \Swift_Mailer $mailer)
{
    $this->request = $requestStack->getCurrentRequest();
    $this->mailer  = $mailer;
}

public function sendMail($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText)
{
    /* @var $mailer \Swift_Mailer */
    if(!$this->mailer->getTransport()->isStarted()){
        $this->mailer->getTransport()->start();
    }

    /* @var $message \Swift_Message */
    $message = $this->mailer->createMessage();
    $message->setSubject($subject);

    $message->setBody($bodyHtml, 'text/html');
    $message->addPart($bodyText, 'text/plain', 'UTF8');

    $message->addTo($recipientEmail, $recipientName);
    $message->setFrom( array('example@gmail.com' => 'Chance') );

    $this->mailer->send($message);
    $this->mailer->getTransport()->stop();
}
}

Original controller code for sending emails prior to putting it in as a service

/**
 * @Route("/", name="contact")
 * @Template("AcmeEmailBundle:Default:index.html.twig")
 */
public function contactAction(Request $request)
{
$form = $this->createForm(new ContactType());

if ($request->isMethod('POST')) {
    $form->submit($request);

    if ($form->isValid()) {
        $message = \Swift_Message::newInstance()
            ->setSubject($form->get('subject')->getData())
            ->setFrom($form->get('email')->getData())
            ->setTo('example@gmail.com')
            ->setBody(
                $this->renderView(
                    'AcmeEmailBundle:Default:index.html.twig',
                    array(
                        'ip' => $request->getClientIp(),
                        'name' => $form->get('name')->getData(),
                        'message' => $form->get('message')->getData()
                    )
                )
            );

        $this->get('mailer')->send($message);

        $request->getSession()->getFlashBag()->add('success', 'Your email has been sent! Thanks!');

        return $this->redirect($this->generateUrl('contact'));
    }
}

return array(
    'form' => $form->createView()
);
}

services.yml

services:
email_manager:
    class: Acme\EmailBundle\Service\EmailManager
    arguments: [@request_stack, @mailer]
    scope: request
chance
  • 315
  • 5
  • 15

1 Answers1

2

When your controller extends Controller like so

<?php

namespace Acme\DemoBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

//...

/**
 * DemoController
 */
class DemoController extends Controller
{
    // ...

}

You can access the services like this:

$emailManager = $this->container->get('email_manager');

You can then send your email like this:

$emailManager->sendEmail($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText);

Complete Overview

1 Create an Email Manager that will compose your emails and send them

<?php

namespace Acme\EmailBundle\Manager;

//...

/**
 * Composes and Sends emails
 */
class EmailManager 
{
    /**
     * The mailer
     *
     * @var \Swift_Mailer
     */
    protected $mailer;

    /**
     * The email address the mailer will send the emails from
     *
     * @var String
     */
    protected $emailFrom;


    /**
     * @param Request $mailer;
     */
    public function __construct(\Swift_Mailer $mailer, $emailFrom)
    {
        $this->mailer = $mailer;
        $this->emailFrom = $emailFrom;
    }

    /**
     * Compose email
     *
     * @param String $subject
     * @param String $recipientEmail
     * @param String $bodyHtml
     * @return \Swift_Message
     */
    public function composeEmail($subject, $recipientEmail, $bodyHtml)
    {
        /* @var $message \Swift_Message */
        $message = $this->mailer->createMessage();

        $message->setSubject($subject)
                ->setBody($bodyHtml, 'text/html')
                ->setTo($recipientEmail)
                ->setFrom($this->emailFrom);

        return $message;
    }


    /**
     * Send email
     *
     * @param \Swift_Message $message;
     */
    public function sendEmail(\Swift_Message $message)
    {
        if(!$this->mailer->getTransport()->isStarted()){
            $this->mailer->getTransport()->start();
        }

        $this->mailer->send($message);
        $this->mailer->getTransport()->stop();
    }


}

3 Declare it as a service

parameters:
    acme_email.email_from: example@gmail.com

services:
    email_manager:
        class: Acme\EmailBundle\Service\EmailManager
        arguments: [@mailer,%acme_email.email_from%]

]

4 Create a Form Handler that will handle your contact forms

<?php

namespace Acme\ContactBundle\Form\Handler;

use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Acme\EmailBundle\Manager\EmailManager;

/**
 * Handles Contact forms
 */
class ContactFormHandler
{

    /**
     * The request
     *
     * @var Symfony\Component\HttpFoundation\Request;
     */
    protected $request;

    /**
     * The Template Engine
     */
    protected $templating;

    /**
     * The email manager
     */
    protected $emailManager;

    /**
     * @param Request $request;
     * @param TwigEngine $templating
     * @param EmailManager $emailManager
     */
    public function __construct(Request $request, TwigEngine $templating, EmailManager $emailManager)
    {
        $this->request = $request;
        $this->templating =$templating;
        $this->emailManager = $emailManager;

    }

    /**
     * Processes the form with the request
     *
     * @param Form $form
     * @return Email|false 
     */
    public function process(Form $form)
    {
        if ('POST' !== $this->request->getMethod()) {
            return false;
        }

        $form->bind($this->request);

        if ($form->isValid()) {
            return $this->processValidForm($form);
        }

        return false;
    }

    /**
     * Processes the valid form, sends the email
     *
     * @param Form
     * @return EmailInterface The email sent
     */
    public function processValidForm(Form $form)
    {
        /** @var EmailInterface */
        $email = $this->composeEmail($form);

        /** Send Email */
        $this->emailManager->sendEmail($email);

        return $email;
    }

    /**
     * Composes the email from the form
     *
     * @param Form $form
     * @return \Swift_Message
     */
    public function composeEmail(Form $form)
    {
        $subject = $form->get('subject')->getData();
        $recipientEmail = $form->get('email')->getData();
        $bodyHTML = $this->templating->renderView(
                    'AcmeEmailBundle:Default:index.html.twig',
                    array(
                        'ip' => $this->request->getClientIp(),
                        'name' => $form->get('name')->getData(),
                        'message' => $form->get('message')->getData()
                    )
                );                

        /** @var \Swift_Message */
        return $this->emailManager->composeEmail($subject, $recipientEmail, $bodyHTML);
    }

}

3 Declare it as a service:

services:
    acme_contact.contact_form_handler:
        class:     Acme\ContactBundle\FormHandler\ContactFormHandler
        arguments: [@request, @templating, @email_manager]
        scope: request

4 Which gives you sthg short and sweet in your controller

/**
 * @Route("/", name="contact")
 * @Template("AcmeContactBundle:Default:index.html.twig")
 */
public function contactAction(Request $request)
{
    /** BTW, you could create a service to create the form too... */
    $form = $this->createForm(new ContactType());
    $formHandler = $this->container->get('acme_contact.contact_form_handler');

    if ($email = $formHandler->process($form)) {
         $this->setFlash('success', 'Your email has been sent! Thanks!');
         return $this->redirect($this->generateUrl('contact'));
     }

}
Dan
  • 9,935
  • 15
  • 56
  • 66
Mick
  • 30,759
  • 16
  • 111
  • 130
  • Thanks, Patt. What do I put in for the parameters to pass in? I'm getting a undefined variable error for $subject when I try to load the form. – chance Aug 28 '14 at 02:04
  • I am going to write something detailed in the [other question](http://stackoverflow.com/questions/25518215/symfony2-adding-swiftmailer-as-a-service), so that you can see how to reuse your code a bit better. In the mean time, try do do the best you can! – Mick Aug 28 '14 at 02:13
  • Please do, just unsure of how to call the added parts (recipientName, recipientEmail, bodyText) as it's not part of the original ContactType from file I'm using, it only consists of (name, email, subject and message) – chance Aug 28 '14 at 02:58
  • I have always used `$message = $this->createMessage('Welcome', 'to@email.com', 'fullname', $user->getEmail(), $fullName, $body); $this->sendEmail($message, 'user_form');` recipient name and email are almost always hardcoded when I use them. User form is the body. – B Rad Aug 28 '14 at 03:57
  • Updated my answer with precise instructions. Enjoy @chance. – Mick Aug 28 '14 at 13:36
  • @Patt Thanks for taking the time to show me step for step how to do this. Excellent, but am getting the following error when I submit the form: `Attempted to call method "renderView" on class "Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine" in /.../Acme/EmailBundle/Form/FormHandler/ContactFormHandler.php line 82.` renderView is being highlighted by PHP Storm as a method not found in class, do I need to use something for this to show up? – chance Aug 29 '14 at 04:06
  • @Patt Read this here: http://stackoverflow.com/questions/18895782/swift-mail-from-symfony-command then added in container into ContactFormHandler.php and called the render as such `$bodyHTML = $this->container->get('templating')->render(AcmeEmailBundle:Default:index.html.twig', ...` and now get the following error: `Variable "from" does not exist in AcmeEmailBundle:Default:index.html.twig at line 4` – chance Aug 29 '14 at 04:51
  • @Patt The original problem with renderView was resolved with using the container. Then came across a problem with missing variable 'form' due to using the form (index.html.twig) as the sendTo message format. Once I switched this to another twig file, I no longer needed the $form->createView() that I added to your code. Long story short, it works now! Thanks Patt, you've been instructmental in learning how to add this to a service. You've taken this to another level more than I originally planned for. Cheers! (may not understand it all, but I can study your code, until it makes more sense) – chance Aug 29 '14 at 05:21