├── LICENSE.md ├── README.md ├── composer.json └── src ├── Expirable.php ├── ExpirableServiceProvider.php └── ExpiringScope.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) Mark van den Broek 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 Model Expires 2 | 3 | --- 4 | 5 | ![PHP version][ico-php-version] 6 | [![Latest Version on Packagist][ico-version]][link-packagist] 7 | [![Software License][ico-license]](LICENSE.md) 8 | [![Tests][ico-tests]][link-tests] 9 | [![Code style][ico-code-style]][link-code-style] 10 | [![Total Downloads][ico-downloads]][link-downloads] 11 | 12 | # Assign expiration dates to Eloquent models 13 | 14 | ## Installation 15 | 16 | You can install the package via composer: 17 | 18 | ```bash 19 | composer require mvdnbrk/laravel-model-expires 20 | ``` 21 | ## Usage 22 | 23 | To use an expiration date on a model, use the `Expirable` trait: 24 | 25 | ```php 26 | expires(); 49 | }); 50 | } 51 | 52 | public function down(): void 53 | { 54 | Schema::dropExpires(); 55 | } 56 | } 57 | 58 | ``` 59 | 60 | The `Expirable` trait will automatically cast the `expires_at` attribute to a `DateTime` / `Carbon` instance for you. 61 | 62 | ### Customizing the column name 63 | 64 | You may customize the column name by setting the `EXIRES_AT` constant or by overriding the `getExpiresAtColumn` method on your model. 65 | 66 | ```php 67 | class Subscription extends Model 68 | { 69 | use Expirable; 70 | 71 | const EXPIRES_AT = 'ends_at'; 72 | } 73 | ``` 74 | 75 | ```php 76 | $table->expires('ends_at'); 77 | $table->dropExpires('ends_at'); 78 | ``` 79 | 80 | ### Setting expiration 81 | 82 | You may set the expiration of a model by setting the `expires_at` attribute with a TTL in seconds: 83 | 84 | ```php 85 | $subscription->expires_at = 600; 86 | ``` 87 | 88 | Instead of passing the number of seconds as an integer, you may also pass a `DateTime` instance representing the expiration date: 89 | 90 | ```php 91 | $subscription->expires_at = now()->addMinutes(10); 92 | ``` 93 | 94 | ### Discarding expiration 95 | 96 | You may discard the expiration of a model by setting a negative or zero TTL or use the `discardExpiration` method: 97 | 98 | ```php 99 | $subscription->expires_at = 0; 100 | $subscription->expires_at = -5; 101 | 102 | $subscription->discardExpiration()->save(); 103 | ``` 104 | 105 | ### Determining expiration 106 | 107 | To determine if a given model instance has expired, use the `expired` method: 108 | 109 | ```php 110 | if ($subscription->expired()) { 111 | // 112 | } 113 | ``` 114 | 115 | To determine if a given model will expire in the future use the `willExpire` method: 116 | 117 | ```php 118 | if ($subscription->willExpire()) { 119 | // 120 | } 121 | ``` 122 | 123 | ### Querying models 124 | 125 | The `withoutExpired` method will retrieve models that are not expired: 126 | 127 | ```php 128 | $subscriptions = App\Models\Subscription::withoutExpired()->get(); 129 | ``` 130 | 131 | The `onlyExpired` method will retrieve **only** the expired models: 132 | 133 | ```php 134 | $subscriptions = App\Models\Subscription::onlyExpired()->get(); 135 | ``` 136 | 137 | The `expiring` method will retrieve **only** models that will expire in the future: 138 | 139 | ```php 140 | $subscriptions = App\Models\Subscription::expiring()->get(); 141 | ``` 142 | 143 | The `notExpiring` method will retrieve **only** models that will not expire: 144 | 145 | ```php 146 | $subscriptions = App\Models\Subscription::notExpiring()->get(); 147 | ``` 148 | 149 | ### Todo 150 | - Add a `expired:prune` console command to delete expired models, or perform custom implementation. 151 | - Add a query scope that will query models that will expire in `...` 152 | 153 | ## Testing 154 | 155 | ```bash 156 | composer test 157 | ``` 158 | ## Changelog 159 | 160 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 161 | 162 | ## Contributing 163 | 164 | Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 165 | 166 | ## Security Vulnerabilities 167 | 168 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 169 | 170 | ## Credits 171 | 172 | - [Mark van den Broek][link-author] 173 | - [All Contributors][link-contributors] 174 | 175 | ## License 176 | 177 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 178 | 179 | [ico-php-version]: https://img.shields.io/packagist/php-v/mvdnbrk/laravel-model-expires?style=flat-square 180 | [ico-version]: https://img.shields.io/packagist/v/mvdnbrk/laravel-model-expires.svg?style=flat-square 181 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 182 | [ico-tests]: https://img.shields.io/github/workflow/status/mvdnbrk/laravel-model-expires/tests/main?label=tests&style=flat-square 183 | [ico-code-style]: https://styleci.io/repos/220024174/shield?branch=main 184 | [ico-downloads]: https://img.shields.io/packagist/dt/mvdnbrk/laravel-model-expires.svg?style=flat-square 185 | 186 | [link-packagist]: https://packagist.org/packages/mvdnbrk/laravel-model-expires 187 | [link-tests]: https://github.com/mvdnbrk/laravel-model-expires/actions?query=workflow%3Atests 188 | [link-code-style]: https://styleci.io/repos/220024174 189 | [link-downloads]: https://packagist.org/packages/mvdnbrk/laravel-model-expires 190 | [link-author]: https://github.com/mvdnbrk 191 | [link-contributors]: ../../contributors 192 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mvdnbrk/laravel-model-expires", 3 | "description": "A package to assign expiration dates to Eloquent models", 4 | "keywords": [ 5 | "laravel", 6 | "eloquent", 7 | "model", 8 | "date", 9 | "expire", 10 | "expires", 11 | "expiration", 12 | "expirable" 13 | ], 14 | "homepage": "https://github.com/mvdnbrk/laravel-model-expires", 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Mark van den Broek", 19 | "email": "mvdnbrk@gmail.com", 20 | "role": "Developer" 21 | } 22 | ], 23 | "require": { 24 | "php": "^7.2 || ^8.0", 25 | "illuminate/support": "^6.0 || ^7.0 || ^8.0", 26 | "illuminate/database": "^6.0 || ^7.0 || ^8.0" 27 | }, 28 | "require-dev": { 29 | "doctrine/dbal": "^2.10", 30 | "mockery/mockery": "^1.3.3", 31 | "orchestra/testbench": "^4.0 || ^5.0 || ^6.0", 32 | "phpunit/phpunit": "^8.0 || ^9.4" 33 | }, 34 | "config": { 35 | "sort-packages": true 36 | }, 37 | "extra": { 38 | "laravel": { 39 | "providers": [ 40 | "Mvdnbrk\\EloquentExpirable\\ExpirableServiceProvider" 41 | ] 42 | } 43 | }, 44 | "autoload": { 45 | "psr-4": { 46 | "Mvdnbrk\\EloquentExpirable\\": "src" 47 | } 48 | }, 49 | "autoload-dev": { 50 | "psr-4": { 51 | "Mvdnbrk\\EloquentExpirable\\Tests\\": "tests" 52 | } 53 | }, 54 | "minimum-stability": "dev", 55 | "prefer-stable": true, 56 | "scripts": { 57 | "test": "vendor/bin/phpunit" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Expirable.php: -------------------------------------------------------------------------------- 1 | dates[] = $this->getExpiresAtColumn(); 25 | } 26 | 27 | /** 28 | * @param \DateTimeInterface|\DateInterval|int|null $ttl 29 | */ 30 | public function setExpiresAtAttribute($ttl): void 31 | { 32 | $seconds = $this->getSeconds($ttl); 33 | 34 | $this->attributes[$this->getExpiresAtColumn()] = $seconds ? Carbon::now()->addSeconds($seconds) : null; 35 | } 36 | 37 | public function discardExpiration(): self 38 | { 39 | $this->setExpiresAtAttribute(0); 40 | 41 | return $this; 42 | } 43 | 44 | public function expired(): bool 45 | { 46 | $expiresAt = $this->{$this->getExpiresAtColumn()}; 47 | 48 | return $expiresAt && $expiresAt->isPast(); 49 | } 50 | 51 | public function willExpire(): bool 52 | { 53 | $expiresAt = $this->{$this->getExpiresAtColumn()}; 54 | 55 | return $expiresAt && $expiresAt->isFuture(); 56 | } 57 | 58 | public function getExpiresAtColumn(): string 59 | { 60 | return defined('static::EXPIRES_AT') ? static::EXPIRES_AT : 'expires_at'; 61 | } 62 | 63 | public function getQualifiedExpiresAtColumn(): string 64 | { 65 | return $this->qualifyColumn($this->getExpiresAtColumn()); 66 | } 67 | 68 | /** 69 | * @param \DateTimeInterface|\DateInterval|int|null $ttl 70 | */ 71 | protected function getSeconds($ttl): int 72 | { 73 | $duration = $ttl ? $this->parseDateInterval($ttl) : 0; 74 | 75 | if ($duration instanceof DateTimeInterface) { 76 | $duration = Carbon::now()->diffInRealSeconds($duration, false); 77 | } 78 | 79 | return (int) $duration > 0 ? $duration : 0; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/ExpirableServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerBlueprintMacros(); 13 | } 14 | 15 | protected function registerBlueprintMacros(): void 16 | { 17 | if ($this->app->runningInConsole()) { 18 | Blueprint::macro('expires', function (string $column = 'expires_at', int $precision = 0) { 19 | /* @var \Illuminate\Database\Schema\Blueprint $this */ 20 | return $this->timestamp($column, $precision)->nullable(); 21 | }); 22 | 23 | Blueprint::macro('dropExpires', function (string $column = 'expires_at') { 24 | /* @var \Illuminate\Database\Schema\Blueprint $this */ 25 | return $this->dropColumn($column); 26 | }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ExpiringScope.php: -------------------------------------------------------------------------------- 1 | extensions as $extension) { 27 | $this->{"add{$extension}"}($builder); 28 | } 29 | } 30 | 31 | protected function addExpiring(Builder $builder): void 32 | { 33 | $builder->macro('expiring', function (Builder $builder) { 34 | $model = $builder->getModel(); 35 | 36 | return $builder->whereNotNull($model->getQualifiedExpiresAtColumn()) 37 | ->where($model->getQualifiedExpiresAtColumn(), '>', $model->freshTimestamp()); 38 | }); 39 | } 40 | 41 | protected function addNotExpiring(Builder $builder): void 42 | { 43 | $builder->macro('notExpiring', function (Builder $builder) { 44 | return $builder->whereNull($builder->getModel()->getQualifiedExpiresAtColumn()); 45 | }); 46 | } 47 | 48 | protected function addOnlyExpired(Builder $builder): void 49 | { 50 | $builder->macro('onlyExpired', function (Builder $builder) { 51 | $model = $builder->getModel(); 52 | 53 | return $builder->where($model->getQualifiedExpiresAtColumn(), '<=', $model->freshTimestamp()); 54 | }); 55 | } 56 | 57 | protected function addWithoutExpired(Builder $builder): void 58 | { 59 | $builder->macro('withoutExpired', function (Builder $builder) { 60 | $model = $builder->getModel(); 61 | 62 | return $builder->where($model->getQualifiedExpiresAtColumn(), '>', $model->freshTimestamp()) 63 | ->orWhereNull($model->getQualifiedExpiresAtColumn()); 64 | }); 65 | } 66 | } 67 | --------------------------------------------------------------------------------