Passed
Push — feature/unit-tests ( 16a1aa...e0ee05 )
by Daniel
05:07
created

UserMailer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 22
ccs 11
cts 11
cp 1
rs 9.9332
nc 1
nop 10
cc 1
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Silverback API Component Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentBundle\Mailer;
15
16
use Silverback\ApiComponentBundle\Entity\User\AbstractUser;
17
use Silverback\ApiComponentBundle\Exception\InvalidArgumentException;
18
use Silverback\ApiComponentBundle\Exception\MailerTransportException;
19
use Silverback\ApiComponentBundle\Exception\RfcComplianceException;
20
use Silverback\ApiComponentBundle\Url\RefererUrlHelper;
21
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
22
use Symfony\Component\HttpFoundation\RequestStack;
23
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
24
use Symfony\Component\Mailer\MailerInterface;
25
use Symfony\Component\Mime\Address;
26
use Symfony\Component\Mime\RawMessage;
27
28
/**
29
 * @author Daniel West <[email protected]>
30
 */
31
class UserMailer
32
{
33
    private MailerInterface $mailer;
34
    private RefererUrlHelper $refererUrlHelper;
35
    private RequestStack $requestStack;
36
    private string $websiteName;
37
    private string $defaultPasswordResetPath;
38
    private string $defaultChangeEmailVerifyPath;
39
    private bool $sendUserWelcomeEmailEnabled;
40
    private bool $sendUserEnabledEmailEnabled;
41
    private bool $sendUserUsernameChangedEmailEnabled;
42
    private bool $sendUserPasswordChangedEmailEnabled;
43
44 15
    public function __construct(
45
        MailerInterface $mailer,
46
        RefererUrlHelper $refererUrlHelper,
47
        RequestStack $requestStack,
48
        string $websiteName = 'Website Name',
49
        string $defaultPasswordResetPath = '/reset-password/{{ username }}/{{ token }}',
50
        string $defaultChangeEmailVerifyPath = '/verify-new-email/{{ username }}/{{ token }}',
51
        bool $sendUserWelcomeEmailEnabled = true,
52
        bool $sendUserEnabledEmailEnabled = true,
53
        bool $sendUserUsernameChangedEmailEnabled = true,
54
        bool $sendUserPasswordChangedEmailEnabled = true
55
    ) {
56 15
        $this->mailer = $mailer;
57 15
        $this->refererUrlHelper = $refererUrlHelper;
58 15
        $this->requestStack = $requestStack;
59 15
        $this->defaultPasswordResetPath = $defaultPasswordResetPath;
60 15
        $this->defaultChangeEmailVerifyPath = $defaultChangeEmailVerifyPath;
61 15
        $this->websiteName = $websiteName;
62 15
        $this->sendUserWelcomeEmailEnabled = $sendUserWelcomeEmailEnabled;
63 15
        $this->sendUserEnabledEmailEnabled = $sendUserEnabledEmailEnabled;
64 15
        $this->sendUserUsernameChangedEmailEnabled = $sendUserUsernameChangedEmailEnabled;
65 15
        $this->sendUserPasswordChangedEmailEnabled = $sendUserPasswordChangedEmailEnabled;
66 15
    }
67
68 7
    public function sendPasswordResetEmail(AbstractUser $user): void
69
    {
70 7
        $token = $user->getNewPasswordConfirmationToken();
71 7
        if (!$token) {
72 1
            throw new InvalidArgumentException('A new password confirmation token must be set to send the `password reset` email');
73
        }
74
75 6
        $userUsername = self::getUserUsername($user);
76 5
        $resetUrl = $this->pathToReferrerUrl(
77 5
            $token,
78
            $userUsername,
79 5
            'resetPath',
80 5
            $this->defaultPasswordResetPath
81
        );
82 5
        $email = $this->createEmailMessage(
83 5
            'Your password reset request',
84 5
            'user_forgot_password.html.twig',
85
            $user,
86
            [
87 5
                'reset_url' => $resetUrl,
88
            ]
89
        );
90 3
        $this->send($email);
91 2
    }
92
93 3
    public function sendChangeEmailConfirmationEmail(AbstractUser $user): void
94
    {
95 3
        $userUsername = self::getUserUsername($user);
96 2
        $verifyUrl = $this->getEmailConfirmationUrl($user, $userUsername);
97 1
        $email = $this->createEmailMessage(
98 1
            'Your password reset request',
99 1
            'user_verify_email.html.twig',
100
            $user,
101
            [
102 1
                'verify_url' => $verifyUrl,
103
            ]
104
        );
105 1
        $this->send($email);
106 1
    }
107
108 5
    public function sendUserWelcomeEmail(AbstractUser $user): void
109
    {
110 5
        if (!$this->sendUserWelcomeEmailEnabled) {
111
            return;
112
        }
113 5
        $userUsername = self::getUserUsername($user);
114
        try {
115 5
            $verifyUrl = $this->getEmailConfirmationUrl($user, $userUsername);
116 5
        } catch (InvalidArgumentException $exception) {
117
            // if we have not set the email verify token this will be thrown. this is an optional token though.
118 5
            $verifyUrl = null;
119
        }
120 5
        $email = $this->createEmailMessage(
121 5
            sprintf('Welcome to %s', $this->websiteName),
122 5
            'user_welcome.html.twig',
123
            $user,
124
            [
125 5
                'verify_url' => $verifyUrl,
126
            ]
127
        );
128 5
        $this->send($email);
129 5
    }
130
131
    public function sendUserEnabledEmail(AbstractUser $user): void
132
    {
133
        if (!$this->sendUserEnabledEmailEnabled) {
134
            return;
135
        }
136
        $email = $this->createEmailMessage(
137
            'Your account has been enabled',
138
            'user_enabled.html.twig',
139
            $user
140
        );
141
        $this->send($email);
142
    }
143
144
    public function sendUsernameChangedEmail(AbstractUser $user): void
145
    {
146
        if (!$this->sendUserUsernameChangedEmailEnabled) {
147
            return;
148
        }
149
        $email = $this->createEmailMessage(
150
            'Your username has been changed',
151
            'username_changed.html.twig',
152
            $user
153
        );
154
        $this->send($email);
155
    }
156
157
    public function sendPasswordChangedEmail(AbstractUser $user): void
158
    {
159
        if (!$this->sendUserPasswordChangedEmailEnabled) {
160
            return;
161
        }
162
        $email = $this->createEmailMessage(
163
            'Your password has been changed',
164
            'user_password_changed.html.twig',
165
            $user
166
        );
167
        $this->send($email);
168
    }
169
170 11
    private static function getUserEmail(AbstractUser $user): string
171
    {
172 11
        if (!($userEmail = $user->getEmailAddress())) {
173 1
            throw new InvalidArgumentException('The user must have an email address set to send them any email');
174
        }
175
176 10
        return $userEmail;
177
    }
178
179 14
    private static function getUserUsername(AbstractUser $user): string
180
    {
181 14
        if (!($userUsername = $user->getUsername())) {
182 2
            throw new InvalidArgumentException('The user must have a username set to send them any email');
183
        }
184
185 12
        return $userUsername;
186
    }
187
188 11
    private function createEmailMessage(string $subject, string $htmlTemplate, AbstractUser $user, array $context = [])
189
    {
190
        $defaultContext = [
191 11
            'user' => $user,
192 11
            'username' => self::getUserUsername($user),
193 11
            'website_name' => $this->websiteName,
194
        ];
195
196
        try {
197 11
            $toAddress = Address::fromString(self::getUserEmail($user));
198 2
        } catch (\Symfony\Component\Mime\Exception\RfcComplianceException $exception) {
199 1
            $exception = new RfcComplianceException($exception->getMessage());
200 1
            throw $exception;
201
        }
202
203 9
        return (new TemplatedEmail())
204 9
            ->to($toAddress)
205 9
            ->subject($subject)
206 9
            ->htmlTemplate('@SilverbackApiComponent/emails/' . $htmlTemplate)
207 9
            ->context(array_merge($defaultContext, $context));
208
    }
209
210 9
    private function send(RawMessage $message): void
211
    {
212
        try {
213 9
            $this->mailer->send($message);
214 1
        } catch (TransportExceptionInterface $exception) {
215 1
            $exception = new MailerTransportException($exception->getMessage());
216 1
            $exception->appendDebug($exception->getDebug());
217 1
            throw $exception;
218
        }
219 8
    }
220
221 7
    private function getEmailConfirmationUrl(AbstractUser $user, string $userUsername): string
222
    {
223 7
        $token = $user->getNewEmailVerificationToken();
224 7
        if (!$token) {
225 5
            throw new InvalidArgumentException('A new email verification token must be set to send the `email verification` email');
226
        }
227
228 2
        return $this->pathToReferrerUrl(
229 2
            $token,
230
            $userUsername,
231 2
            'verifyPath',
232 2
            $this->defaultChangeEmailVerifyPath
233
        );
234
    }
235
236 7
    private function pathToReferrerUrl(string $token, string $username, string $queryKey, string $defaultPath): string
237
    {
238 7
        $request = $this->requestStack->getMasterRequest();
239 7
        $path = $request ? $request->query->get($queryKey, $defaultPath) : $defaultPath;
240 7
        $path = str_replace(['{{ token }}', '{{ username }}'], [$token, $username], $path);
241
242 7
        return $this->refererUrlHelper->getAbsoluteUrl($path);
243
    }
244
}
245