├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .php-cs-fixer.php
├── README.md
├── composer.json
├── composer.lock
├── database
├── factories
│ ├── DismissalFactory.php
│ ├── DismissibleFactory.php
│ ├── TestDismisserTypeOneFactory.php
│ └── TestDismisserTypeTwoFactory.php
└── migrations
│ └── 2024_04_20_133407_create_dismissibles_for_laravel_tables.php
├── images
└── dismissibles-for-laravel.jpg
├── phpstan.neon
├── phpunit.xml
├── src
├── Concerns
│ └── Dismiss.php
├── Contracts
│ └── Dismisser.php
├── DismissiblesServiceProvider.php
├── Facades
│ └── Dismissibles.php
├── Models
│ ├── Dismissal.php
│ ├── Dismissible.php
│ ├── TestDismisserTypeOne.php
│ └── TestDismisserTypeTwo.php
└── Traits
│ └── HasDismissibles.php
└── tests
├── BaseTestCase.php
├── Unit
├── Concerns
│ └── DismissTest.php
├── Facades
│ └── Dismissibles
│ │ ├── DismissTest.php
│ │ ├── GetAllForTest.php
│ │ ├── GetTest.php
│ │ ├── IsDismissedTest.php
│ │ └── ShouldShowTest.php
├── Models
│ ├── Dismissal
│ │ ├── DismissedUntilTest.php
│ │ ├── ExtraDataTest.php
│ │ ├── ScopeDismissedAtTest.php
│ │ └── ScopeDismissedByTest.php
│ └── Dismissible
│ │ ├── ActiveFromTest.php
│ │ ├── ActiveUntilTest.php
│ │ ├── IsDismissedByTest.php
│ │ ├── ScopeActiveTest.php
│ │ └── ScopeNotDismissedByTest.php
└── Traits
│ └── HasDismissibles
│ └── DismissalsTest.php
└── database
└── migrations
└── 2024_04_21_153207_create_test_dismissers_tables.php
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build-test:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v3
11 | - name: Setup PHP
12 | uses: shivammathur/setup-php@v2
13 | with:
14 | php-version: 8.2
15 | - name: Install backend dependencies
16 | run: composer install --no-ansi --no-interaction --no-scripts --no-progress
17 | - name: Run tests
18 | run: composer test
19 | - name: Run PHPStan
20 | run: composer phpstan
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor/
3 | .phpunit.result.cache
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | setRiskyAllowed(true)
7 | ->setRules([
8 | '@PSR12' => true,
9 | 'array_syntax' => ['syntax' => 'short'],
10 | 'class_attributes_separation' => ['elements' => ['method' => 'one']],
11 | 'multiline_whitespace_before_semicolons' => true,
12 | 'single_quote' => true,
13 | 'blank_line_after_opening_tag' => true,
14 | 'binary_operator_spaces' => [
15 | 'operators' => [
16 | '=>' => 'align_single_space_minimal',
17 | ],
18 | ],
19 | 'declare_strict_types' => true,
20 | 'blank_line_before_statement' => ['statements' => ['return']],
21 | 'cast_spaces' => true,
22 | 'concat_space' => ['spacing' => 'one'],
23 | 'declare_equal_normalize' => true,
24 | 'type_declaration_spaces' => ['elements' => ['function']],
25 | 'single_line_comment_style' => false,
26 | 'include' => true,
27 | 'lowercase_cast' => true,
28 | 'native_function_casing' => true,
29 | 'increment_style' => ['style' => 'post'],
30 | 'new_with_parentheses' => true,
31 | 'no_blank_lines_after_class_opening' => true,
32 | 'no_blank_lines_after_phpdoc' => true,
33 | 'no_empty_phpdoc' => true,
34 | 'no_empty_statement' => true,
35 | 'no_extra_blank_lines' => [
36 | 'tokens' => [
37 | 'curly_brace_block',
38 | 'extra',
39 | 'parenthesis_brace_block',
40 | 'square_brace_block',
41 | 'throw',
42 | 'use',
43 | ],
44 | ],
45 | 'ordered_imports' => true,
46 | 'no_leading_import_slash' => true,
47 | 'no_leading_namespace_whitespace' => true,
48 | 'no_mixed_echo_print' => ['use' => 'echo'],
49 | 'no_multiline_whitespace_around_double_arrow' => true,
50 | 'no_short_bool_cast' => true,
51 | 'no_singleline_whitespace_before_semicolons' => true,
52 | 'no_spaces_around_offset' => true,
53 | 'no_trailing_comma_in_singleline' => true,
54 | 'no_unneeded_control_parentheses' => true,
55 | 'no_unused_imports' => true,
56 | 'no_useless_else' => true,
57 | 'no_useless_return' => true,
58 | 'no_whitespace_before_comma_in_array' => true,
59 | 'no_whitespace_in_blank_line' => true,
60 | 'normalize_index_brace' => true,
61 | 'object_operator_without_whitespace' => true,
62 | 'phpdoc_align' => true,
63 | 'phpdoc_annotation_without_dot' => true,
64 | 'phpdoc_indent' => true,
65 | 'phpdoc_no_access' => true,
66 | 'phpdoc_no_alias_tag' => true,
67 | 'phpdoc_no_empty_return' => false,
68 | 'phpdoc_no_package' => true,
69 | 'phpdoc_no_useless_inheritdoc' => true,
70 | 'phpdoc_return_self_reference' => true,
71 | 'phpdoc_scalar' => true,
72 | 'phpdoc_separation' => [
73 | 'groups' => [
74 | // general
75 | [
76 | 'deprecated',
77 | 'internal',
78 | 'todo',
79 | ],
80 | // doc blocks, phpstan types
81 | [
82 | 'template',
83 | 'extends',
84 | 'mixin',
85 | 'phpstan-template',
86 | 'phpstan-extends',
87 | 'phpstan-param',
88 | 'param',
89 | 'param-out',
90 | 'phpstan-var',
91 | 'var',
92 | 'return',
93 | 'phpstan-return',
94 | 'property',
95 | 'property-read',
96 | 'property-write',
97 | 'method',
98 | ],
99 | // phpunit specific
100 | [
101 | 'test',
102 | 'dataProvider',
103 | 'covers',
104 | 'coversDefaultClass',
105 | 'coversNothing',
106 | 'depends',
107 | 'requires',
108 | ],
109 | ],
110 | ],
111 | 'phpdoc_single_line_var_spacing' => true,
112 | 'phpdoc_summary' => true,
113 | 'phpdoc_to_comment' => false,
114 | 'phpdoc_trim' => true,
115 | 'phpdoc_types' => true,
116 | 'phpdoc_var_without_name' => false,
117 | 'self_accessor' => true,
118 | 'short_scalar_cast' => true,
119 | 'single_class_element_per_statement' => true,
120 | 'space_after_semicolon' => true,
121 | 'standardize_not_equals' => true,
122 | 'trailing_comma_in_multiline' => ['elements' => ['arrays']],
123 | 'trim_array_spaces' => true,
124 | 'whitespace_after_comma_in_array' => true,
125 | ])
126 | ->setLineEnding("\n");
127 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📣 Dismissibles for Laravel
2 |
3 | 
4 |
5 | A Laravel package for easily managing the visibility of your recurring, dismissible objects like popups/notifications/modals on the backend. This package does not include frontend components, so it's compatible with any frontend you can use.
6 |
7 | ## 📕 Table of Contents
8 |
9 | - [✅ What problem does this solve?](#-what-problem-does-this-solve)
10 | - [📦 Installation](#-installation)
11 | - [❓ How to use](#-how-to-use)
12 | - [❗ Good to know](#-good-to-know)
13 | - [💾 Database tables](#-database-tables)
14 | - [☕ Buy me a coffee](#-buy-me-a-coffee)
15 |
16 | ## ✅ What problem does this solve?
17 | Say you have a popup you want to show to every user, daily for a week. Users can dismiss it and it should not show up again for the rest of the day until the next day.
18 |
19 | This packages handles the complex logic regarding whether the (dismissible) popup should be visible to the current user at the current moment. It basically handles the visibility of your dismissible. It's highly customizable, making it very flexible for many scenario's.
20 |
21 | Because it's serverside we can easily get statistics like who dismissed what, when and where.
22 |
23 | ## 📦 Installation
24 | 1. Require the package in your Laravel application
25 | ```shell
26 | composer require rellix/dismissibles-for-laravel
27 | ```
28 |
29 | 2. Run the migrations to create the database tables
30 | ```shell
31 | php artisan migrate
32 | ```
33 |
34 | ## ❓ How to use
35 |
36 | ### 1. Add the interface and trait to any model
37 | ```php
38 | use Rellix\Dismissibles\Contracts\Dismisser;
39 | use Rellix\Dismissibles\Traits\HasDismissibles;
40 |
41 | class User implements Dismisser
42 | {
43 | use HasDismissibles;
44 |
45 | ...
46 | }
47 |
48 | ```
49 |
50 | ### 2. Create a dismissible (migration)
51 | ```php
52 | use Illuminate\Support\Facades\Date;
53 | use Illuminate\Support\Facades\DB;
54 |
55 | return new class () extends Migration {
56 | public function up(): void
57 | {
58 | DB::table('dismissibles')->insert([
59 | 'name' => 'Test Popup', // This is your **unique** identifier
60 | 'active_from' => Date::createFromFormat('d-m-Y', '01-03-2024'),
61 | 'active_until' => null, // Optional end date
62 | 'created_at' => Date::now(),
63 | 'updated_at' => Date::now(),
64 | ]);
65 | }
66 | };
67 | ```
68 |
69 | and run your created migration:
70 | ```php
71 | php artisan migrate
72 | ```
73 |
74 |
75 |
76 | 💡 You can also create/fetch a Dismissible inline using the "active"-scope and "firstOrCreate".
77 |
78 | ```php
79 | Dismissible::active()->firstOrCreate(
80 | ['name' => 'Test Popup'],
81 | [
82 | 'active_from' => Date::createFromFormat('d-m-Y', '01-03-2024'),
83 | 'active_until' => null,
84 | 'created_at' => Date::now(),
85 | 'updated_at' => Date::now(),
86 | ]
87 | );
88 | ```
89 |
90 |
91 |
92 | ### 3. Check if it should be visible at the current moment
93 | ```php
94 | use Rellix\Dismissibles\Facades\Dismissibles;
95 |
96 | $showPopup = Dismissibles::shouldBeVisible('Test Popup', $user);
97 |
98 | // Here are some more examples, including ones with additional conditionals:
99 | $showPopup = Dismissibles::shouldBeVisible('Happy New Year 2025 Popup', $user);
100 | $showPopup = Dismissibles::shouldBeVisible('Newsletter signup modal', $user) && !$user->is_subscribed;
101 | $showPopup = Dismissibles::shouldBeVisible('Complete your profile notification', $user) && !$user->has_completed_profile;
102 | $showPopup = Dismissibles::shouldBeVisible('50% Off First Purchase Popup', $user) && !$user->has_orders;
103 |
104 | // You can also get all Dismissibles in one query (performance) and use the model methods.
105 | $dismissibles = Dismissibles::getAllFor($user);
106 |
107 | ```
108 |
109 |
110 |
111 | 💡 You can also use the individual models.
112 |
113 | ```php
114 | use Rellix\Dismissibles\Facades\Dismissibles;
115 |
116 | $popup = Dismissibles::get('Test Popup');
117 |
118 | $showPopup = $popup->shouldBeVisibleTo($user);
119 | ```
120 |
121 |
122 |
123 | ### 4. Dismiss it for a specified period
124 | ```php
125 | use Rellix\Dismissibles\Facades\Dismissibles;
126 |
127 | Dismissibles::dismiss('Test Popup', $user)->untilNextWeek();
128 |
129 | // Here's an overview of all the ways you can dismiss:
130 | Dismissibles::dismiss('Test Popup', $user)
131 | ->untilTomorrow();
132 | ->untilNextWeek();
133 | ->untilNextMonth();
134 | ->untilNextQuarter();
135 | ->untilNextYear();
136 | ->until($dateTime);
137 | ->forHours($numberOfHours);
138 | ->forDays($numberOfDays);
139 | ->forWeeks($numberOfWeeks);
140 | ->forMonths($numberOfMonths);
141 | ->forYears($numberOfYears);
142 | ->forever();
143 | ```
144 |
145 |
146 |
147 | 💡 You can also use the individual models.
148 |
149 | ```php
150 | use Rellix\Dismissibles\Facades\Dismissibles;
151 |
152 | $popup = Dismissibles::get('Test Popup');
153 |
154 | // Here's an overview of all the ways you can dismiss:
155 | $popup->dismissFor($user)
156 | ->untilTomorrow();
157 | ->untilNextWeek();
158 | ->untilNextMonth();
159 | ->untilNextQuarter();
160 | ->untilNextYear();
161 | ->until($dateTime);
162 | ->forHours($numberOfHours);
163 | ->forDays($numberOfDays);
164 | ->forWeeks($numberOfWeeks);
165 | ->forMonths($numberOfMonths);
166 | ->forYears($numberOfYears);
167 | ->forever();
168 | ```
169 |
170 |
171 |
172 | ## ❗ Good to know
173 | - The facade contains some oneliners by `$name`, but you can also use the scopes/methods in the `Dismissible` and `Dismissal` Eloquent models as you wish for ultimate flexibility.
174 | - It's recommended to centralize dismissible names in an enum (or config)
175 | - Need extra data regarding the dismissal? All dismiss methods allow you to pass an `$extraData` array as last parameter which will be written to the `dismissals` table as json.
176 | - Feel free to request more methods/scopes
177 |
178 | ## 💾 Database tables
179 | The database structure allows you to easily track activity regarding dismissibles. Due to the `extra_data` column it's also very flexible!
180 |
181 | ### dismissibles (popups, notifications, modals)
182 | | id | name | active_from | active_until | created_at | updated_at |
183 | |----|------------|---------------------|--------------|---------------------|---------------------|
184 | | 3 | Test Popup | 2024-03-01 00:00:00 | null | 2023-12-15 17:35:54 | 2023-12-15 17:35:54 |
185 |
186 |
187 | ### dismissals (activity)
188 | | id | dismissible_id | dismisser_type | dismisser_id | dismissed_until | extra_data | created_at | updated_at |
189 | |----|----------------|-----------------|--------------|---------------------|------------------------------|---------------------|---------------------|
190 | | 15 | 3 | App\Models\User | 328 | 2024-04-29 00:00:00 | "{\"route\":\"home.index\"}" | 2024-04-28 17:35:54 | 2024-04-28 17:35:54 |
191 |
192 | ## ☕ Buy me a coffee
193 | If you like this package, consider [buying me a coffee](https://www.paypal.com/donate/?business=E6QBKXWLXMD92&no_recurring=1&item_name=Buy+me+a+coffee¤cy_code=EUR) :-).
194 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rellix/dismissibles-for-laravel",
3 | "description": "A Laravel package for easily handling the visibility of dismissible, recurring objects like popups/notifications/modals on the server side.",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "laravel",
8 | "dismissibles",
9 | "dismissible",
10 | "dismiss",
11 | "popup",
12 | "notification",
13 | "modal",
14 | "recurring",
15 | "serverside"
16 | ],
17 | "scripts": {
18 | "test": "./vendor/bin/phpunit",
19 | "test-coverage": "./vendor/bin/phpunit --coverage-text",
20 | "phpstan": "./vendor/bin/phpstan analyse -c phpstan.neon"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "Rellix\\Dismissibles\\": "src/",
25 | "Rellix\\Dismissibles\\Database\\Factories\\": "database/factories"
26 | }
27 | },
28 | "autoload-dev": {
29 | "psr-4": {
30 | "Rellix\\Dismissibles\\Tests\\": "tests"
31 | }
32 | },
33 | "minimum-stability": "dev",
34 | "prefer-stable": true,
35 | "require": {
36 | "php": "^8.1",
37 | "laravel/framework": ">=7.0"
38 | },
39 | "extra": {
40 | "laravel": {
41 | "providers": [
42 | "Rellix\\Dismissibles\\DismissiblesServiceProvider"
43 | ],
44 | "aliases": {
45 | "Dismissibles": "Rellix\\Dismissibles\\Facades\\Dismissibles"
46 | }
47 | }
48 | },
49 | "require-dev": {
50 | "friendsofphp/php-cs-fixer": "^3.54",
51 | "orchestra/testbench": "^9.0",
52 | "phpstan/phpstan": "^1.10"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/database/factories/DismissalFactory.php:
--------------------------------------------------------------------------------
1 | fn () => TestDismisserTypeOne::factory()->create(),
20 | 'dismisser_type' => TestDismisserTypeOne::class,
21 | 'dismissible_id' => fn () => Dismissible::factory()->create(),
22 | 'dismissed_until' => function (array $attributes) {
23 | if ($this->faker->optional()) {
24 | return null;
25 | }
26 |
27 | $dismissible = Dismissible::find($attributes['dismissible_id']);
28 |
29 | return $this->faker->dateTimeBetween($dismissible->active_from, $dismissible->active_until);
30 | },
31 | 'extra_data' => $this->faker->optional() ? ['some_extra_data' => $this->faker->randomNumber()] : null,
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/database/factories/DismissibleFactory.php:
--------------------------------------------------------------------------------
1 | faker->dateTimeBetween('-3 weeks', 'now');
18 |
19 | return [
20 | 'name' => $this->faker->unique()->text(),
21 | 'active_from' => $activeFrom,
22 | 'active_until' => $this->faker->optional() ? $this->faker->dateTimeBetween($activeFrom, '+3 weeks') : null,
23 | ];
24 | }
25 |
26 | public function active(?CarbonPeriod $period = null): Factory
27 | {
28 | if ($period === null) {
29 | $activeFrom = $this->faker->dateTimeBetween('-4 weeks', '-1 week');
30 | $activeUntil = $this->faker->optional() ? $this->faker->dateTimeBetween('+1 week', '+4 weeks') : null;
31 |
32 | $period = CarbonPeriod::create($activeFrom, $activeUntil);
33 | }
34 |
35 | return $this->state(function (array $attributes) use ($period) {
36 | return [
37 | 'active_from' => $period->start,
38 | 'active_until' => $period->end,
39 | ];
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/database/factories/TestDismisserTypeOneFactory.php:
--------------------------------------------------------------------------------
1 | id();
14 | $table->string('name')->unique();
15 | $table->dateTime('active_from');
16 | $table->dateTime('active_until')->nullable();
17 | $table->timestamps();
18 | });
19 |
20 | Schema::create('dismissals', function (Blueprint $table) {
21 | $table->id();
22 | $table->unsignedBigInteger('dismissible_id');
23 | $table->morphs('dismisser');
24 | $table->dateTime('dismissed_until')->nullable();
25 | $table->json('extra_data')->nullable();
26 | $table->timestamps();
27 |
28 | $table
29 | ->foreign('dismissible_id')
30 | ->references('id')
31 | ->on('dismissibles')
32 | ->cascadeOnUpdate()
33 | ->cascadeOnDelete();
34 |
35 | $table->unique(
36 | ['dismissible_id', 'dismisser_type', 'dismisser_id', 'dismissed_until'],
37 | 'dismisser_dismissible_until_unique'
38 | );
39 | });
40 | }
41 |
42 | public function down(): void
43 | {
44 | Schema::dropIfExists('dismissibles');
45 | Schema::dropIfExists('dismissals');
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/images/dismissibles-for-laravel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rellix999/dismissibles-for-laravel/bfa1278a0a4cb8357b9a07fd15b0577d8f1725ed/images/dismissibles-for-laravel.jpg
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | level: 9
3 | paths:
4 | - src
5 | ignoreErrors:
6 | # Since $extraData comes from the library consumer, we don't know the structure/contents:
7 | -
8 | message: '#Method [a-zA-Z0-9\\:()]+ has parameter \$extraData with no value type specified in iterable type array#'
9 | path: src/Concerns/Dismiss.php
10 | # Laravel magic:
11 | - message: '#Call to an undefined static method Rellix\\Dismissibles\\Models\\Dismissible::active\(\).#'
12 | - message: '#Call to an undefined static method Rellix\\Dismissibles\\Models\\Dismissible::firstWhere\(\).#'
13 | - message: '#Call to an undefined static method Rellix\\Dismissibles\\Models\\Dismissible::visibleTo\(\).#'
14 | - message: '#Call to an undefined method Illuminate\\Database\\Eloquent\\Relations\\HasMany::dismissedBy\(\).#'
15 | - message: '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::dismissedBy\(\).#'
16 | - message: '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder::active\(\).#'
17 | - message: '#Access to an undefined property Rellix\\Dismissibles\\Contracts\\Dismisser::\$id#'
18 | - message: '#Parameter \#[0-9] of method Illuminate\\Database\\Eloquent\\Relations\\MorphTo::associate() expects Illuminate\\Database\\Eloquent\\Model|null, Rellix\\Dismissibles\\Contracts\\Dismisser given#'
19 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./tests
15 |
16 |
17 |
18 |
19 |
20 | ./src
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Concerns/Dismiss.php:
--------------------------------------------------------------------------------
1 | $dismissibles */
17 | private function __construct(
18 | public readonly Dismisser $dismisser,
19 | public readonly Collection $dismissibles
20 | ) {
21 | }
22 |
23 | public static function single(Dismisser $dismisser, Dismissible $dismissible): self
24 | {
25 | return new self($dismisser, new Collection([$dismissible]));
26 | }
27 |
28 | /** @param Collection $dismissibles */
29 | public static function multiple(Dismisser $dismisser, Collection $dismissibles): self
30 | {
31 | return new self($dismisser, $dismissibles);
32 | }
33 |
34 | public function untilTomorrow(?array $extraData = null): void
35 | {
36 | $until = Carbon::tomorrow();
37 |
38 | $this->dismiss($until, $extraData);
39 | }
40 |
41 | public function untilNextWeek(?array $extraData = null): void
42 | {
43 | $until = Carbon::now()->addWeek()->startOfWeek();
44 |
45 | $this->dismiss($until, $extraData);
46 | }
47 |
48 | public function untilNextMonth(?array $extraData = null): void
49 | {
50 | $until = Carbon::now()->addMonth()->startOfMonth();
51 |
52 | $this->dismiss($until, $extraData);
53 | }
54 |
55 | public function untilNextQuarter(?array $extraData = null): void
56 | {
57 | $until = Carbon::now()->addQuarter()->startOfQuarter();
58 |
59 | $this->dismiss($until, $extraData);
60 | }
61 |
62 | public function untilNextYear(?array $extraData = null): void
63 | {
64 | $until = Carbon::now()->addYear()->startOfYear();
65 |
66 | $this->dismiss($until, $extraData);
67 | }
68 |
69 | public function until(DateTimeInterface $dateTime, ?array $extraData = null): void
70 | {
71 | $this->dismiss($dateTime, $extraData);
72 | }
73 |
74 | public function forHours(int $hours, ?array $extraData = null): void
75 | {
76 | $until = Carbon::now()->addHours($hours);
77 |
78 | $this->dismiss($until, $extraData);
79 | }
80 |
81 | public function forDays(int $days, ?array $extraData = null): void
82 | {
83 | $until = Carbon::now()->addDays($days);
84 |
85 | $this->dismiss($until, $extraData);
86 | }
87 |
88 | public function forWeeks(int $weeks, ?array $extraData = null): void
89 | {
90 | $until = Carbon::now()->addWeeks($weeks);
91 |
92 | $this->dismiss($until, $extraData);
93 | }
94 |
95 | public function forMonths(int $months, ?array $extraData = null): void
96 | {
97 | $until = Carbon::now()->addMonths($months);
98 |
99 | $this->dismiss($until, $extraData);
100 | }
101 |
102 | public function forYears(int $years, ?array $extraData = null): void
103 | {
104 | $until = Carbon::now()->addYears($years);
105 |
106 | $this->dismiss($until, $extraData);
107 | }
108 |
109 | public function forever(?array $extraData = null): void
110 | {
111 | $this->dismiss(null, $extraData);
112 | }
113 |
114 | private function dismiss(?DateTimeInterface $until = null, ?array $extraData = null): void
115 | {
116 | foreach ($this->dismissibles as $dismissible) {
117 | $dismissal = new Dismissal([
118 | 'dismissed_until' => $until,
119 | 'extra_data' => $extraData ? json_encode($extraData) : null,
120 | ]);
121 |
122 | $dismissal->dismisser()->associate($this->dismisser);
123 | $dismissal->dismissible()->associate($dismissible);
124 |
125 | $dismissal->save();
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/Contracts/Dismisser.php:
--------------------------------------------------------------------------------
1 | loadMigrationsFrom(__DIR__ . '/../database/migrations');
16 |
17 | if (App::environment() === 'testing') {
18 | $this->loadMigrationsFrom(__DIR__ . '/../tests/database/migrations');
19 | }
20 | }
21 |
22 | public function register(): void
23 | {
24 | $this->app->bind('dismissibles', function ($app) {
25 | return new Dismissibles();
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Facades/Dismissibles.php:
--------------------------------------------------------------------------------
1 | firstWhere('name', $name);
26 | }
27 |
28 | /**
29 | * Returns all active dismissibles that should be visible to the $dismisser.
30 | *
31 | * @return Collection
32 | */
33 | public static function getAllFor(Dismisser $dismisser): Collection
34 | {
35 | return Dismissible::visibleTo($dismisser)->orderBy('active_from', 'asc')->get();
36 | }
37 |
38 | /**
39 | * Returns whether the dismissible should be visible to the dismisser at the current moment.
40 | */
41 | public static function shouldBeVisible(string $name, Dismisser $dismisser): bool
42 | {
43 | $dismissible = self::get($name);
44 | if (!$dismissible) {
45 | return false;
46 | }
47 |
48 | return $dismissible->shouldBeVisibleTo($dismisser);
49 | }
50 |
51 | /**
52 | * Returns a Dismiss object which allows you to dismiss the dismissible for a specified period.
53 | */
54 | public static function dismiss(string $name, Dismisser $dismisser): ?Dismiss
55 | {
56 | $dismissible = self::get($name);
57 | if (!$dismissible) {
58 | return null;
59 | }
60 |
61 | return $dismissible->dismissFor($dismisser);
62 | }
63 |
64 | /**
65 | * Returns whether a dismissible is currently dismissed by the dismisser.
66 | */
67 | public static function isDismissed(string $name, Dismisser $dismisser): bool
68 | {
69 | /** @var Dismissible $dismissible */
70 | $dismissible = Dismissible::firstWhere('name', $name);
71 |
72 | return $dismissible->isDismissedBy($dismisser);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Models/Dismissal.php:
--------------------------------------------------------------------------------
1 | */
26 | protected $casts = [
27 | 'dismissed_until' => 'immutable_datetime',
28 | 'extra_data' => 'array',
29 | ];
30 |
31 | protected static function newFactory(): DismissalFactory
32 | {
33 | return DismissalFactory::new();
34 | }
35 |
36 | public function dismissible(): BelongsTo
37 | {
38 | return $this->belongsTo(Dismissible::class);
39 | }
40 |
41 | public function dismisser(): MorphTo
42 | {
43 | return $this->morphTo();
44 | }
45 |
46 | public function scopeDismissedBy(Builder $query, Dismisser $dismisser): void
47 | {
48 | $query
49 | ->where('dismisser_type', get_class($dismisser))
50 | ->where('dismisser_id', $dismisser->id);
51 | }
52 |
53 | public function scopeDismissedAt(Builder $query, ?Carbon $moment = null): void
54 | {
55 | if (!$moment) {
56 | $moment = Carbon::now();
57 | }
58 |
59 | $query
60 | ->where('dismissed_until', '>', $moment)
61 | ->orWhereNull('dismissed_until');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Models/Dismissible.php:
--------------------------------------------------------------------------------
1 | */
32 | protected $casts = [
33 | 'active_from' => 'immutable_datetime',
34 | 'active_until' => 'immutable_datetime',
35 | ];
36 |
37 | protected static function newFactory(): DismissibleFactory
38 | {
39 | return DismissibleFactory::new();
40 | }
41 |
42 | public function dismissals(): HasMany
43 | {
44 | return $this->hasMany(Dismissal::class);
45 | }
46 |
47 | public function activePeriod(): CarbonPeriod
48 | {
49 | return CarbonPeriod::create($this->active_from, $this->active_until);
50 | }
51 |
52 | public function shouldBeVisibleTo(Dismisser $dismisser): bool
53 | {
54 | return !$this->isDismissedBy($dismisser);
55 | }
56 |
57 | public function dismissFor(Dismisser $dismisser): Dismiss
58 | {
59 | return Dismiss::single($dismisser, $this);
60 | }
61 |
62 | public function isDismissedBy(Dismisser $dismisser, ?Carbon $moment = null): bool
63 | {
64 | if (!$moment) {
65 | $moment = Carbon::now();
66 | }
67 |
68 | return $this->dismissals()
69 | ->dismissedBy($dismisser)
70 | ->dismissedAt($moment)
71 | ->exists();
72 | }
73 |
74 | public function scopeVisibleTo(Builder $query, Dismisser $dismisser): void
75 | {
76 | $query->active()->notDismissedBy($dismisser);
77 | }
78 |
79 | public function scopeActive(Builder $query, ?Carbon $moment = null): void
80 | {
81 | if (!$moment) {
82 | $moment = Carbon::now();
83 | }
84 |
85 | $query
86 | ->where('active_from', '<=', $moment)
87 | ->where(function (Builder $query) use ($moment) {
88 | $query
89 | ->where('active_until', '>', $moment)
90 | ->orWhereNull('active_until');
91 | });
92 | }
93 |
94 | public function scopeNotDismissedBy(Builder $query, Dismisser $dismisser, ?Carbon $moment = null): void
95 | {
96 | if (!$moment) {
97 | $moment = Carbon::now();
98 | }
99 |
100 | $query->whereDoesntHave('dismissals', function (Builder $query) use ($dismisser, $moment) {
101 | $query->dismissedBy($dismisser)->dismissedAt($moment);
102 | });
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/Models/TestDismisserTypeOne.php:
--------------------------------------------------------------------------------
1 | morphMany(Dismissal::class, 'dismisser');
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/BaseTestCase.php:
--------------------------------------------------------------------------------
1 | artisan('migrate', ['--database' => 'test'])->run();
17 | }
18 |
19 | protected function getPackageProviders($app)
20 | {
21 | return [DismissiblesServiceProvider::class];
22 | }
23 |
24 | protected function getEnvironmentSetUp($app)
25 | {
26 | $app['config']->set('database.default', 'test');
27 |
28 | $app['config']->set('database.connections.test', [
29 | 'driver' => 'sqlite',
30 | 'database' => ':memory:',
31 | 'prefix' => '',
32 | ]);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/Unit/Concerns/DismissTest.php:
--------------------------------------------------------------------------------
1 | dismissible = Dismissible::factory()->create();
31 | $this->dismisser = TestDismisserTypeOne::factory()->create();
32 | $this->dismiss = Dismiss::single($this->dismisser, $this->dismissible);
33 | }
34 |
35 | #[Test]
36 | public function it_creates_a_dismissal_with_the_correct_type_and_id_for_type_one()
37 | {
38 | $dismissible = Dismissible::factory()->create();
39 | $dismisser = TestDismisserTypeOne::factory()->create();
40 | $dismiss = Dismiss::single($dismisser, $dismissible);
41 |
42 | $dismiss->untilTomorrow();
43 |
44 | /** @var Dismissal $dismissal */
45 | $dismissal = Dismissal::orderBy('id', 'desc')->first();
46 |
47 | $this->assertDatabaseHas('dismissals', [
48 | 'id' => $dismissal->id,
49 | 'dismisser_type' => TestDismisserTypeOne::class,
50 | 'dismisser_id' => $dismisser->id,
51 | ]);
52 | }
53 |
54 | #[Test]
55 | public function it_creates_a_dismissal_with_the_correct_type_and_id_for_type_two()
56 | {
57 | $dismissible = Dismissible::factory()->create();
58 | $dismisser = TestDismisserTypeTwo::factory()->create();
59 | $dismiss = Dismiss::single($dismisser, $dismissible);
60 |
61 | $dismiss->untilTomorrow();
62 |
63 | /** @var Dismissal $dismissal */
64 | $dismissal = Dismissal::orderBy('id', 'desc')->first();
65 |
66 | $this->assertDatabaseHas('dismissals', [
67 | 'id' => $dismissal->id,
68 | 'dismisser_type' => TestDismisserTypeTwo::class,
69 | 'dismisser_id' => $dismisser->id,
70 | ]);
71 | }
72 |
73 | #[Test]
74 | #[DataProvider('untilTomorrowDataProvider')]
75 | public function until_tomorrow(string $now, string $expectedDismissedUntil)
76 | {
77 | $this->setTestNow($now);
78 |
79 | $this->dismiss->untilTomorrow();
80 |
81 | $expectedData = $this->getExpectedDismissalData([
82 | 'dismissed_until' => $expectedDismissedUntil,
83 | ]);
84 |
85 | $this->assertDatabaseHas('dismissals', $expectedData);
86 | }
87 |
88 | #[Test]
89 | #[DataProvider('untilNextWeekDataProvider')]
90 | public function until_next_week(string $now, string $expectedDismissedUntil)
91 | {
92 | $this->setTestNow($now);
93 |
94 | $this->dismiss->untilNextWeek();
95 |
96 | $expectedData = $this->getExpectedDismissalData([
97 | 'dismissed_until' => $expectedDismissedUntil,
98 | ]);
99 |
100 | $this->assertDatabaseHas('dismissals', $expectedData);
101 | }
102 |
103 | #[Test]
104 | #[DataProvider('untilNextMonthDataProvider')]
105 | public function until_next_month(string $now, string $expectedDismissedUntil)
106 | {
107 | $this->setTestNow($now);
108 |
109 | $this->dismiss->untilNextMonth();
110 |
111 | $expectedData = $this->getExpectedDismissalData([
112 | 'dismissed_until' => $expectedDismissedUntil,
113 | ]);
114 |
115 | $this->assertDatabaseHas('dismissals', $expectedData);
116 | }
117 |
118 | #[Test]
119 | #[DataProvider('untilNextQuarterDataProvider')]
120 | public function until_next_quarter(string $now, string $expectedDismissedUntil)
121 | {
122 | $this->setTestNow($now);
123 |
124 | $this->dismiss->untilNextQuarter();
125 |
126 | $expectedData = $this->getExpectedDismissalData([
127 | 'dismissed_until' => $expectedDismissedUntil,
128 | ]);
129 |
130 | $this->assertDatabaseHas('dismissals', $expectedData);
131 | }
132 |
133 | #[Test]
134 | #[DataProvider('untilNextYearDataProvider')]
135 | public function until_next_year(string $now, string $expectedDismissedUntil)
136 | {
137 | $this->setTestNow($now);
138 |
139 | $this->dismiss->untilNextYear();
140 |
141 | $expectedData = $this->getExpectedDismissalData([
142 | 'dismissed_until' => $expectedDismissedUntil,
143 | ]);
144 |
145 | $this->assertDatabaseHas('dismissals', $expectedData);
146 | }
147 |
148 | #[Test]
149 | #[DataProvider('untilDataProvider')]
150 | public function until(string $now, DateTimeInterface $dateTime, string $expectedDismissedUntil)
151 | {
152 | $this->setTestNow($now);
153 |
154 | $this->dismiss->until($dateTime);
155 |
156 | $expectedData = $this->getExpectedDismissalData([
157 | 'dismissed_until' => $expectedDismissedUntil,
158 | ]);
159 |
160 | $this->assertDatabaseHas('dismissals', $expectedData);
161 | }
162 |
163 | #[Test]
164 | #[DataProvider('forHoursDataProvider')]
165 | public function for_hours(string $now, int $hours, string $expectedDismissedUntil)
166 | {
167 | $this->setTestNow($now);
168 |
169 | $this->dismiss->forHours($hours);
170 |
171 | $expectedData = $this->getExpectedDismissalData([
172 | 'dismissed_until' => $expectedDismissedUntil,
173 | ]);
174 |
175 | $this->assertDatabaseHas('dismissals', $expectedData);
176 | }
177 |
178 | #[Test]
179 | #[DataProvider('forDaysDataProvider')]
180 | public function for_days(string $now, int $days, string $expectedDismissedUntil)
181 | {
182 | $this->setTestNow($now);
183 |
184 | $this->dismiss->forDays($days);
185 |
186 | $expectedData = $this->getExpectedDismissalData([
187 | 'dismissed_until' => $expectedDismissedUntil,
188 | ]);
189 |
190 | $this->assertDatabaseHas('dismissals', $expectedData);
191 | }
192 |
193 | #[Test]
194 | #[DataProvider('forWeeksDataProvider')]
195 | public function for_weeks(string $now, int $weeks, string $expectedDismissedUntil)
196 | {
197 | $this->setTestNow($now);
198 |
199 | $this->dismiss->forWeeks($weeks);
200 |
201 | $expectedData = $this->getExpectedDismissalData([
202 | 'dismissed_until' => $expectedDismissedUntil,
203 | ]);
204 |
205 | $this->assertDatabaseHas('dismissals', $expectedData);
206 | }
207 |
208 | #[Test]
209 | #[DataProvider('forMonthsDataProvider')]
210 | public function for_months(string $now, int $months, string $expectedDismissedUntil)
211 | {
212 | $this->setTestNow($now);
213 |
214 | $this->dismiss->forMonths($months);
215 |
216 | $expectedData = $this->getExpectedDismissalData([
217 | 'dismissed_until' => $expectedDismissedUntil,
218 | ]);
219 |
220 | $this->assertDatabaseHas('dismissals', $expectedData);
221 | }
222 |
223 | #[Test]
224 | #[DataProvider('forYearsDataProvider')]
225 | public function for_years(string $now, int $years, string $expectedDismissedUntil)
226 | {
227 | $this->setTestNow($now);
228 |
229 | $this->dismiss->forYears($years);
230 |
231 | $expectedData = $this->getExpectedDismissalData([
232 | 'dismissed_until' => $expectedDismissedUntil,
233 | ]);
234 |
235 | $this->assertDatabaseHas('dismissals', $expectedData);
236 | }
237 |
238 | #[Test]
239 | #[DataProvider('foreverDataProvider')]
240 | public function forever(string $now)
241 | {
242 | $this->setTestNow($now);
243 |
244 | $this->dismiss->forever();
245 |
246 | $expectedData = $this->getExpectedDismissalData([
247 | 'dismissed_until' => null,
248 | ]);
249 |
250 | $this->assertDatabaseHas('dismissals', $expectedData);
251 | }
252 |
253 | public static function untilTomorrowDataProvider(): array
254 | {
255 | return [
256 | ['2023-01-01 00:00:00', '2023-01-02 00:00:00'],
257 | ['2023-08-16 14:00:00', '2023-08-17 00:00:00'],
258 | ['2023-12-31 23:59:59', '2024-01-01 00:00:00'],
259 | ];
260 | }
261 |
262 | public static function untilNextWeekDataProvider()
263 | {
264 | return [
265 | ['2023-08-16 14:00:00', '2023-08-21 00:00:00'],
266 | ['2023-09-01 12:00:00', '2023-09-04 00:00:00'],
267 | ];
268 | }
269 |
270 | public static function untilNextMonthDataProvider()
271 | {
272 | return [
273 | ['2023-08-16 14:00:00', '2023-09-01 00:00:00'],
274 | ['2023-09-01 13:01:59', '2023-10-01 00:00:00'],
275 | ];
276 | }
277 |
278 | public static function untilNextQuarterDataProvider()
279 | {
280 | return [
281 | ['2023-08-16 14:00:00', '2023-10-01 00:00:00'],
282 | ['2023-01-01 22:10:13', '2023-04-01 00:00:00'],
283 | ];
284 | }
285 |
286 | public static function untilNextYearDataProvider()
287 | {
288 | return [
289 | ['2023-08-16 14:00:00', '2024-01-01 00:00:00'],
290 | ['2023-01-01 14:00:00', '2024-01-01 00:00:00'],
291 | ['2024-12-31 14:00:00', '2025-01-01 00:00:00'],
292 | ];
293 | }
294 |
295 | public static function untilDataProvider()
296 | {
297 | return [
298 | [
299 | '2023-08-16 14:00:00',
300 | Carbon::createFromFormat('d-m-Y H:i:s', '20-08-2023 12:32:02'),
301 | '2023-08-20 12:32:02',
302 | ],
303 | [
304 | '2023-01-01 14:00:00',
305 | Carbon::createFromFormat('d-m-Y H:i:s', '01-01-2023 14:00:01'),
306 | '2023-01-01 14:00:01',
307 | ],
308 | ];
309 | }
310 |
311 | public static function forHoursDataProvider(): array
312 | {
313 | return [
314 | ['2023-08-16 14:00:00', 1, '2023-08-16 15:00:00'],
315 | ['2023-08-16 14:00:00', 2, '2023-08-16 16:00:00'],
316 | ['2023-08-16 14:00:00', 10, '2023-08-17 00:00:00'],
317 | ['2023-08-16 14:00:00', 24, '2023-08-17 14:00:00'],
318 | ['2023-08-16 14:00:00', 48, '2023-08-18 14:00:00'],
319 | ['2023-08-16 14:00:00', 100, '2023-08-20 18:00:00'],
320 | ];
321 | }
322 |
323 | public static function forDaysDataProvider(): array
324 | {
325 | return [
326 | ['2023-08-16 14:00:00', 1, '2023-08-17 14:00:00'],
327 | ['2023-08-16 14:00:00', 2, '2023-08-18 14:00:00'],
328 | ['2023-08-16 14:00:00', 7, '2023-08-23 14:00:00'],
329 | ['2023-08-16 14:00:00', 100, '2023-11-24 14:00:00'],
330 | ['2023-08-16 14:00:00', 365, '2024-08-15 14:00:00'],
331 | ];
332 | }
333 |
334 | public static function forWeeksDataProvider(): array
335 | {
336 | return [
337 | ['2023-08-16 14:00:00', 1, '2023-08-23 14:00:00'],
338 | ['2023-08-16 14:00:00', 2, '2023-08-30 14:00:00'],
339 | ['2023-08-16 14:00:00', 7, '2023-10-04 14:00:00'],
340 | ['2023-08-16 14:00:00', 52, '2024-08-14 14:00:00'],
341 | ];
342 | }
343 |
344 | public static function forMonthsDataProvider(): array
345 | {
346 | return [
347 | ['2023-08-16 14:00:00', 1, '2023-09-16 14:00:00'],
348 | ['2023-08-16 14:00:00', 2, '2023-10-16 14:00:00'],
349 | ['2023-08-16 14:00:00', 6, '2024-02-16 14:00:00'],
350 | ['2023-08-16 14:00:00', 12, '2024-08-16 14:00:00'],
351 | ];
352 | }
353 |
354 | public static function forYearsDataProvider(): array
355 | {
356 | return [
357 | ['2023-08-16 14:00:00', 1, '2024-08-16 14:00:00'],
358 | ['2023-08-16 14:00:00', 2, '2025-08-16 14:00:00'],
359 | ['2023-08-16 14:00:00', 6, '2029-08-16 14:00:00'],
360 | ['2023-08-16 14:00:00', 12, '2035-08-16 14:00:00'],
361 | ['2023-08-16 14:00:00', 50, '2073-08-16 14:00:00'],
362 | ];
363 | }
364 |
365 | public static function foreverDataProvider()
366 | {
367 | return [
368 | ['2023-08-16 14:00:00'],
369 | ['2023-08-16 14:00:00'],
370 | ['2023-08-16 14:00:00'],
371 | ];
372 | }
373 |
374 | private function setTestNow(string $now)
375 | {
376 | Carbon::setTestNow(Carbon::createFromFormat('Y-m-d H:i:s', $now));
377 | }
378 |
379 | private function getExpectedDismissalData(array $expectedData): array
380 | {
381 | return [
382 | 'dismissible_id' => $this->dismissible->id,
383 | 'dismisser_id' => $this->dismisser->id,
384 | 'dismisser_type' => TestDismisserTypeOne::class,
385 | ...$expectedData,
386 | ];
387 | }
388 | }
389 |
--------------------------------------------------------------------------------
/tests/Unit/Facades/Dismissibles/DismissTest.php:
--------------------------------------------------------------------------------
1 | dismissible = Dismissible::factory()->active()->create();
27 | $this->dismisser = TestDismisserTypeOne::factory()->create();
28 | }
29 |
30 | #[Test]
31 | public function it_returns_null_before_active_period()
32 | {
33 | $now = CarbonImmutable::now();
34 |
35 | $dismissible = Dismissible::factory()
36 | ->active(CarbonPeriod::create($now->subWeek(), $now->subDay()))
37 | ->create();
38 |
39 | $actualValue = Dismissibles::dismiss($dismissible->name, $this->dismisser);
40 |
41 | $this->assertNull($actualValue);
42 | }
43 |
44 | #[Test]
45 | public function it_returns_an_object_during_active_period()
46 | {
47 | $actualValue = Dismissibles::dismiss($this->dismissible->name, $this->dismisser);
48 |
49 | $this->assertIsObject($actualValue);
50 | }
51 |
52 | #[Test]
53 | public function it_returns_a_dismiss_object_during_active_period()
54 | {
55 | $actualValue = Dismissibles::dismiss($this->dismissible->name, $this->dismisser);
56 |
57 | $this->assertInstanceOf(Dismiss::class, $actualValue);
58 | }
59 |
60 | #[Test]
61 | public function the_dismiss_object_has_the_correct_dismisser()
62 | {
63 | $actualValue = Dismissibles::dismiss($this->dismissible->name, $this->dismisser);
64 |
65 | $this->assertTrue($actualValue->dismisser->is($this->dismisser));
66 | }
67 |
68 | #[Test]
69 | public function the_dismiss_object_has_the_correct_dismissible_no_more_no_less()
70 | {
71 | $actualValue = Dismissibles::dismiss($this->dismissible->name, $this->dismisser);
72 |
73 | $dismissibleIds = $actualValue->dismissibles->pluck('id')->toArray();
74 |
75 | $this->assertEquals([$this->dismissible->id], $dismissibleIds);
76 | }
77 |
78 | #[Test]
79 | public function it_returns_null_after_active_period()
80 | {
81 | $now = CarbonImmutable::now();
82 |
83 | $dismissible = Dismissible::factory()
84 | ->active(CarbonPeriod::create($now->subWeek(), $now->subDay()))
85 | ->create();
86 |
87 | $actualValue = Dismissibles::dismiss($dismissible->name, $this->dismisser);
88 |
89 | $this->assertNull($actualValue);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tests/Unit/Facades/Dismissibles/GetAllForTest.php:
--------------------------------------------------------------------------------
1 | create();
23 |
24 | $actualResult = Dismissibles::getAllFor($dismisser);
25 |
26 | $this->assertInstanceOf(Collection::class, $actualResult);
27 | $this->assertCount(0, $actualResult);
28 | }
29 |
30 | #[Test]
31 | public function it_returns_no_inactive_dismissibles()
32 | {
33 | $dismisser = TestDismisserTypeOne::factory()->create();
34 |
35 | $now = CarbonImmutable::now();
36 |
37 | Dismissible::factory()
38 | ->active(CarbonPeriod::create($now->subWeek(), $now->subDay()))
39 | ->create();
40 |
41 | Dismissible::factory()
42 | ->active(CarbonPeriod::create($now->addDay(), $now->addWeek()))
43 | ->create();
44 |
45 | $actualResult = Dismissibles::getAllFor($dismisser);
46 |
47 | $this->assertInstanceOf(Collection::class, $actualResult);
48 | $this->assertCount(0, $actualResult);
49 | }
50 |
51 | #[Test]
52 | public function it_returns_active_dismissibles()
53 | {
54 | $dismisser = TestDismisserTypeOne::factory()->create();
55 |
56 | $now = CarbonImmutable::now();
57 |
58 | Dismissible::factory()
59 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeek()))
60 | ->create();
61 |
62 | $actualResult = Dismissibles::getAllFor($dismisser);
63 |
64 | $this->assertInstanceOf(Collection::class, $actualResult);
65 | $this->assertCount(1, $actualResult);
66 | }
67 |
68 | #[Test]
69 | public function it_returns_dismissibles_which_are_dismissed_until_past_date_time()
70 | {
71 | $dismisser = TestDismisserTypeOne::factory()->create();
72 |
73 | $now = CarbonImmutable::now();
74 |
75 | $dismissible = Dismissible::factory()
76 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeek()))
77 | ->create();
78 |
79 | Dismissal::factory()
80 | ->for($dismisser, 'dismisser')
81 | ->for($dismissible)
82 | ->create([
83 | 'dismissed_until' => $now->subDay(),
84 | ]);
85 |
86 | $actualResult = Dismissibles::getAllFor($dismisser);
87 |
88 | $this->assertInstanceOf(Collection::class, $actualResult);
89 | $this->assertCount(1, $actualResult);
90 | }
91 |
92 | #[Test]
93 | public function it_returns_dismissibles_which_are_dismissed_until_dismissible_active_from_date_time()
94 | {
95 | $dismisser = TestDismisserTypeOne::factory()->create();
96 |
97 | $now = CarbonImmutable::now();
98 |
99 | /** @var Dismissible $dismissible */
100 | $dismissible = Dismissible::factory()
101 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeek()))
102 | ->create();
103 |
104 | Dismissal::factory()
105 | ->for($dismisser, 'dismisser')
106 | ->for($dismissible)
107 | ->create([
108 | 'dismissed_until' => $dismissible->active_from,
109 | ]);
110 |
111 | $actualResult = Dismissibles::getAllFor($dismisser);
112 |
113 | $this->assertInstanceOf(Collection::class, $actualResult);
114 | $this->assertCount(1, $actualResult);
115 | }
116 |
117 | #[Test]
118 | public function it_returns_dismissibles_ordered_by_active_from_ascending()
119 | {
120 | $dismisser = TestDismisserTypeOne::factory()->create();
121 |
122 | $now = CarbonImmutable::now();
123 |
124 | /** @var Dismissible $dismissible */
125 | $newestDismissible = Dismissible::factory()
126 | ->active(CarbonPeriod::create($now->subDay(), $now->addWeek()))
127 | ->create();
128 |
129 | /** @var Dismissible $dismissible */
130 | $oldestDismissible = Dismissible::factory()
131 | ->active(CarbonPeriod::create($now->subMonth(), $now->addWeek()))
132 | ->create();
133 |
134 | /** @var Dismissible $dismissible */
135 | $middleDismissible = Dismissible::factory()
136 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeek()))
137 | ->create();
138 |
139 | $actualResult = Dismissibles::getAllFor($dismisser);
140 |
141 | $this->assertTrue($actualResult->get(0)->is($oldestDismissible));
142 | $this->assertTrue($actualResult->get(1)->is($middleDismissible));
143 | $this->assertTrue($actualResult->get(2)->is($newestDismissible));
144 | }
145 |
146 | #[Test]
147 | public function it_does_not_return_dismissibles_which_are_dismissed_until_future_date_time()
148 | {
149 | $dismisser = TestDismisserTypeOne::factory()->create();
150 |
151 | $now = CarbonImmutable::now();
152 |
153 | /** @var Dismissible $dismissible */
154 | $dismissible = Dismissible::factory()
155 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeek()))
156 | ->create();
157 |
158 | Dismissal::factory()
159 | ->for($dismisser, 'dismisser')
160 | ->for($dismissible)
161 | ->create([
162 | 'dismissed_until' => $now->addMinute(),
163 | ]);
164 |
165 | $actualResult = Dismissibles::getAllFor($dismisser);
166 |
167 | $this->assertInstanceOf(Collection::class, $actualResult);
168 | $this->assertCount(0, $actualResult);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/tests/Unit/Facades/Dismissibles/GetTest.php:
--------------------------------------------------------------------------------
1 | name = 'test';
23 | }
24 |
25 | #[Test]
26 | public function it_returns_null_when_name_does_not_exist()
27 | {
28 | $actualResult = Dismissibles::get($this->name);
29 |
30 | $this->assertNull($actualResult);
31 | }
32 |
33 | #[Test]
34 | public function it_returns_null_before_active_period()
35 | {
36 | $now = CarbonImmutable::now();
37 |
38 | Dismissible::factory()
39 | ->active(CarbonPeriod::create($now->addDay(), $now->addWeek()))
40 | ->create([
41 | 'name' => $this->name,
42 | ]);
43 |
44 | $actualResult = Dismissibles::get($this->name);
45 |
46 | $this->assertNull($actualResult);
47 | }
48 |
49 | #[Test]
50 | public function it_returns_dismissible_during_active_period_when_active_until_is_set()
51 | {
52 | $now = CarbonImmutable::now();
53 |
54 | $dismissible = Dismissible::factory()
55 | ->active(CarbonPeriod::create($now->subMinute(), $now->addDay()))
56 | ->create([
57 | 'name' => $this->name,
58 | ]);
59 |
60 | $actualResult = Dismissibles::get($this->name);
61 |
62 | $this->assertTrue($actualResult->is($dismissible));
63 | }
64 |
65 | #[Test]
66 | public function it_returns_dismissible_during_active_period_when_active_until_is_null()
67 | {
68 | $now = CarbonImmutable::now();
69 |
70 | $dismissible = Dismissible::factory()
71 | ->active(CarbonPeriod::create($now->subMinute()))
72 | ->create([
73 | 'name' => $this->name,
74 | ]);
75 |
76 | $actualResult = Dismissibles::get($this->name);
77 |
78 | $this->assertTrue($actualResult->is($dismissible));
79 | }
80 |
81 | #[Test]
82 | public function it_returns_null_after_active_period()
83 | {
84 | $now = CarbonImmutable::now();
85 |
86 | Dismissible::factory()
87 | ->active(CarbonPeriod::create($now->subWeek(), $now->subDay()))
88 | ->create([
89 | 'name' => $this->name,
90 | ]);
91 |
92 | $actualResult = Dismissibles::get($this->name);
93 |
94 | $this->assertNull($actualResult);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/Unit/Facades/Dismissibles/IsDismissedTest.php:
--------------------------------------------------------------------------------
1 | dismisser = TestDismisserTypeOne::factory()->create();
26 | }
27 |
28 | #[Test]
29 | public function it_returns_false_when_dismisser_has_not_dismissed()
30 | {
31 | /** @var Dismissible $dismissible */
32 | $dismissible = Dismissible::factory()->create();
33 |
34 | $actualValue = Dismissibles::isDismissed($dismissible->name, $this->dismisser);
35 |
36 | $this->assertFalse($actualValue);
37 | }
38 |
39 | #[Test]
40 | public function it_returns_false_when_dismisser_has_dismissed_different_dismissible_until_past_date()
41 | {
42 | /** @var Dismissible $dismissible */
43 | $dismissible = Dismissible::factory()->create();
44 |
45 | Dismissal::factory()
46 | ->for($this->dismisser, 'dismisser')
47 | ->create([
48 | 'dismissed_until' => Carbon::yesterday(),
49 | ]);
50 |
51 | $actualValue = Dismissibles::isDismissed($dismissible->name, $this->dismisser);
52 |
53 | $this->assertFalse($actualValue);
54 | }
55 |
56 | #[Test]
57 | public function it_returns_false_when_dismisser_has_dismissed_different_dismissible_until_future_date()
58 | {
59 | Dismissal::factory()
60 | ->for($this->dismisser, 'dismisser')
61 | ->create([
62 | 'dismissed_until' => Carbon::tomorrow(),
63 | ]);
64 |
65 | /** @var Dismissible $dismissible */
66 | $dismissible = Dismissible::factory()->create();
67 |
68 | $actualValue = Dismissibles::isDismissed($dismissible->name, $this->dismisser);
69 |
70 | $this->assertFalse($actualValue);
71 | }
72 |
73 | #[Test]
74 | public function it_returns_false_when_dismisser_has_dismissed_until_past_date()
75 | {
76 | /** @var Dismissible $dismissible */
77 | $dismissible = Dismissible::factory()->create();
78 |
79 | Dismissal::factory()
80 | ->for($dismissible)
81 | ->for($this->dismisser, 'dismisser')
82 | ->create([
83 | 'dismissed_until' => Carbon::now()->subDay(),
84 | ]);
85 |
86 | $actualValue = Dismissibles::isDismissed($dismissible->name, $this->dismisser);
87 |
88 | $this->assertFalse($actualValue);
89 | }
90 |
91 | #[Test]
92 | public function it_returns_true_when_dismisser_has_dismissed_until_future_date()
93 | {
94 | /** @var Dismissible $dismissible */
95 | $dismissible = Dismissible::factory()->create();
96 |
97 | Dismissal::factory()
98 | ->for($dismissible)
99 | ->for($this->dismisser, 'dismisser')
100 | ->create([
101 | 'dismissed_until' => Carbon::now()->addDay(),
102 | ]);
103 |
104 | $actualValue = Dismissibles::isDismissed($dismissible->name, $this->dismisser);
105 |
106 | $this->assertTrue($actualValue);
107 | }
108 |
109 | #[Test]
110 | public function it_returns_true_when_user_has_dismissed_until_null()
111 | {
112 | /** @var Dismissible $dismissible */
113 | $dismissible = Dismissible::factory()->create();
114 |
115 | Dismissal::factory()
116 | ->for($dismissible)
117 | ->for($this->dismisser, 'dismisser')
118 | ->create([
119 | 'dismissed_until' => null,
120 | ]);
121 |
122 | $actualValue = Dismissibles::isDismissed($dismissible->name, $this->dismisser);
123 |
124 | $this->assertTrue($actualValue);
125 | }
126 |
127 | #[Test]
128 | public function it_returns_the_correct_value_when_dismissal_with_same_id_but_different_type_exists()
129 | {
130 | TestDismisserTypeOne::truncate();
131 | TestDismisserTypeTwo::truncate();
132 |
133 | $dismisserOfTypeOne = TestDismisserTypeOne::factory()->create();
134 | $dismisserOfTypeTwo = TestDismisserTypeTwo::factory()->create();
135 |
136 | $this->assertEquals($dismisserOfTypeOne->id, $dismisserOfTypeTwo->id);
137 |
138 | /** @var Dismissible $dismissible */
139 | $dismissible = Dismissible::factory()->active()->create();
140 |
141 | Dismissal::factory()
142 | ->for($dismissible)
143 | ->for($dismisserOfTypeOne, 'dismisser')
144 | ->create([
145 | 'dismissed_until' => Carbon::tomorrow(),
146 | ]);
147 |
148 | $this->assertTrue(Dismissibles::isDismissed($dismissible->name, $dismisserOfTypeOne));
149 | $this->assertFalse(Dismissibles::isDismissed($dismissible->name, $dismisserOfTypeTwo));
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/tests/Unit/Facades/Dismissibles/ShouldShowTest.php:
--------------------------------------------------------------------------------
1 | dismisser = TestDismisserTypeOne::factory()->create();
26 | }
27 |
28 | #[Test]
29 | public function it_returns_false_when_dismissible_name_does_not_exist()
30 | {
31 | $actualValue = Dismissibles::shouldBeVisible('test', $this->dismisser);
32 |
33 | $this->assertFalse($actualValue);
34 | }
35 |
36 | #[Test]
37 | public function it_returns_false_before_active_period_not_dismissed()
38 | {
39 | $now = CarbonImmutable::now();
40 |
41 | $dismissible = Dismissible::factory()
42 | ->active(CarbonPeriod::create($now->addDay(), $now->addWeek()))
43 | ->create();
44 |
45 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
46 |
47 | $this->assertFalse($actualValue);
48 | }
49 |
50 | #[Test]
51 | public function it_returns_false_before_active_period_dismissed_in_past()
52 | {
53 | $now = CarbonImmutable::now();
54 |
55 | /** @var Dismissible $dismissible */
56 | $dismissible = Dismissible::factory()
57 | ->active(CarbonPeriod::create($now->addWeek(), $now->addWeeks(2)))
58 | ->create();
59 |
60 | Dismissal::factory()
61 | ->for($dismissible)
62 | ->for($this->dismisser, 'dismisser')
63 | ->create([
64 | 'dismissed_until' => $now->subDay(),
65 | ]);
66 |
67 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
68 |
69 | $this->assertFalse($actualValue);
70 | }
71 |
72 | #[Test]
73 | public function it_returns_false_before_active_period_dismissed_in_future()
74 | {
75 | $now = CarbonImmutable::now();
76 |
77 | /** @var Dismissible $dismissible */
78 | $dismissible = Dismissible::factory()
79 | ->active(CarbonPeriod::create($now->addWeek(), $now->addWeeks(2)))
80 | ->create();
81 |
82 | Dismissal::factory()
83 | ->for($dismissible)
84 | ->for($this->dismisser, 'dismisser')
85 | ->create([
86 | 'dismissed_until' => $now->addDay(),
87 | ]);
88 |
89 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
90 |
91 | $this->assertFalse($actualValue);
92 | }
93 |
94 | #[Test]
95 | public function it_returns_false_before_active_period_dismissed_forever()
96 | {
97 | $now = CarbonImmutable::now();
98 |
99 | /** @var Dismissible $dismissible */
100 | $dismissible = Dismissible::factory()
101 | ->active(CarbonPeriod::create($now->addWeek(), $now->addWeeks(2)))
102 | ->create();
103 |
104 | Dismissal::factory()
105 | ->for($dismissible)
106 | ->for($this->dismisser, 'dismisser')
107 | ->create([
108 | 'dismissed_until' => null,
109 | ]);
110 |
111 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
112 |
113 | $this->assertFalse($actualValue);
114 | }
115 |
116 | #[Test]
117 | public function it_returns_true_on_active_period_start_not_dismissed()
118 | {
119 | $now = CarbonImmutable::now();
120 |
121 | /** @var Dismissible $dismissible */
122 | $dismissible = Dismissible::factory()
123 | ->active(CarbonPeriod::create($now, $now->addWeeks(2)))
124 | ->create();
125 |
126 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
127 |
128 | $this->assertTrue($actualValue);
129 | }
130 |
131 | #[Test]
132 | public function it_returns_true_on_active_period_start_dismissed_in_past()
133 | {
134 | $now = CarbonImmutable::now();
135 |
136 | /** @var Dismissible $dismissible */
137 | $dismissible = Dismissible::factory()
138 | ->active(CarbonPeriod::create($now, $now->addWeeks(2)))
139 | ->create();
140 |
141 | Dismissal::factory()
142 | ->for($dismissible)
143 | ->for($this->dismisser, 'dismisser')
144 | ->create([
145 | 'dismissed_until' => $now->subDay(),
146 | ]);
147 |
148 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
149 |
150 | $this->assertTrue($actualValue);
151 | }
152 |
153 | #[Test]
154 | public function it_returns_false_on_active_period_start_dismissed_in_future()
155 | {
156 | $now = CarbonImmutable::now();
157 |
158 | /** @var Dismissible $dismissible */
159 | $dismissible = Dismissible::factory()
160 | ->active(CarbonPeriod::create($now, $now->addWeeks(2)))
161 | ->create();
162 |
163 | Dismissal::factory()
164 | ->for($dismissible)
165 | ->for($this->dismisser, 'dismisser')
166 | ->create([
167 | 'dismissed_until' => $now->addDay(),
168 | ]);
169 |
170 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
171 |
172 | $this->assertFalse($actualValue);
173 | }
174 |
175 | #[Test]
176 | public function it_returns_true_during_active_period_not_dismissed()
177 | {
178 | $now = CarbonImmutable::now();
179 |
180 | /** @var Dismissible $dismissible */
181 | $dismissible = Dismissible::factory()
182 | ->active(CarbonPeriod::create($now->subDay(), $now->addDay()))
183 | ->create();
184 |
185 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
186 |
187 | $this->assertTrue($actualValue);
188 | }
189 |
190 | #[Test]
191 | public function it_returns_true_during_active_period_dismissed_in_past()
192 | {
193 | $now = CarbonImmutable::now();
194 |
195 | /** @var Dismissible $dismissible */
196 | $dismissible = Dismissible::factory()
197 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeeks(2)))
198 | ->create();
199 |
200 | Dismissal::factory()
201 | ->for($dismissible)
202 | ->for($this->dismisser, 'dismisser')
203 | ->create([
204 | 'dismissed_until' => $now->subDay(),
205 | ]);
206 |
207 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
208 |
209 | $this->assertTrue($actualValue);
210 | }
211 |
212 | #[Test]
213 | public function it_returns_false_during_active_period_dismissed_in_future()
214 | {
215 | $now = CarbonImmutable::now();
216 |
217 | /** @var Dismissible $dismissible */
218 | $dismissible = Dismissible::factory()
219 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeeks(2)))
220 | ->create();
221 |
222 | Dismissal::factory()
223 | ->for($dismissible)
224 | ->for($this->dismisser, 'dismisser')
225 | ->create([
226 | 'dismissed_until' => $now->addDay(),
227 | ]);
228 |
229 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
230 |
231 | $this->assertFalse($actualValue);
232 | }
233 |
234 | #[Test]
235 | public function it_returns_false_during_active_period_dismissed_forever()
236 | {
237 | $now = CarbonImmutable::now();
238 |
239 | /** @var Dismissible $dismissible */
240 | $dismissible = Dismissible::factory()
241 | ->active(CarbonPeriod::create($now->subWeek(), $now->addWeeks(2)))
242 | ->create();
243 |
244 | Dismissal::factory()
245 | ->for($dismissible)
246 | ->for($this->dismisser, 'dismisser')
247 | ->create([
248 | 'dismissed_until' => null,
249 | ]);
250 |
251 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
252 |
253 | $this->assertFalse($actualValue);
254 | }
255 |
256 | #[Test]
257 | public function it_returns_false_after_active_period_not_dismissed()
258 | {
259 | $now = CarbonImmutable::now();
260 |
261 | /** @var Dismissible $dismissible */
262 | $dismissible = Dismissible::factory()
263 | ->active(CarbonPeriod::create($now->subDay(), $now->subSecond()))
264 | ->create();
265 |
266 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
267 |
268 | $this->assertFalse($actualValue);
269 | }
270 |
271 | #[Test]
272 | public function it_returns_false_after_active_period_dismissed_in_past()
273 | {
274 | $now = CarbonImmutable::now();
275 |
276 | /** @var Dismissible $dismissible */
277 | $dismissible = Dismissible::factory()
278 | ->active(CarbonPeriod::create($now->subWeeks(2), $now->subWeek()))
279 | ->create();
280 |
281 | Dismissal::factory()
282 | ->for($dismissible)
283 | ->for($this->dismisser, 'dismisser')
284 | ->create([
285 | 'dismissed_until' => $now->subDay(),
286 | ]);
287 |
288 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
289 |
290 | $this->assertFalse($actualValue);
291 | }
292 |
293 | #[Test]
294 | public function it_returns_false_after_active_period_dismissed_in_future()
295 | {
296 | $now = CarbonImmutable::now();
297 |
298 | /** @var Dismissible $dismissible */
299 | $dismissible = Dismissible::factory()
300 | ->active(CarbonPeriod::create($now->subWeeks(2), $now->subWeek()))
301 | ->create();
302 |
303 | Dismissal::factory()
304 | ->for($dismissible)
305 | ->for($this->dismisser, 'dismisser')
306 | ->create([
307 | 'dismissed_until' => $now->addDay(),
308 | ]);
309 |
310 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
311 |
312 | $this->assertFalse($actualValue);
313 | }
314 |
315 | #[Test]
316 | public function it_returns_false_after_active_period_dismissed_forever()
317 | {
318 | $now = CarbonImmutable::now();
319 |
320 | /** @var Dismissible $dismissible */
321 | $dismissible = Dismissible::factory()
322 | ->active(CarbonPeriod::create($now->subWeeks(2), $now->subWeek()))
323 | ->create();
324 |
325 | Dismissal::factory()
326 | ->for($dismissible)
327 | ->for($this->dismisser, 'dismisser')
328 | ->create([
329 | 'dismissed_until' => null,
330 | ]);
331 |
332 | $actualValue = Dismissibles::shouldBeVisible($dismissible->name, $this->dismisser);
333 |
334 | $this->assertFalse($actualValue);
335 | }
336 | }
337 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissal/DismissedUntilTest.php:
--------------------------------------------------------------------------------
1 | create([
19 | 'dismissed_until' => Date::now(),
20 | ]);
21 |
22 | $this->assertTrue($dismissal->dismissed_until instanceof DateTimeInterface);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissal/ExtraDataTest.php:
--------------------------------------------------------------------------------
1 | create([
17 | 'extra_data' => null,
18 | ]);
19 |
20 | $this->assertNull($dismissal->extra_data);
21 | }
22 |
23 | #[Test]
24 | public function it_returns_array_when_database_value_is_not_null()
25 | {
26 | $dismissal = Dismissal::factory()->create([
27 | 'extra_data' => ['something'],
28 | ]);
29 |
30 | $this->assertIsArray($dismissal->extra_data);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissal/ScopeDismissedAtTest.php:
--------------------------------------------------------------------------------
1 | assertEmpty(Dismissal::dismissedAt()->get());
18 | }
19 |
20 | #[Test]
21 | public function it_does_not_return_a_dismissal_when_dismissed_until_past_date_time()
22 | {
23 | Dismissal::factory()->create([
24 | 'dismissed_until' => Carbon::yesterday(),
25 | ]);
26 |
27 | $this->assertEmpty(Dismissal::dismissedAt()->get());
28 | }
29 |
30 | #[Test]
31 | public function it_does_not_return_a_dismissal_when_dismissed_until_equals_now()
32 | {
33 | $now = Carbon::now();
34 |
35 | Carbon::setTestNow($now);
36 |
37 | Dismissal::factory()->create([
38 | 'dismissed_until' => $now,
39 | ]);
40 |
41 | $this->assertEmpty(Dismissal::dismissedAt()->get());
42 | }
43 |
44 | #[Test]
45 | public function it_returns_a_dismissal_when_dismissed_until_future_date_time()
46 | {
47 | Dismissal::factory()->create([
48 | 'dismissed_until' => Carbon::tomorrow(),
49 | ]);
50 |
51 | $this->assertNotEmpty(Dismissal::dismissedAt()->get());
52 | }
53 |
54 | #[Test]
55 | public function it_returns_a_dismissal_when_dismissed_until_forever()
56 | {
57 | Dismissal::factory()->create([
58 | 'dismissed_until' => null,
59 | ]);
60 |
61 | $this->assertNotEmpty(Dismissal::dismissedAt()->get());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissal/ScopeDismissedByTest.php:
--------------------------------------------------------------------------------
1 | dismisser = TestDismisserTypeOne::factory()->create();
23 | }
24 |
25 | #[Test]
26 | public function it_does_not_return_a_dismissal_when_not_dismissed()
27 | {
28 | $this->assertEmpty(Dismissal::dismissedBy($this->dismisser)->get());
29 | }
30 |
31 | #[Test]
32 | public function it_does_not_return_a_dismissal_when_dismissed_by_same_type_other_id()
33 | {
34 | Dismissal::factory()
35 | ->for(TestDismisserTypeOne::factory()->create(), 'dismisser')
36 | ->create();
37 |
38 | $this->assertEmpty(Dismissal::dismissedBy($this->dismisser)->get());
39 | }
40 |
41 | #[Test]
42 | public function it_does_not_return_a_dismissal_when_dismissed_by_same_id_other_type()
43 | {
44 | TestDismisserTypeOne::truncate();
45 | TestDismisserTypeTwo::truncate();
46 |
47 | $dismisserTypeOne = TestDismisserTypeOne::factory()->create();
48 | $dismisserTypeTwo = TestDismisserTypeTwo::factory()->create();
49 |
50 | $this->assertEquals($dismisserTypeOne->id, $dismisserTypeTwo->id);
51 |
52 | Dismissal::factory()
53 | ->for($dismisserTypeTwo, 'dismisser')
54 | ->create();
55 |
56 | $this->assertEmpty(Dismissal::dismissedBy($this->dismisser)->get());
57 | }
58 |
59 | #[Test]
60 | public function it_returns_a_dismissal_when_dismissed_by_same_type_and_id()
61 | {
62 | Dismissal::factory()
63 | ->for($this->dismisser, 'dismisser')
64 | ->create();
65 |
66 | $this->assertNotEmpty(Dismissal::dismissedBy($this->dismisser)->get());
67 | }
68 |
69 | #[Test]
70 | public function it_returns_the_correct_dismissal_when_dismissed_by_same_type_and_id()
71 | {
72 | $dismissal = Dismissal::factory()
73 | ->for($this->dismisser, 'dismisser')
74 | ->create();
75 |
76 | $actualDismissals = Dismissal::dismissedBy($this->dismisser)->get();
77 |
78 | $this->assertCount(1, $actualDismissals);
79 | $this->assertTrue($dismissal->is($actualDismissals->first()));
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissible/ActiveFromTest.php:
--------------------------------------------------------------------------------
1 | create();
18 |
19 | $this->assertTrue($dismissible->active_from instanceof CarbonImmutable);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissible/ActiveUntilTest.php:
--------------------------------------------------------------------------------
1 | create([
20 | 'active_until' => Carbon::now()->addDay(),
21 | ]);
22 |
23 | $this->assertTrue($dismissible->active_until instanceof CarbonImmutable);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissible/IsDismissedByTest.php:
--------------------------------------------------------------------------------
1 | dismisser = TestDismisserTypeOne::factory()->create();
25 | }
26 |
27 | #[Test]
28 | public function it_returns_false_when_dismisser_has_not_dismissed()
29 | {
30 | /** @var Dismissible $dismissible */
31 | $dismissible = Dismissible::factory()->create();
32 |
33 | $actualValue = $dismissible->isDismissedBy($this->dismisser);
34 |
35 | $this->assertFalse($actualValue);
36 | }
37 |
38 | #[Test]
39 | public function it_returns_false_when_dismisser_has_dismissed_different_dismissible_until_past_date()
40 | {
41 | Dismissal::factory()
42 | ->for($this->dismisser, 'dismisser')
43 | ->create([
44 | 'dismissed_until' => Carbon::yesterday(),
45 | ]);
46 |
47 | /** @var Dismissible $dismissible */
48 | $dismissible = Dismissible::factory()->create();
49 |
50 | $actualValue = $dismissible->isDismissedBy($this->dismisser);
51 |
52 | $this->assertFalse($actualValue);
53 | }
54 |
55 | #[Test]
56 | public function it_returns_false_when_dismisser_has_dismissed_different_dismissible_until_future_date()
57 | {
58 | Dismissal::factory()
59 | ->for($this->dismisser, 'dismisser')
60 | ->create([
61 | 'dismissed_until' => Carbon::tomorrow(),
62 | ]);
63 |
64 | /** @var Dismissible $dismissible */
65 | $dismissible = Dismissible::factory()->create();
66 |
67 | $actualValue = $dismissible->isDismissedBy($this->dismisser);
68 |
69 | $this->assertFalse($actualValue);
70 | }
71 |
72 | #[Test]
73 | public function it_returns_false_when_dismisser_has_dismissed_until_past_date()
74 | {
75 | /** @var Dismissible $dismissible */
76 | $dismissible = Dismissible::factory()->create();
77 |
78 | Dismissal::factory()
79 | ->for($dismissible)
80 | ->for($this->dismisser, 'dismisser')
81 | ->create([
82 | 'dismissed_until' => Carbon::yesterday(),
83 | ]);
84 |
85 | $actualValue = $dismissible->isDismissedBy($this->dismisser);
86 |
87 | $this->assertFalse($actualValue);
88 | }
89 |
90 | #[Test]
91 | public function it_returns_false_when_dismisser_has_dismissed_until_future_date()
92 | {
93 | /** @var Dismissible $dismissible */
94 | $dismissible = Dismissible::factory()->create();
95 |
96 | Dismissal::factory()
97 | ->for($dismissible)
98 | ->for($this->dismisser, 'dismisser')
99 | ->create([
100 | 'dismissed_until' => Carbon::tomorrow(),
101 | ]);
102 |
103 | $actualValue = $dismissible->isDismissedBy($this->dismisser);
104 |
105 | $this->assertTrue($actualValue);
106 | }
107 |
108 | #[Test]
109 | public function it_returns_the_correct_value_when_dismissal_with_same_id_but_different_type_exists()
110 | {
111 | TestDismisserTypeOne::truncate();
112 | TestDismisserTypeTwo::truncate();
113 |
114 | $dismisserOfTypeOne = TestDismisserTypeOne::factory()->create();
115 | $dismisserOfTypeTwo = TestDismisserTypeTwo::factory()->create();
116 |
117 | $this->assertEquals($dismisserOfTypeOne->id, $dismisserOfTypeTwo->id);
118 |
119 | /** @var Dismissible $dismissible */
120 | $dismissible = Dismissible::factory()->active()->create();
121 |
122 | Dismissal::factory()
123 | ->for($dismissible)
124 | ->for($dismisserOfTypeOne, 'dismisser')
125 | ->create([
126 | 'dismissed_until' => Carbon::tomorrow(),
127 | ]);
128 |
129 | $this->assertTrue($dismissible->isDismissedBy($dismisserOfTypeOne));
130 | $this->assertFalse($dismissible->isDismissedBy($dismisserOfTypeTwo));
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissible/ScopeActiveTest.php:
--------------------------------------------------------------------------------
1 | active(CarbonPeriod::create($now->addDay(), $now->addWeek()))
22 | ->create();
23 |
24 | $actualValue = Dismissible::active()->get();
25 |
26 | $this->assertEmpty($actualValue);
27 | }
28 |
29 | #[Test]
30 | public function it_returns_the_dismissible_on_active_period_start()
31 | {
32 | $now = CarbonImmutable::now();
33 |
34 | Dismissible::factory()
35 | ->active(CarbonPeriod::create($now, $now->addMinute()))
36 | ->create();
37 |
38 | $actualValue = Dismissible::active()->get();
39 |
40 | $this->assertNotEmpty($actualValue);
41 | }
42 |
43 | #[Test]
44 | public function it_returns_the_dismissible_during_active_period_when_active_until_is_set()
45 | {
46 | $now = CarbonImmutable::now();
47 |
48 | Dismissible::factory()
49 | ->active(CarbonPeriod::create($now->subMinute(), $now->addMinute()))
50 | ->create();
51 |
52 | $actualValue = Dismissible::active()->get();
53 |
54 | $this->assertNotEmpty($actualValue);
55 | }
56 |
57 | #[Test]
58 | public function it_returns_the_dismissible_during_active_period_when_active_until_is_null()
59 | {
60 | $now = CarbonImmutable::now();
61 |
62 | Dismissible::factory()
63 | ->active(CarbonPeriod::create($now->subMinute()))
64 | ->create();
65 |
66 | $actualValue = Dismissible::active()->get();
67 |
68 | $this->assertNotEmpty($actualValue);
69 | }
70 |
71 | #[Test]
72 | public function it_does_not_return_the_dismissible_after_active_period()
73 | {
74 | $now = CarbonImmutable::now();
75 |
76 | Dismissible::factory()
77 | ->active(CarbonPeriod::create($now->subWeek(), $now->subDay()))
78 | ->create();
79 |
80 | $actualValue = Dismissible::active()->get();
81 |
82 | $this->assertEmpty($actualValue);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/tests/Unit/Models/Dismissible/ScopeNotDismissedByTest.php:
--------------------------------------------------------------------------------
1 | dismisser = TestDismisserTypeOne::factory()->create();
26 | }
27 |
28 | #[Test]
29 | public function it_returns_dismissibles_which_are_active_in_the_past(): void
30 | {
31 | Dismissible::factory()->active()->create();
32 |
33 | $actualValue = Dismissible::notDismissedBy($this->dismisser)->get();
34 |
35 | $this->assertNotEmpty($actualValue);
36 | }
37 |
38 | #[Test]
39 | public function it_returns_dismissibles_which_are_active_in_the_future(): void
40 | {
41 | $now = CarbonImmutable::now();
42 |
43 | Dismissible::factory()
44 | ->active(CarbonPeriod::create($now->addDay(), $now->addWeek()))
45 | ->create();
46 |
47 | $actualValue = Dismissible::notDismissedBy($this->dismisser)->get();
48 |
49 | $this->assertNotEmpty($actualValue);
50 | }
51 |
52 | #[Test]
53 | public function it_returns_dismissibles_when_dismissed_in_the_past(): void
54 | {
55 | $dismissible = Dismissible::factory()->create();
56 |
57 | Dismissal::factory()
58 | ->for($this->dismisser, 'dismisser')
59 | ->for($dismissible)
60 | ->create([
61 | 'dismissed_until' => Carbon::yesterday(),
62 | ]);
63 |
64 | $actualValue = Dismissible::notDismissedBy($this->dismisser)->get();
65 |
66 | $this->assertNotEmpty($actualValue);
67 | }
68 |
69 | #[Test]
70 | public function it_returns_dismissibles_when_dismissed_in_the_future_by_someone_else(): void
71 | {
72 | $dismissible = Dismissible::factory()->create();
73 |
74 | Dismissal::factory()
75 | ->for($dismissible)
76 | ->create([
77 | 'dismissed_until' => Carbon::yesterday(),
78 | ]);
79 |
80 | $actualValue = Dismissible::notDismissedBy($this->dismisser)->get();
81 |
82 | $this->assertNotEmpty($actualValue);
83 | }
84 |
85 | #[Test]
86 | public function it_does_not_return_dismissibles_when_dismissed_until_future_date_time(): void
87 | {
88 | $dismissible = Dismissible::factory()->create();
89 |
90 | Dismissal::factory()
91 | ->for($this->dismisser, 'dismisser')
92 | ->for($dismissible)
93 | ->create([
94 | 'dismissed_until' => Carbon::tomorrow(),
95 | ]);
96 |
97 | $actualValue = Dismissible::notDismissedBy($this->dismisser)->get();
98 |
99 | $this->assertEmpty($actualValue);
100 | }
101 |
102 | #[Test]
103 | public function it_does_not_return_dismissibles_when_dismissed_until_forever(): void
104 | {
105 | $dismissible = Dismissible::factory()->create();
106 |
107 | Dismissal::factory()
108 | ->for($this->dismisser, 'dismisser')
109 | ->for($dismissible)
110 | ->create([
111 | 'dismissed_until' => null,
112 | ]);
113 |
114 | $actualValue = Dismissible::notDismissedBy($this->dismisser)->get();
115 |
116 | $this->assertEmpty($actualValue);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/tests/Unit/Traits/HasDismissibles/DismissalsTest.php:
--------------------------------------------------------------------------------
1 | create();
19 |
20 | $dismissible = Dismissible::factory()->create();
21 |
22 | Dismissal::factory(5)
23 | ->for($dismisser, 'dismisser')
24 | ->for($dismissible)
25 | ->create();
26 |
27 | $actualValue = $dismisser->dismissals;
28 |
29 | $this->assertCount(5, $actualValue);
30 |
31 | /** @var Dismissal $dismissal */
32 | foreach ($actualValue as $dismissal) {
33 | $this->assertTrue($dismissal->dismisser->is($dismisser));
34 | $this->assertTrue($dismissal->dismissible->is($dismissible));
35 | }
36 | }
37 |
38 | #[Test]
39 | public function it_returns_all_dismissals_by_a_dismisser_of_different_dismissibles()
40 | {
41 | /** @var TestDismisserTypeOne $dismisser */
42 | $dismisser = TestDismisserTypeOne::factory()->create();
43 |
44 | Dismissal::factory(5)
45 | ->for($dismisser, 'dismisser')
46 | ->create();
47 |
48 | $actualValue = $dismisser->dismissals;
49 |
50 | $this->assertCount(5, $actualValue);
51 |
52 | /** @var Dismissal $dismissal */
53 | foreach ($actualValue as $dismissal) {
54 | $this->assertTrue($dismissal->dismisser->is($dismisser));
55 | }
56 | }
57 |
58 | #[Test]
59 | public function it_only_returns_dismissals_of_the_dismisser()
60 | {
61 | /** @var TestDismisserTypeOne $dismisser */
62 | $dismisser = TestDismisserTypeOne::factory()->create();
63 |
64 | /** @var Dismissible $dismissible */
65 | $dismissible = Dismissible::factory()->create();
66 |
67 | $expectedDismissal = Dismissal::factory()
68 | ->for($dismisser, 'dismisser')
69 | ->for($dismissible)
70 | ->create();
71 |
72 | Dismissal::factory()
73 | ->for($dismissible)
74 | ->create();
75 |
76 | Dismissal::factory()->create();
77 |
78 | $actualValue = $dismisser->dismissals;
79 |
80 | $this->assertCount(1, $actualValue);
81 |
82 | /** @var Dismissal $actualDismissal */
83 | $actualDismissal = $actualValue->first();
84 |
85 | $this->assertTrue($actualDismissal->is($expectedDismissal));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/database/migrations/2024_04_21_153207_create_test_dismissers_tables.php:
--------------------------------------------------------------------------------
1 | tables as $table) {
15 | Schema::create($table, function (Blueprint $table) {
16 | $table->id();
17 | $table->timestamps();
18 | });
19 | }
20 | }
21 |
22 | public function down(): void
23 | {
24 | foreach ($this->tables as $table) {
25 | Schema::dropIfExists($table);
26 | }
27 | }
28 | };
29 |
--------------------------------------------------------------------------------