Skip to content
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: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
],
"minimum-stability": "stable",
"require": {
"php": "^7.1|^8.0",
"nette/mail": "~3.0",
"php": "^8.1",
"nette/mail": "^3.1 || ^4.0",
"nette/di": "^2.3 || ~3.0",
"doctrine/orm": "^2.9 || ^3.0",
"tracy/tracy": "^2.3",
"adt/background-queue": "^4.22"
},
Expand Down
27 changes: 10 additions & 17 deletions src/Entity/AbstractMailQueueEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,31 @@


/**
* @ORM\MappedSuperclass
* Mapping is defined as PHP attributes only - the annotation driver was removed
* in ORM 3, so projects using this package need the attribute driver.
*
* @property \DateTime $createdAt
* @property \DateTime|NULL $sentAt
* @property string $from
* @property string $subject
* @property \Nette\Mail\Message|NULL $message
*/
#[ORM\MappedSuperclass]
abstract class AbstractMailQueueEntry {

/**
* @ORM\Column(type="datetime")
*/
#[ORM\Column(type: 'datetime')]
protected $createdAt;

/**
* @ORM\Column(type="datetime", nullable=true)
*/
#[ORM\Column(type: 'datetime', nullable: true)]
protected $sentAt;

/**
* @ORM\Column(type="string", name="`from`")
*/
#[ORM\Column(type: 'string', name: '`from`')]
protected $from;

/**
* @ORM\Column(type="text")
*/
#[ORM\Column(type: 'text')]
protected $subject;

/**
* @ORM\Column(type="blob", nullable=true)
*/
#[ORM\Column(type: 'blob', nullable: true)]
protected $message;

/**
Expand All @@ -57,7 +50,7 @@ public function getMessage() {
* @param \Nette\Mail\Message|NULL $message
* @return $this
*/
public function setMessage(\Nette\Mail\Message $message = NULL): self {
public function setMessage(?\Nette\Mail\Message $message = NULL): self {
if ($message === NULL) {
$this->message = NULL;
} else {
Expand Down
4 changes: 1 addition & 3 deletions src/Entity/MailQueueEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
use Doctrine\ORM\Mapping as ORM;


/**
* @ORM\Entity
*/
#[ORM\Entity]
class MailQueueEntry extends AbstractMailQueueEntry {
use \ADT\MailQueue\Traits\Identifier;
}
4 changes: 3 additions & 1 deletion src/Service/QueueMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace ADT\MailQueue\Service;

class QueueMailer implements \Nette\Mail\IMailer {
// Nette\Mail\Mailer exists since nette/mail 3.1 (IMailer is its alias there)
// and is the only name available in nette/mail 4, so it works for both generations.
class QueueMailer implements \Nette\Mail\Mailer {

/** @var QueueService */
protected $queueService;
Expand Down
32 changes: 27 additions & 5 deletions src/Service/QueueService.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class QueueService {
/** @var string */
protected $queueEntryClass;

/** @var \Kdyby\Doctrine\EntityManager */
/** @var EntityManagerInterface */
protected $em;

/** @var string */
Expand All @@ -28,7 +28,7 @@ class QueueService {
/** @var string */
protected $mutexTimeFile;

/** @var \Nette\Mail\IMailer */
/** @var \Nette\Mail\Mailer */
protected $mailer;

/** @var IMessenger */
Expand All @@ -55,6 +55,9 @@ class QueueService {
/** @var string */
protected $backgroundQueueCallbackName;

/** @var bool|null Detected lazily via reflection - see flushEntry() */
protected $emSupportsSingleEntityFlush;

public function __construct($config, EntityManagerInterface $em) {
if (! is_dir($config['tempDir'])) {
mkdir($config['tempDir']);
Expand All @@ -73,7 +76,7 @@ public function __construct($config, EntityManagerInterface $em) {
$this->backgroundQueueCallbackName = $config['backgroundQueueCallbackName'];
}

public function setMailer(\Nette\Mail\IMailer $mailer) {
public function setMailer(\Nette\Mail\Mailer $mailer) {
$this->mailer = $mailer;
return $this;
}
Expand Down Expand Up @@ -122,7 +125,7 @@ protected function createQueueEntry(\Nette\Mail\Message $message, $custom = [])
public function enqueue(\Nette\Mail\Message $message, $custom = [], ?int $backgroundQueuePriority = null) {
$entry = $this->createQueueEntry($message, $custom);
$this->em->persist($entry);
$this->em->flush($entry);
$this->flushEntry($entry);

$this->backgroundQueueService->publish($this->backgroundQueueCallbackName, [$entry->getId()], null, null, false, null, $backgroundQueuePriority);

Expand Down Expand Up @@ -184,10 +187,29 @@ public function process(int $entryId) {
}

$this->em->persist($entry);
$this->em->flush($entry);
$this->flushEntry($entry);

return TRUE;
}

/**
* Flushes the queue entry without touching other pending changes when possible.
*
* ORM 2 supports flushing a single entity, which keeps the caller's unit of work
* untouched - enqueueing a mail must not write unrelated pending changes to DB.
* ORM 3 removed single-entity flush, so there the full flush() is the only option
* and callers should not enqueue mails with an unflushed unit of work.
*/
protected function flushEntry(Entity\AbstractMailQueueEntry $entry): void {
if ($this->emSupportsSingleEntityFlush === null) {
$this->emSupportsSingleEntityFlush = (new \ReflectionMethod($this->em, 'flush'))->getNumberOfParameters() > 0;
}

if ($this->emSupportsSingleEntityFlush) {
$this->em->flush($entry);
} else {
$this->em->flush();
}
}

}
12 changes: 6 additions & 6 deletions src/Traits/Identifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

namespace ADT\MailQueue\Traits;

use Doctrine\ORM\Mapping as ORM;


trait Identifier
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
* @var integer|null
*/
/** @var integer|null */
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue]
protected $id;

/**
Expand Down