feat: mailing to filtered teamer list
addresses #869dv9bur
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class TeamerMailingDto
|
||||
{
|
||||
#[Assert\NotBlank(message: 'Bitte gib einen Betreff an')]
|
||||
private ?string $subject = null;
|
||||
|
||||
#[Assert\NotBlank(message: 'Bitte gib eine Nachricht ein')]
|
||||
private ?string $message = null;
|
||||
|
||||
public function getSubject(): ?string
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
public function setSubject(?string $subject): static
|
||||
{
|
||||
$this->subject = $subject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage(): ?string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMessage(?string $message): static
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* A single candidate for a mailing, projected straight out of the database.
|
||||
*
|
||||
* A mailing needs a name and an address per person and nothing else, so hydrating the
|
||||
* teamer entities - with their availabilities, licenses, photo and user account - would
|
||||
* cost a lot of time and memory for data that is never read.
|
||||
*/
|
||||
class TeamerMailingRecipient
|
||||
{
|
||||
public function __construct(
|
||||
private readonly int $id,
|
||||
private readonly ?string $firstName,
|
||||
private readonly ?string $lastName,
|
||||
private readonly ?string $email,
|
||||
private readonly ?\DateTimeImmutable $deletedAt,
|
||||
private readonly ?\DateTimeImmutable $disabledAt,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* id: int,
|
||||
* firstName: ?string,
|
||||
* lastName: ?string,
|
||||
* email: ?string,
|
||||
* deletedAt: ?\DateTimeImmutable,
|
||||
* disabledAt: ?\DateTimeImmutable,
|
||||
* } $row a row of TeamerRepository::getMailingRecipients()
|
||||
*/
|
||||
public static function fromRow(array $row): self
|
||||
{
|
||||
return new self(
|
||||
$row['id'],
|
||||
$row['firstName'],
|
||||
$row['lastName'],
|
||||
$row['email'],
|
||||
$row['deletedAt'],
|
||||
$row['disabledAt']
|
||||
);
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getFirstName(): string
|
||||
{
|
||||
return (string) $this->firstName;
|
||||
}
|
||||
|
||||
public function getLastName(): string
|
||||
{
|
||||
return (string) $this->lastName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Built the same way as Teamer::getFullName(), so a mailing reads like every other
|
||||
* place the app spells a teamer out.
|
||||
*/
|
||||
public function getFullName(): string
|
||||
{
|
||||
return sprintf('%s %s', $this->firstName, $this->lastName);
|
||||
}
|
||||
|
||||
public function getEmail(): ?string
|
||||
{
|
||||
if (null === $this->email || '' === trim($this->email)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function isDeleted(): bool
|
||||
{
|
||||
return null !== $this->deletedAt;
|
||||
}
|
||||
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
return null !== $this->disabledAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* The outcome of matching a teamer filter against the mailing rules, so that the compose
|
||||
* page, the confirmation modal and the send itself all report the very same numbers.
|
||||
*/
|
||||
class TeamerMailingRecipientsDto
|
||||
{
|
||||
/**
|
||||
* @var TeamerMailingRecipient[]
|
||||
*/
|
||||
private array $eligible = [];
|
||||
|
||||
private int $skippedNoEmail = 0;
|
||||
private int $skippedDeleted = 0;
|
||||
private int $skippedDisabled = 0;
|
||||
|
||||
public function addEligible(TeamerMailingRecipient $recipient): static
|
||||
{
|
||||
$this->eligible[] = $recipient;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addSkippedNoEmail(): static
|
||||
{
|
||||
++$this->skippedNoEmail;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addSkippedDeleted(): static
|
||||
{
|
||||
++$this->skippedDeleted;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addSkippedDisabled(): static
|
||||
{
|
||||
++$this->skippedDisabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TeamerMailingRecipient[]
|
||||
*/
|
||||
public function getEligible(): array
|
||||
{
|
||||
return $this->eligible;
|
||||
}
|
||||
|
||||
public function getEligibleCount(): int
|
||||
{
|
||||
return count($this->eligible);
|
||||
}
|
||||
|
||||
public function getSkippedNoEmail(): int
|
||||
{
|
||||
return $this->skippedNoEmail;
|
||||
}
|
||||
|
||||
public function getSkippedDeleted(): int
|
||||
{
|
||||
return $this->skippedDeleted;
|
||||
}
|
||||
|
||||
public function getSkippedDisabled(): int
|
||||
{
|
||||
return $this->skippedDisabled;
|
||||
}
|
||||
|
||||
public function getSkippedCount(): int
|
||||
{
|
||||
return $this->skippedNoEmail + $this->skippedDeleted + $this->skippedDisabled;
|
||||
}
|
||||
|
||||
public function getTotal(): int
|
||||
{
|
||||
return $this->getEligibleCount() + $this->getSkippedCount();
|
||||
}
|
||||
|
||||
public function hasSkipped(): bool
|
||||
{
|
||||
return 0 < $this->getSkippedCount();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user