Skip to content

Commit

Permalink
Add hour precision when mailator is dispached
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasile Papuc committed Jun 27, 2023
1 parent 7c3fa06 commit 071ebd4
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Binarcode\LaravelMailator\Models\MailatorLog;
use Binarcode\LaravelMailator\Models\MailatorSchedule;

class AlterSchedulerAtHoursColumnTables extends Migration
{
public function up()
{
Schema::table(config('mailator.schedulers_table_name', 'mailator_schedulers'), function (Blueprint $table) {
$table->json('schedule_at_hours')->nullable()->after('last_sent_at');
});
}
}
2 changes: 1 addition & 1 deletion database/migrations/create_mailator_tables.php.stub
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CreateMailatorTables extends Migration
$table->json('recipients')->nullable();
$table->text('when')->nullable();
$table->string('frequency_option')->default(MailatorSchedule::FREQUENCY_OPTIONS_ONCE)->comment('How often send email notification.');

$table->json('schedule_at_hours')->nullable();
$table->timestamp('last_sent_at')->nullable();
$table->timestamp('last_failed_at')->nullable();
$table->timestamp('completed_at')->nullable();
Expand Down
2 changes: 1 addition & 1 deletion src/Actions/RunSchedulersAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class RunSchedulersAction
{
use ClassResolver;

public function __invoke()
public function __invoke(): void
{
static::scheduler()::query()
->ready()
Expand Down
20 changes: 20 additions & 0 deletions src/Constraints/HoursSchedulerCheckerConstraint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Binarcode\LaravelMailator\Constraints;

use Binarcode\LaravelMailator\Models\MailatorLog;
use Binarcode\LaravelMailator\Models\MailatorSchedule;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class HoursSchedulerCheckerConstraint implements SendScheduleConstraint
{
public function canSend(MailatorSchedule $schedule, Collection $logs): bool
{
if (!$schedule->hasPrecision()) {
return true;
}

return in_array(now()->hour, $schedule->schedule_at_hours);
}
}
6 changes: 6 additions & 0 deletions src/LaravelMailatorServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ public function boot()
__DIR__ . '/../database/migrations/create_mailator_tables.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_mailator_tables.php'),
], 'mailator-migrations');
}

if (! class_exists('AlterSchedulerAtHoursColumnTables')) {
$this->publishes([
__DIR__ . '/../database/migrations/alter_table_mailator_schedule_at_hours_column.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_alter_table_mailator_schedule_at_hours_column.php'),
], 'mailator-migrations');
}
// Publishing the views.
$this->publishes([
__DIR__.'/../resources/views/publish' => resource_path('views/vendor/laravel-mailator'),
Expand Down
25 changes: 14 additions & 11 deletions src/Models/Concerns/ConstraintsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Binarcode\LaravelMailator\Constraints\BeforeConstraint;
use Binarcode\LaravelMailator\Constraints\DailyConstraint;
use Binarcode\LaravelMailator\Constraints\Descriptionable;
use Binarcode\LaravelMailator\Constraints\HoursSchedulerCheckerConstraint;
use Binarcode\LaravelMailator\Constraints\ManualConstraint;
use Binarcode\LaravelMailator\Constraints\ManyConstraint;
use Binarcode\LaravelMailator\Constraints\NeverConstraint;
Expand Down Expand Up @@ -34,9 +35,10 @@ public function configurationsPasses(): bool
ManyConstraint::class,
DailyConstraint::class,
WeeklyConstraint::class,
HoursSchedulerCheckerConstraint::class,
])
->map(fn ($class) => app($class))
->every(fn (SendScheduleConstraint $event) => $event->canSend($this, $this->logs));
->map(fn($class) => app($class))
->every(fn(SendScheduleConstraint $event) => $event->canSend($this, $this->logs));
}

public function whenPasses(): bool
Expand All @@ -47,17 +49,18 @@ public function whenPasses(): bool
public function eventsPasses(): bool
{
return collect($this->constraints)
->map(fn (string $event) => unserialize($event))
->filter(fn ($event) => is_subclass_of($event, SendScheduleConstraint::class))
->filter(fn (SendScheduleConstraint $event) => $event->canSend($this, $this->logs))->count() === collect($this->constraints)->count();
->map(fn(string $event) => unserialize($event))
->filter(fn($event) => is_subclass_of($event, SendScheduleConstraint::class))
->filter(fn(SendScheduleConstraint $event) => $event->canSend($this,
$this->logs))->count() === collect($this->constraints)->count();
}

public function constraintsDescriptions(): array
{
try {
return collect($this->constraints)
->map(fn (string $event) => unserialize($event))
->filter(fn ($event) => is_subclass_of($event, Descriptionable::class))
->map(fn(string $event) => unserialize($event))
->filter(fn($event) => is_subclass_of($event, Descriptionable::class))
->reduce(function ($base, Descriptionable $descriable) {
return array_merge($base, $descriable::conditions());
}, []);
Expand All @@ -72,10 +75,10 @@ public function constraintsNotSatisfiedDescriptions(): array
{
try {
return collect($this->constraints)
->map(fn (string $event) => unserialize($event))
->filter(fn ($event) => is_subclass_of($event, Descriptionable::class))
->filter(fn ($event) => is_subclass_of($event, SendScheduleConstraint::class))
->filter(fn ($event) => ! $event->canSend($this, $this->logs))
->map(fn(string $event) => unserialize($event))
->filter(fn($event) => is_subclass_of($event, Descriptionable::class))
->filter(fn($event) => is_subclass_of($event, SendScheduleConstraint::class))
->filter(fn($event) => !$event->canSend($this, $this->logs))
->reduce(function ($base, Descriptionable $descriable) {
return array_merge($base, $descriable::conditions());
}, []);
Expand Down
52 changes: 33 additions & 19 deletions src/Models/MailatorSchedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
* @property Carbon $last_failed_at
* @property string $failure_reason
* @property Carbon $last_sent_at
* @property array|null $schedule_at_hours
* @property Carbon $completed_at
* @property string $frequency_option
* @property-read Collection $logs
Expand Down Expand Up @@ -86,6 +87,7 @@ public function getTable()
protected $casts = [
'constraints' => 'array',
'recipients' => 'array',
'schedule_at_hours' => 'array',
'timestamp_target' => 'datetime',
'last_failed_at' => 'datetime',
'last_sent_at' => 'datetime',
Expand All @@ -111,8 +113,8 @@ public function mailable(Mailable $mailable): self
{
if ($mailable instanceof Constraintable) {
collect($mailable->constraints())
->filter(fn ($constraint) => $constraint instanceof SendScheduleConstraint)
->each(fn (SendScheduleConstraint $constraint) => $this->constraint($constraint));
->filter(fn($constraint) => $constraint instanceof SendScheduleConstraint)
->each(fn(SendScheduleConstraint $constraint) => $this->constraint($constraint));
}

$this->mailable_class = serialize($mailable);
Expand Down Expand Up @@ -242,6 +244,11 @@ public function isWeekly(): bool
return $this->frequency_option === static::FREQUENCY_OPTIONS_WEEKLY;
}

public function hasPrecision(): bool
{
return (bool) $this->schedule_at_hours;
}

public function isAfter(): bool
{
return $this->time_frame_origin === static::TIME_FRAME_ORIGIN_AFTER;
Expand Down Expand Up @@ -305,6 +312,13 @@ public function days(int $number): self
return $this;
}

public function precision(array $scheduleAtHour): self
{
$this->schedule_at_hours = $scheduleAtHour;

return $this;
}

public function weeks(int $number): static
{
$this->delay_minutes = $number * ConverterEnum::MINUTES_IN_WEEK;
Expand All @@ -323,7 +337,7 @@ public function recipients(...$recipients): self
{
$this->recipients = array_merge(collect($recipients)
->flatten()
->filter(fn ($email) => $this->ensureValidEmail($email))
->filter(fn($email) => $this->ensureValidEmail($email))
->unique()
->toArray(), $this->recipients ?? []);

Expand All @@ -349,15 +363,15 @@ public function shouldSend(): bool
try {
$this->load('logs');

if (! $this->configurationsPasses()) {
if (!$this->configurationsPasses()) {
return false;
}

if (! $this->whenPasses()) {
if (!$this->whenPasses()) {
return false;
}

if (! $this->eventsPasses()) {
if (!$this->eventsPasses()) {
if ($this->isStopable()) {
$this->markComplete();
}
Expand All @@ -366,7 +380,7 @@ public function shouldSend(): bool
}

return true;
} catch (Exception | Throwable $e) {
} catch (Exception|Throwable $e) {
$this->markAsFailed($e->getMessage());

app(ResolveGarbageAction::class)->handle($this);
Expand All @@ -377,7 +391,7 @@ public function shouldSend(): bool

public function executeWhenPasses(bool $now = false): void
{
if (! $this->save()) {
if (!$this->save()) {
return;
}

Expand All @@ -388,7 +402,7 @@ public function executeWhenPasses(bool $now = false): void

public function execute(bool $now = false): void
{
if (! $this->save()) {
if (!$this->save()) {
return;
}

Expand All @@ -406,7 +420,7 @@ public function execute(bool $now = false): void
dispatch(new SendMailJob($this));
}
}
} catch (Exception | Throwable $e) {
} catch (Exception|Throwable $e) {
$this->markAsFailed($e->getMessage());
}
}
Expand All @@ -418,14 +432,14 @@ public static function run(): void

public function hasCustomAction(): bool
{
return ! is_null($this->action);
return !is_null($this->action);
}

public function getMailable(): ?Mailable
{
try {
return unserialize($this->mailable_class);
} catch (Throwable | TypeError $e) {
} catch (Throwable|TypeError $e) {
$this->markAsFailed($e->getMessage());
}

Expand Down Expand Up @@ -473,13 +487,13 @@ public function markAsFailed(string $failureReason): self
public function getRecipients(): array
{
return collect($this->recipients)
->filter(fn ($email) => $this->ensureValidEmail($email))
->filter(fn($email) => $this->ensureValidEmail($email))
->toArray();
}

protected function ensureValidEmail(string $email): bool
{
return ! Validator::make(
return !Validator::make(
compact('email'),
['email' => 'required|email']
)->fails();
Expand All @@ -492,7 +506,7 @@ public function actionClass(Action $action): self
return $this;
}

public function tag(string | array $tag): self
public function tag(string|array $tag): self
{
if (is_array($tag)) {
$tag = implode(',', $tag);
Expand Down Expand Up @@ -543,7 +557,7 @@ public function markComplete(): self

public function isCompleted(): bool
{
return ! is_null($this->completed_at);
return !is_null($this->completed_at);
}

public function failedLastTimes(int $times): bool
Expand All @@ -565,12 +579,12 @@ public function timestampTarget(): ?CarbonInterface

public function isRepetitive(): bool
{
return ! $this->isOnce();
return !$this->isOnce();
}

public function wasSentOnce(): bool
{
return ! is_null($this->last_sent_at);
return !is_null($this->last_sent_at);
}

public function getConstraints(): ConstraintsCollection
Expand All @@ -580,7 +594,7 @@ public function getConstraints(): ConstraintsCollection

public function save(array $options = [])
{
if (! $this->isUnique()) {
if (!$this->isUnique()) {
return parent::save($options);
}

Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/Models/MailatorScheduleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public function test_can_use_carbon_target_date_before(): void

$scheduler->save();

// MailatorSchedule::run();
// Mail::assertNothingSent();
MailatorSchedule::run();
Mail::assertNothingSent();

$this->travelTo(now()->addDays(6));
MailatorSchedule::run();
Expand Down
Loading

0 comments on commit 071ebd4

Please sign in to comment.