Skip to content
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

Allow to use "symfony/mailer" within Mailer #1263

Merged
merged 1 commit into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UPGRADE-4.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ UPGRADE 4.x
UPGRADE FROM 4.x to 4.x
=======================

### Sonata\UserBundle\Mailer\Mailer

Passing an instance of `\Swift_Mailer` as argument 3 for `Sonata\UserBundle\Mailer\Mailer::__construct()`
is deprecated. Pass an instance of `Symfony\Component\Mailer\MailerInterface` instead.

### Dependencies

- "sonata-project/datagrid-bundle" is bumped from ^2.4 to ^3.0.
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
"sonata-project/datagrid-bundle": "^3.0.1",
"sonata-project/doctrine-extensions": "^1.10.1",
"sonata-project/form-extensions": "^0.1 || ^1.4",
"swiftmailer/swiftmailer": "^4.3 || ^5.0 || ^6.0",
"symfony/config": "^4.4",
"symfony/console": "^4.4",
"symfony/dependency-injection": "^4.4",
"symfony/form": "^4.4",
"symfony/framework-bundle": "^4.4",
"symfony/http-foundation": "^4.4",
"symfony/http-kernel": "^4.4",
"symfony/mailer": "^4.4 || ^5.1",
"symfony/mime": "^4.4.10 || ^5.1",
"symfony/options-resolver": "^4.4 || ^5.1",
"symfony/security-acl": "^3.0",
"symfony/security-core": "^4.4",
Expand Down
65 changes: 57 additions & 8 deletions src/Mailer/Mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

use FOS\UserBundle\Mailer\MailerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Mailer\MailerInterface as SymfonyMailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;

Expand All @@ -31,7 +33,9 @@ final class Mailer implements MailerInterface
private $twig;

/**
* @var \Swift_Mailer
* NEXT_MAJOR: Remove the support for `\Swift_Mailer` in this property.
*
* @var SymfonyMailerInterface|\Swift_Mailer
*/
private $mailer;

Expand All @@ -45,8 +49,28 @@ final class Mailer implements MailerInterface
*/
private $emailTemplate;

public function __construct(UrlGeneratorInterface $urlGenerator, Environment $twig, \Swift_Mailer $mailer, array $fromEmail, string $emailTemplate)
public function __construct(UrlGeneratorInterface $urlGenerator, Environment $twig, object $mailer, array $fromEmail, string $emailTemplate)
{
// NEXT_MAJOR: Remove the following 2 conditions and use `Symfony\Component\Mailer\MailerInterface` as argument declaration for `$mailer`.
if (!$mailer instanceof SymfonyMailerInterface && !$mailer instanceof \Swift_Mailer) {
throw new \TypeError(sprintf(
'Argument 3 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.',
__METHOD__,
SymfonyMailerInterface::class,
\Swift_Mailer::class,
\get_class($mailer)
));
}

if (!$mailer instanceof SymfonyMailerInterface) {
@trigger_error(sprintf(
'Passing other type than "%s" as argument 3 for "%s()" is deprecated since sonata-project/user-bundle 4.x'
.' and will be not supported in version 5.x.',
SymfonyMailerInterface::class,
__METHOD__
), E_USER_DEPRECATED);
}

$this->urlGenerator = $urlGenerator;
$this->twig = $twig;
$this->mailer = $mailer;
Expand All @@ -69,17 +93,42 @@ public function sendResettingEmailMessage(UserInterface $user): void
$renderedLines = preg_split('/\R/', trim($rendered), 2, PREG_SPLIT_NO_EMPTY);
$subject = array_shift($renderedLines);
$body = implode('', $renderedLines);
$message = (new \Swift_Message())
->setSubject($subject)
->setFrom($this->fromEmail)
->setTo((string) $user->getEmail())
->setBody($body);

$this->mailer->send($message);
// NEXT_MAJOR: Remove this condition.
if ($this->mailer instanceof \Swift_Mailer) {
$this->sendResettingEmailMessageWithSwiftMailer($user, $subject, $body);

return;
}

$fromName = current($this->fromEmail);
$fromAddress = current(array_keys($this->fromEmail));

$this->mailer->send(
(new Email())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using templateEmail to have direct support for twig using the template name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will not possible in a transparent way, since the Twig template is currently providing the subject and the body:

$renderedLines = preg_split('/\R/', trim($rendered), 2, PREG_SPLIT_NO_EMPTY);
        $subject = array_shift($renderedLines);
        $body = implode('', $renderedLines);

->from(sprintf('%s <%s>', $fromName, $fromAddress))
->to((string) $user->getEmail())
->subject($subject)
->html($body)
);
}

public function sendConfirmationEmailMessage(UserInterface $user): void
{
throw new \LogicException('This method is not implemented.');
}

/**
* NEXT_MAJOR: Remove this method.
*/
private function sendResettingEmailMessageWithSwiftMailer(UserInterface $user, string $subject, string $body): void
{
$this->mailer->send(
(new \Swift_Message())
->setSubject($subject)
->setFrom($this->fromEmail)
->setTo((string) $user->getEmail())
->setBody($body)
);
}
}
69 changes: 59 additions & 10 deletions tests/Mailer/MailerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sonata\UserBundle\Mailer\Mailer;
use Symfony\Component\Mailer\MailerInterface as SymfonyMailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\RouterInterface;
use Twig\Environment;

Expand All @@ -33,7 +35,7 @@ class MailerTest extends TestCase
private $templating;

/**
* @var \Swift_Mailer|MockObject
* @var SymfonyMailerInterface|MockObject
*/
private $mailer;

Expand All @@ -51,8 +53,10 @@ protected function setUp(): void
{
$this->router = $this->createMock(RouterInterface::class);
$this->templating = $this->createMock(Environment::class);
$this->mailer = $this->createMock(\Swift_Mailer::class);
$this->emailFrom = ['noreply@sonata-project.org'];
$this->mailer = $this->createMock(SymfonyMailerInterface::class);
$this->emailFrom = [
'noreply@sonata-project.org' => 'Sonata Project',
];
$this->template = 'foo';
}

Expand All @@ -71,7 +75,7 @@ public function testSendConfirmationEmailMessage(): void
*/
public function testSendResettingEmailMessage(string $template, string $subject, string $body): void
{
$user = $this->createMock(UserInterface::class);
$user = $this->createStub(UserInterface::class);
$user
->method('getConfirmationToken')
->willReturn('user-token');
Expand All @@ -89,18 +93,63 @@ public function testSendResettingEmailMessage(string $template, string $subject,
->with('foo', ['user' => $user, 'confirmationUrl' => '/foo'])
->willReturn($template);

$fromName = current($this->emailFrom);
$fromAddress = current(array_keys($this->emailFrom));

$email = (new Email())
->from(sprintf('%s <%s>', $fromName, $fromAddress))
->to((string) $user->getEmail())
->subject($subject)
->html($body);

$this->mailer->expects($this->once())
->method('send')
->willReturnCallback(function (\Swift_Message $message) use ($subject, $body): void {
$this->assertSame($subject, $message->getSubject());
$this->assertSame($body, $message->getBody());
$this->assertArrayHasKey($this->emailFrom[0], $message->getFrom());
$this->assertArrayHasKey('user@sonata-project.org', $message->getTo());
});
->with($this->equalTo($email));

$this->getMailer()->sendResettingEmailMessage($user);
}

/**
* NEXT_MAJOR: Remove this method.
*
* @group legacy
*
* @dataProvider emailTemplateData
*/
public function testSendResettingEmailMessageWithSwiftMailer(string $template, string $subject, string $body): void
{
$user = $this->createStub(UserInterface::class);
$user
->method('getConfirmationToken')
->willReturn('user-token');
$user
->method('getEmail')
->willReturn('user@sonata-project.org');

$this->router->expects($this->once())
->method('generate')
->with('sonata_user_admin_resetting_reset', ['token' => 'user-token'])
->willReturn('/foo');

$this->templating->expects($this->once())
->method('render')
->with('foo', ['user' => $user, 'confirmationUrl' => '/foo'])
->willReturn($template);

$swiftMailer = $this->createMock(\Swift_Mailer::class);

$swiftMailer->expects($this->once())
->method('send')
->with($this->callback(function (\Swift_Message $message) use ($subject, $body, $user): bool {
return $subject === $message->getSubject()
&& $body === $message->getBody()
&& $this->emailFrom === $message->getFrom()
&& \array_key_exists((string) $user->getEmail(), $message->getTo());
}));

(new Mailer($this->router, $this->templating, $swiftMailer, $this->emailFrom, $this->template))->sendResettingEmailMessage($user);
}

public function emailTemplateData(): array
{
return [
Expand Down