Problem/Motivation
Allow creating a Symfony Mailer EmailBuilder plugin / policy that is not hardwired to a module or config entity?
Proposed resolution
I propose a fix as follows:
- Add a label to the EmailBuilder annotation, which can be omitted if the plugin ID is an entity type or module name
- Add a boolean setting
proxy
to the EmailBuilder annotation, which should be set for any builder proxied on behalf of another module
- Fix
EmailBuilderManager::processDefinition()
. The code to set $definition['provider']
is only needed for proxy definitions.
- Rename
EmailFactoryInterface::sendModuleEmail()
, to sendTypedEmail(string $type)
.
Original issue:
I have a Drupal project with various custom emails, using the hook_mail() logic from Drupal core.
I have enabled symfony_mailer_bc.module and they seem to work.
However, I am trying to change my custom code to use a native Symfony Mailer implementation, so I can disable symfony_mailer_bc.module in the long run.
But I'm struggling to find good examples / documentation.
I my module I have 2 separate email flows (with different params), so I am creating 2 EmailBuilder plugins:
namespace Drupal\ipv_waiting_list\Plugin\EmailBuilder;
use Drupal\commerce_product\Entity\ProductVariationInterface;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\MailerHelperTrait;
use Drupal\symfony_mailer\Processor\EmailBuilderBase;
use Drupal\user\UserInterface;
/**
* Defines the Email Builder plug-in for ipv_waiting_list module.
*
* @EmailBuilder(
* id = "ipv_waiting_list",
* sub_types = {
* "lesson_max_capacity" = @Translation("Email informing lesson reached maximum capacity - notification to coordinators and secretariat"),
* "participant" = @Translation("Email to participant on waiting list"),
* },
* common_adjusters = {"email_subject", "email_body", "email_skip_sending"},
* )
*/
class WaitingListEmailBuilder extends EmailBuilderBase {
use MailerHelperTrait;
/**
* Saves the parameters for a newly created email.
*
* @param \Drupal\symfony_mailer\EmailInterface $email
* The email to modify.
* @param \Drupal\user\UserInterface|null $user
* The (optional) mail receiving user.
* @param \Drupal\commerce_product\Entity\ProductVariationInterface|null $lesson
* The (optional) lesson object.
*/
public function createParams(EmailInterface $email, UserInterface $user = NULL, ProductVariationInterface $lesson = NULL) {
assert($user != NULL);
assert($lesson != NULL);
$email->setParam('user', $user);
$email->setParam('lesson', $lesson);
}
/**
* {@inheritdoc}
*/
public function build(EmailInterface $email) {
$email->setTo($email->getParam('user'));
}
}
and
namespace Drupal\ipv_waiting_list\Plugin\EmailBuilder;
use Drupal\commerce_product\Entity\ProductVariationInterface;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\MailerHelperTrait;
use Drupal\symfony_mailer\Processor\EmailBuilderBase;
/**
* Defines the Email Builder plug-in for waiting list confirmation message.
*
* @EmailBuilder(
* id = "ipv_waiting_list_confirmation",
* sub_types = {
* "confirmation" = @Translation("Email confirming registration on waiting list"),
* },
* common_adjusters = {"email_subject", "email_body", "email_skip_sending"},
* )
*/
class WaitingListConfirmationEmailBuilder extends EmailBuilderBase {
use MailerHelperTrait;
/**
* Saves the parameters for a newly created email.
*
* @param \Drupal\symfony_mailer\EmailInterface $email
* The email to modify.
* @param string|null $recipient
* The (optional) mail receiving user.
* @param \Drupal\commerce_product\Entity\ProductVariationInterface|null $lesson
* The (optional) lesson.
*/
public function createParams(EmailInterface $email, string $recipient = NULL, ProductVariationInterface $lesson = NULL) {
assert($recipient != NULL);
$email->setParam('recipient', $recipient);
$email->setParam('lesson', $lesson);
}
/**
* {@inheritdoc}
*/
public function build(EmailInterface $email) {
$email->setTo($email->getParam('recipient'));
}
}
Now when I go to /admin/config/system/mailer/policy/add, I expect to see both in the Type dropdown, but I only see the module name. When I select that, I see the 2 subtypes of my WaitingListEmailBuilder class. No sign of the WaitingListConfirmationEmailBuilder type / subtype.
Is there only 1 EmailBuilder per module possible? Doesn't seem so, because symfony_mailer_bc contains multiple... Not sure why I'm not seeing them both though...
When I then select a subtype of WaitingListEmailBuilder and try to add a Body element, I get PHP notices and cannot proceed:
Notice: Undefined index: content in /app/web/modules/contrib/symfony_mailer/src/Plugin/EmailAdjuster/BodyEmailAdjuster.php on line 56
Notice: Trying to access array offset on value of type null in /app/web/modules/contrib/symfony_mailer/src/Plugin/EmailAdjuster/BodyEmailAdjuster.php on line 61
Also, I'm not sure what to replace the code with, that used to call the Drupal core mail.manager.
I now have:
/** @var \Drupal\symfony_mailer\EmailFactoryInterface $email_factory */
$email_factory = \Drupal::service('email_factory');
$email_factory->newModuleEmail('ipv_waiting_list_confirmation', 'confirmation')
->setParam('recipient', $entity->getEmail())
->setParam('commerce_product_variation', $this->lesson)
->send();
However I'm not sure if the module name or the type is needed for the first parameter. Seems the type should work, but why does this imply it's for a specific module? Maybe there should be a general $email_factory->newEmail()? Although that's probably nit-picking if I know I can use newModuleEmail().
It would seem to me this is a pretty basic use case? What am I doing wrong exactly? Am I missing some point?
I have checked the doc page
https://www.drupal.org/docs/contributed-modules/symfony-mailer-0/develop... β
but that seems to be aimed towards letting my module work with symfony_mailer_bc, whereas I want it to work natively...