├── .github └── workflows │ └── build.yml ├── .gitignore ├── .styleci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── laravel-digest.php ├── database └── migrations │ └── 2022_01_30_000000_create_digests_table.php ├── phpunit.xml ├── resources └── views │ └── digest.blade.php ├── sonar-project.properties ├── src ├── Console │ └── Commands │ │ └── SendDigest.php ├── Facades │ └── Digest.php ├── LaravelDigest.php ├── LaravelDigestServiceProvider.php ├── Mail │ └── DefaultMailable.php └── Models │ └── Digest.php └── tests ├── Feature ├── AmountDigestTest.php └── FrequencyDigestTest.php └── TestCase.php /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: [ opened, synchronize, reopened ] 8 | jobs: 9 | testing-php7: 10 | name: "Testing PHP 7.4" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - name: Setup php 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: 7.4 21 | extensions: dom, curl, libxml, mbstring, redis, zip 22 | coverage: none 23 | - name: Install dependencies 24 | run: composer install --prefer-dist --no-interaction --no-progress 25 | - name: Execute tests 26 | run: vendor/bin/phpunit --verbose 27 | testing-php8: 28 | name: "Testing PHP 8.0" 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout code 32 | uses: actions/checkout@v2 33 | with: 34 | fetch-depth: 0 35 | - name: Setup php 36 | uses: shivammathur/setup-php@v2 37 | with: 38 | php-version: 8.0 39 | extensions: dom, curl, libxml, mbstring, redis, zip 40 | coverage: none 41 | - name: Install dependencies 42 | run: composer install --prefer-dist --no-interaction --no-progress 43 | - name: Execute tests 44 | run: vendor/bin/phpunit --verbose 45 | testing-php82: 46 | name: "Testing PHP 8.2" 47 | runs-on: ubuntu-latest 48 | steps: 49 | - name: Checkout code 50 | uses: actions/checkout@v2 51 | with: 52 | fetch-depth: 0 53 | - name: Setup php 54 | uses: shivammathur/setup-php@v2 55 | with: 56 | php-version: 8.2 57 | extensions: dom, curl, libxml, mbstring, redis, zip 58 | coverage: none 59 | - name: Install dependencies 60 | run: composer install --prefer-dist --no-interaction --no-progress 61 | - name: Execute tests 62 | run: vendor/bin/phpunit --migrate-configuration -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | .phpunit.result.cache -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `LaravelDigest` will be documented in this file. 4 | 5 | ## Version 1.0 6 | 7 | ### Added 8 | - All basic functionality 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome and will be fully credited. 4 | 5 | Contributions are accepted via Pull Requests on [Github](https://github.com/hmones/laravel-digest). 6 | 7 | # Things you could do 8 | If you want to contribute but do not know where to start, this list provides some starting points. 9 | - Add compatibility with previous versions of Laravel 10 | - Create more tests 11 | - Add configuration options for changing where Facades are stored 12 | - Add force option to override 13 | 14 | ## Pull Requests 15 | 16 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 17 | 18 | - **Document any change in behaviour** - Make sure the `readme.md` and any other relevant documentation are kept up-to-date. 19 | 20 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 21 | 22 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 23 | 24 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 25 | 26 | 27 | **Happy coding**! -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Haytham Mones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Laravel Digest

2 | 3 |

4 | Build Status 5 | Style CI 6 | Total Downloads 7 | Latest Stable Version 8 | License 9 |

10 | 11 | A simple package to create and send digest emails every certain period or when the amount reaches a certain threshold. 12 | Usage Examples: 13 | - Sending a digest email to website administrator every new 100 registrations on the website. 14 | - Sending a daily email with logged error messages on the website. 15 | - Sending users a monthly newsletter that contains all posts issued in the month. 16 | 17 | ## Installation 18 | 19 | Via Composer 20 | 21 | ```bash 22 | composer require hmones/laravel-digest 23 | ``` 24 | 25 | ## Configuration 26 | 27 | To publish the package configuration 28 | 29 | ```bash 30 | php artisan vendor:publish --tag=laravel-digest-config 31 | ``` 32 | 33 | The configuration file contains the following parameters: 34 | - `method`: the method that you would like to use to send emails, it takes two values, `queue` or `send` 35 | - Env variable: `DIGEST_METHOD` 36 | - Default value: `queue` 37 | - `frequency.enabled` whether you would like to enable sending emails every certain period, if not enabled emails will not be scheduled 38 | - Env variable: `DIGEST_FREQUENCY_ENABLED` 39 | - Default value: `true` 40 | - `frequency.daily.time` if frequency is enabled this parameter is used as the time to send the daily digest emails 41 | - Env variable: `DIGEST_DAILY_TIME` 42 | - Default value: `00:00` 43 | - `frequency.weekly.time` if frequency is enabled this parameter is used as the time to send the weekly digest emails 44 | - Env variable: `DIGEST_WEEKLY_TIME` 45 | - Default value: `00:00` 46 | - `frequency.monthly.time` if frequency is enabled this parameter is used as the time to send the monthly digest emails 47 | - Env variable: `DIGEST_MONTHLY_TIME` 48 | - Default value: `00:00` 49 | - `frequency.weekly.day` if frequency is enabled this parameter is used as the day to send the weekly digest emails (1 is Sunday and 7 is Saturday) 50 | - Env variable: `DIGEST_WEEKLY_DAY` 51 | - Default value: `1` 52 | - `frequency.monthly.day` if frequency is enabled this parameter is used as the day to send the monthly digest emails 53 | - Env variable: `DIGEST_MONTHLY_DAY` 54 | - Default value: `1` 55 | - `frequency.custom` you can set as much custom frequency definitions as you want and the parameter takes a valid cron expression 56 | - `amount.enabled` whether you would like to enable sending emails every certain amount per batch 57 | - Env variable: `DIGEST_AMOUNT_ENABLED` 58 | - Default value: `true` 59 | - `amount.threshold` the number of emails after which an digest email should be sent. 60 | - Env variable: `DIGEST_AMOUNT_THRESHOLD` 61 | - Default value: `10` 62 | 63 | ## Usage 64 | 65 | To create an email digest, make sure you have the following first: 66 | - A mailable, configured with the sending addresses and email views and subject. 67 | - The mailable should accept an array variable in its constructor, this array variable will contain all the records of data passed to individual emails concatenated and sent automatically by the package to the mailable to compile the views for sending the digest. 68 | 69 | Example: Sending a digest email every time 10 new users register on the website with a summary of their names. 70 | 71 | 1. Adjust the configuration variable in `config\laravel-digest.php` by setting `amount.enabled => true` and `frequency.enabled => false` 72 | 2. Create the following mailable 73 | ```php 74 | data = $data; 91 | } 92 | 93 | public function build(): Mailable 94 | { 95 | return $this->view('userCreated')->subject('10 new users are registered')->to('email@test.com'); 96 | } 97 | } 98 | ``` 99 | 3. Create a view to render the names of the users `resources\userCreated.blade.php` 100 | ```html 101 | 102 | Sample Email 103 | 104 |

The following users have just registered:

105 |
    106 | @foreach($data as $record) 107 |
  1. {{$record['name']}}
  2. 108 | @endforeach 109 |
110 | 111 | 112 | ``` 113 | 4. Create an observer for user creation and add an record to the digest every time a user is created: 114 | 115 | ```php 116 | $user->name]; 132 | //Frequency can take values such as daily, weekly, monthly, custom or an integer threshold 10, 20 ...etc 133 | $frequency = 10; 134 | 135 | Digest::add($batchId, $mailable, $data, $frequency); 136 | } 137 | } 138 | ``` 139 | 5. The package will take care of everything else and send emails once the number of registered users reach 10. 140 | 141 | 142 | ## Change log 143 | 144 | Please see the [changelog](CHANGELOG.md) for more information on what has changed recently. 145 | 146 | ## Testing 147 | 148 | ``` bash 149 | composer test 150 | ``` 151 | 152 | ## Contributing 153 | 154 | Please see [contributing.md](CONTRIBUTING.md) for details and a todolist. 155 | 156 | ## Security 157 | 158 | If you discover any security related issues, please email author email instead of using the issue tracker. 159 | 160 | ## Credits 161 | - [Haytham Mones][link-author] 162 | 163 | ## License 164 | 165 | Please see the [license file](LICENSE.md) for more information. 166 | 167 | [link-author]: https://github.com/hmones 168 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hmones/laravel-digest", 3 | "description": "A simple package to create and send digest emails every certain period or when the amount reaches a certain threshold.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Haytham Mones", 8 | "email": "haytham.mones@gmail.com", 9 | "homepage": "https://haythammones.com" 10 | } 11 | ], 12 | "homepage": "https://github.com/hmones/laravel-digest", 13 | "keywords": [ 14 | "Laravel", 15 | "LaravelDigest" 16 | ], 17 | "minimum-stability": "stable", 18 | "scripts": { 19 | "test": "vendor/bin/phpunit" 20 | }, 21 | "require": { 22 | "php": "^7.4|^8.0|^8.2", 23 | "ext-json": "*", 24 | "illuminate/support": "~7|~8|~9|~10|^11.0" 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "~9.0|^10.5", 28 | "orchestra/testbench": "~5|~6|~7|^9.0" 29 | }, 30 | "autoload": { 31 | "psr-4": { 32 | "Hmones\\LaravelDigest\\": "src/" 33 | } 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "Hmones\\LaravelDigest\\Tests\\": "tests" 38 | } 39 | }, 40 | "extra": { 41 | "laravel": { 42 | "providers": [ 43 | "Hmones\\LaravelDigest\\LaravelDigestServiceProvider" 44 | ], 45 | "aliases": { 46 | "LaravelDigest": "Hmones\\LaravelDigest\\Facades\\LaravelDigest" 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config/laravel-digest.php: -------------------------------------------------------------------------------- 1 | env('DIGEST_METHOD', 'queue'), 10 | /* 11 | * Duration option specifies whether you want to send the digest mail every specific period. 12 | * The option can be enabled, disabled by setting enabled flag. 13 | * The frequency setting can be configured via the type parameter which takes one of three values (daily, weekly, monthly) 14 | * The time of sending the email can be set via the time parameter, 15 | * The day in which the email is sent can be specified by the day parameter, please note that 16 | * for monthly mails the day is the day of the month and for weekly mails the day is the day of the week, where sunday is 1 17 | */ 18 | 'frequency' => [ 19 | 'enabled' => env('DIGEST_FREQUENCY_ENABLED', true), 20 | 'daily' => [ 21 | 'time' => env('DIGEST_DAILY_TIME', '00:00'), 22 | ], 23 | 'weekly' => [ 24 | 'time' => env('DIGEST_WEEKLY_TIME', '00:00'), 25 | 'day' => env('DIGEST_WEEKLY_DAY', 1), 26 | ], 27 | 'monthly' => [ 28 | 'time' => env('DIGEST_MONTHLY_TIME', '00:00'), 29 | 'day' => env('DIGEST_MONTHLY_DAY', 1), 30 | ], 31 | /* 32 | * You can define here multiple custom scenarios under different names, that fits your needs, 33 | * each custom scenario should have a cron expression that specifies the custom schedule that fits your needs. 34 | * A cron expression takes the following format (default is yearly): 35 | * *(minute) *(hour) *(day of month) *(month) *(day of week) 36 | */ 37 | 'custom' => '0 0 1 1 *', 38 | ], 39 | /* 40 | * Amount option specifies whether you want to send the gigest email every specific number of emails that accumulate for the digest 41 | * This can be optimum for frequently recurring issues on your app that you would like to be informed about after certain threshold 42 | */ 43 | 'amount' => [ 44 | 'enabled' => env('DIGEST_AMOUNT_ENABLED', true), 45 | 'threshold' => env('DIGEST_AMOUNT_THRESHOLD', 10), 46 | ], 47 | ]; 48 | -------------------------------------------------------------------------------- /database/migrations/2022_01_30_000000_create_digests_table.php: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->string('batch'); 14 | $table->string('mailable'); 15 | $table->string('frequency')->nullable(); 16 | $table->text('data')->nullable(); 17 | $table->timestamps(); 18 | }); 19 | } 20 | 21 | public function down(): void 22 | { 23 | Schema::dropIfExists('digests'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /resources/views/digest.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @foreach($data as $record) 3 |
  1. {{$record}}
  2. 4 | @endforeach 5 |
-------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=hmones_laravel-digest 2 | sonar.organization=hmones 3 | 4 | # This is the name and version displayed in the SonarCloud UI. 5 | sonar.projectName=Laravel-Digest 6 | sonar.projectVersion=1.0 7 | 8 | # Encoding of the source code. Default is default system encoding 9 | sonar.sourceEncoding=UTF-8 -------------------------------------------------------------------------------- /src/Console/Commands/SendDigest.php: -------------------------------------------------------------------------------- 1 | info('The digest frequency option is not enabled from the configuration, please enable it first'); 20 | 21 | return; 22 | } 23 | 24 | $frequency = $this->argument('frequency'); 25 | 26 | if (! in_array($frequency, Digest::getFrequencies())) { 27 | $this->error('The frequency you selected is not available!'); 28 | 29 | return; 30 | } 31 | 32 | $this->info('Sending '.$frequency.' emails'); 33 | $this->sendBatches($frequency); 34 | $this->deleteBatches($frequency); 35 | $this->info(ucfirst($frequency).' emails sent'); 36 | } 37 | 38 | protected function sendBatches(string $frequency): void 39 | { 40 | $batches = Model::where('frequency', $frequency)->orderBy('created_at', 'desc')->get()->groupBy('batch'); 41 | 42 | foreach ($batches as $batch) { 43 | $mailable = optional($batch->first())->mailable; 44 | $method = config('laravel-digest.method'); 45 | $data = $batch->pluck('data')->toArray(); 46 | 47 | Mail::$method(new $mailable($data)); 48 | } 49 | } 50 | 51 | protected function deleteBatches(string $frequency): void 52 | { 53 | Model::where('frequency', $frequency)->delete(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Facades/Digest.php: -------------------------------------------------------------------------------- 1 | method = config('laravel-digest.method', 'queue'); 18 | $this->frequencies = array_keys(array_filter(config('laravel-digest.frequency'), fn ($key) => $key !== 'enabled', ARRAY_FILTER_USE_KEY)); 19 | $this->customFrequencies = array_filter(config('laravel-digest.frequency'), fn ($key) => ! in_array($key, [ 20 | 'enabled', 21 | 'daily', 22 | 'weekly', 23 | 'monthly', 24 | ]), ARRAY_FILTER_USE_KEY); 25 | } 26 | 27 | public function getCustomFrequencies(): array 28 | { 29 | return $this->customFrequencies; 30 | } 31 | 32 | public function getFrequencies(): array 33 | { 34 | return $this->frequencies; 35 | } 36 | 37 | public function add(string $batch, string $mailable, $data, $frequency = null): bool 38 | { 39 | if (! $this->isValidFrequency($frequency) || ! $this->isValidMailable($mailable)) { 40 | return false; 41 | } 42 | 43 | $frequency = $frequency ?? config('laravel-digest.amount.threshold'); 44 | 45 | Model::create(compact(['batch', 'mailable', 'frequency', 'data'])); 46 | 47 | if (in_array($frequency, $this->frequencies)) { 48 | return true; 49 | } 50 | 51 | $batchRecords = Model::where('batch', $batch)->whereNotIn('frequency', $this->frequencies)->latest(); 52 | 53 | if (config('laravel-digest.amount.enabled') && $batchRecords->count() >= $frequency) { 54 | $this->sendBatch($this->method, $mailable, $batchRecords->pluck('data')->toArray()); 55 | $batchRecords->delete(); 56 | } 57 | 58 | return true; 59 | } 60 | 61 | protected function sendBatch(string $method, string $mailable, array $data): void 62 | { 63 | Mail::$method(new $mailable($data)); 64 | } 65 | 66 | protected function isValidFrequency($frequency): bool 67 | { 68 | return (config('laravel-digest.frequency.enabled') && in_array($frequency, $this->frequencies)) 69 | || (config('laravel-digest.amount.enabled') && ((int) $frequency !== 0 || is_null($frequency))); 70 | } 71 | 72 | protected function isValidMailable(string $mailable): bool 73 | { 74 | return class_exists($mailable); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/LaravelDigestServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 15 | $this->commands([ 16 | SendDigest::class, 17 | ]); 18 | } 19 | 20 | $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); 21 | 22 | $this->publishes([ 23 | __DIR__.'/../config/laravel-digest.php' => config_path('laravel-digest.php'), 24 | ], 'laravel-digest-config'); 25 | 26 | $this->publishes([ 27 | __DIR__.'/../database/migrations/' => database_path('migrations'), 28 | ], 'laravel-digest-migrations'); 29 | 30 | $this->scheduleDigestCommands(); 31 | } 32 | 33 | public function register(): void 34 | { 35 | $this->mergeConfigFrom(__DIR__.'/../config/laravel-digest.php', 'laravel-digest'); 36 | 37 | $this->app->singleton('digest', fn () => new LaravelDigest()); 38 | } 39 | 40 | public function provides(): array 41 | { 42 | return ['laravel-digest']; 43 | } 44 | 45 | protected function scheduleDigestCommands(): void 46 | { 47 | $this->app->afterResolving(Schedule::class, function (Schedule $schedule) { 48 | $schedule->command('digest:send daily')->dailyAt(config('laravel-digest.frequency.daily.time')); 49 | $schedule->command('digest:send weekly')->weeklyOn(config('laravel-digest.frequency.weekly.day'), config('laravel-digest.frequency.weekly.time')); 50 | $schedule->command('digest:send monthly')->monthlyOn(config('laravel-digest.frequency.monthly.day'), config('laravel-digest.frequency.monthly.time')); 51 | foreach (Digest::getCustomFrequencies() as $name => $cron) { 52 | $schedule->command('digest:send '.$name)->cron($cron); 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Mail/DefaultMailable.php: -------------------------------------------------------------------------------- 1 | data = $data; 18 | } 19 | 20 | public function build(): Mailable 21 | { 22 | return $this->view('digest')->to('email@test.com'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Models/Digest.php: -------------------------------------------------------------------------------- 1 | 'array', 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /tests/Feature/AmountDigestTest.php: -------------------------------------------------------------------------------- 1 | addEmails($emptyData); 18 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $emptyData); 19 | $this->assertEquals(DigestModel::count(), 0); 20 | } 21 | 22 | public function test_digest_emails_are_sent_successfully_after_threshold(): void 23 | { 24 | $this->addEmails($this->testData); 25 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 26 | $this->assertEquals(DigestModel::count(), 0); 27 | } 28 | 29 | public function test_digest_emails_with_no_frequency_are_sent_successfully_after_threshold(): void 30 | { 31 | $this->addEmails(['name' => 'Fourth'], 'testBatch', DigestModel::DAILY); 32 | $this->addEmails($this->testData); 33 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 34 | $this->assertEquals(DigestModel::count(), 1); 35 | } 36 | 37 | public function test_digest_emails_are_not_sent_if_threshold_option_not_enabled(): void 38 | { 39 | config(['laravel-digest.amount.enabled' => false]); 40 | $this->addEmails($this->testData); 41 | Mail::assertNothingQueued(); 42 | } 43 | 44 | public function test_digest_emails_are_not_queued_if_method_option_is_set_to_send(): void 45 | { 46 | config(['laravel-digest.method' => 'send']); 47 | $this->addEmails($this->testData); 48 | Mail::assertSent(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 49 | $this->assertEquals(DigestModel::count(), 0); 50 | } 51 | 52 | public function test_digest_emails_with_custom_threshold_are_sent_successfully_after_threshold(): void 53 | { 54 | config([$this->thresholdConfKey => 10]); 55 | $this->addEmails($this->testData, 'testBatch', 3); 56 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 57 | $this->assertEquals(DigestModel::count(), 0); 58 | } 59 | 60 | public function test_digest_emails_with_custom_threshold_are_not_sent_before_threshold(): void 61 | { 62 | config([$this->thresholdConfKey => 3]); 63 | $this->addEmails($this->testData, 'testBatch', 4); 64 | Mail::assertNothingQueued(); 65 | $this->assertEquals(DigestModel::count(), 3); 66 | } 67 | 68 | protected function setUp(): void 69 | { 70 | parent::setUp(); 71 | Mail::fake(); 72 | config([$this->thresholdConfKey => 3]); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Feature/FrequencyDigestTest.php: -------------------------------------------------------------------------------- 1 | make(Schedule::class); 26 | $frequency = collect($schedule->events())->pluck('expression')->toArray(); 27 | $commands = collect($schedule->events())->pluck('command')->map(function ($item) { 28 | return Str::after($item, '\'artisan\' '); 29 | })->toArray(); 30 | $this->assertEquals($commands, [ 31 | $this->dailyDigestCommand, 32 | $this->weeklyDigestCommand, 33 | $this->monthlyDigestCommand, 34 | $this->customDigestCommand, 35 | ]); 36 | $this->assertEquals($frequency, [ 37 | '0 0 * * *', 38 | '0 0 * * 1', 39 | '0 0 1 * *', 40 | '0 0 1 1 *', 41 | ]); 42 | } 43 | 44 | public function test_frequency_digest_emails_sending_events_are_not_sent_when_not_enabled(): void 45 | { 46 | config(['laravel-digest.frequency.enabled' => false]); 47 | $message = 'The digest frequency option is not enabled from the configuration, please enable it first'; 48 | $this->addEmails($this->testData, $this->batchName, DigestModel::DAILY); 49 | $this->addEmails($this->testData, $this->batchName, DigestModel::WEEKLY); 50 | $this->addEmails($this->testData, $this->batchName, DigestModel::MONTHLY); 51 | $this->artisan($this->dailyDigestCommand)->expectsOutput($message); 52 | $this->artisan($this->weeklyDigestCommand)->expectsOutput($message); 53 | $this->artisan($this->monthlyDigestCommand)->expectsOutput($message); 54 | } 55 | 56 | public function test_daily_emails_sent_successfully(): void 57 | { 58 | $this->addEmails($this->testData, $this->batchName, DigestModel::DAILY); 59 | $this->artisan($this->dailyDigestCommand) 60 | ->expectsOutput('Sending daily emails') 61 | ->expectsOutput('Daily emails sent'); 62 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 63 | $this->assertEquals(DigestModel::count(), 0); 64 | } 65 | 66 | public function test_no_email_is_sent_if_frequency_specified_and_day_not_passed(): void 67 | { 68 | config(['laravel-digest.amount.threshold' => 1]); 69 | $this->addEmails($this->testData, $this->batchName, DigestModel::DAILY); 70 | Mail::assertNothingQueued(); 71 | Mail::assertNothingSent(); 72 | $this->assertEquals(DigestModel::count(), 3); 73 | } 74 | 75 | public function test_weekly_emails_sent_successfully(): void 76 | { 77 | $this->addEmails($this->testData, $this->batchName, DigestModel::WEEKLY); 78 | $this->artisan($this->weeklyDigestCommand) 79 | ->expectsOutput('Sending weekly emails') 80 | ->expectsOutput('Weekly emails sent'); 81 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 82 | $this->assertEquals(DigestModel::count(), 0); 83 | } 84 | 85 | public function test_monthly_emails_sent_successfully(): void 86 | { 87 | $this->addEmails($this->testData, $this->batchName, DigestModel::MONTHLY); 88 | $this->artisan($this->monthlyDigestCommand) 89 | ->expectsOutput('Sending monthly emails') 90 | ->expectsOutput('Monthly emails sent'); 91 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 92 | $this->assertEquals(DigestModel::count(), 0); 93 | } 94 | 95 | public function test_custom_emails_sent_successfully(): void 96 | { 97 | config(['laravel-digest.frequency' => [ 98 | 'enabled' => true, 99 | 'daily' => [ 100 | 'time' => '12:00', 101 | ], 102 | 'weekly' => [ 103 | 'time' => '13:00', 104 | 'day' => 1, 105 | ], 106 | 'monthly' => [ 107 | 'time' => '14:00', 108 | 'day' => 2, 109 | ], 110 | 'test' => '0 0 1 1 *', 111 | ]]); 112 | $this->addEmails($this->testData, $this->batchName, 'test'); 113 | $this->artisan('digest:send test') 114 | ->expectsOutput('Sending test emails') 115 | ->expectsOutput('Test emails sent'); 116 | Mail::assertQueued(DefaultMailable::class, fn ($mail) => $mail->data === $this->testData); 117 | $this->assertEquals(DigestModel::count(), 0); 118 | } 119 | 120 | protected function setUp(): void 121 | { 122 | parent::setUp(); 123 | Mail::fake(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 'First'], 18 | ['name' => 'Second'], 19 | ['name' => 'Third'], 20 | ]; 21 | 22 | public function test_digest_emails_are_successfully_to_database(): void 23 | { 24 | $data = ['first', 'second']; 25 | $this->addEmails($data); 26 | $this->assertEquals(DigestModel::count(), 2); 27 | $this->assertEquals(DigestModel::all()->pluck('data')->toArray(), $data); 28 | } 29 | 30 | protected function getPackageProviders($app) 31 | { 32 | return [ 33 | LaravelDigestServiceProvider::class, 34 | ]; 35 | } 36 | 37 | protected function addEmails(array $data, string $batch = 'testBatch', string $frequency = null): void 38 | { 39 | foreach ($data as $record) { 40 | Digest::add($batch, DefaultMailable::class, $record, $frequency); 41 | } 42 | } 43 | } 44 | --------------------------------------------------------------------------------