From 2478b21f67c25f51d2caf2b61363d748e75e9344 Mon Sep 17 00:00:00 2001 From: Mateus Junges Date: Sun, 28 Jan 2024 15:59:36 -0300 Subject: [PATCH 1/4] Allow to customize how invite codes are generated --- README.md | 18 ++++++++++++++++++ src/Facades/InviteCodes.php | 4 +++- src/Factory.php | 36 +++++++++++++++++++++++++++++++----- tests/FactoryTest.php | 28 ++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 892a75c..b1bdf95 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ If you think this package helped you in any way, you can sponsor me on GitHub! - [Create multiple invite codes](#create-multiple-invite-codes) - [Redeeming invite codes](#redeeming-invite-codes) - [Redeeming invite codes without dispatching events](#redeeming-invite-codes-without-dispatching-events) +- [Customizing how invite codes are generated](#customizing-how-invite-codes-are-generated) +- [Extending the `Invite` model](#extending-the-invite-model) - [Handling invite codes exceptions](#handling-invite-codes-exceptions) - [Using artisan commands](#using-artisan-commands) - [Tests](#tests) @@ -228,6 +230,22 @@ you can use the `withoutEvents()` method: \Junges\InviteCodes\Facades\InviteCodes::withoutEvents()->redeem('YOUR-INVITE-CODE'); ``` +# Customizing how invite codes are generated +By default, this package generates a random 16 characters string that. Sometimes, you may want to customize how your invitation code is generated, +for adding a prefix to the invitation code or anything you need. + +If you need to customize how your invite codes are generated, you can add a call to the InviteCodes facade `createInviteCodesusing` method, in your service provider: + +```php +\Junges\InviteCodes\Facades\InviteCodes::createInviteCodeUsing(static function () { + return 'THIS-IS-MY-INVITE-'.\Illuminate\Support\Str::random(); +}); +``` + +From now on, all of your invites will have the `THIS-IS-MY-INVITE-` prefix. + +Also, the package itself will handle duplicate invites, so you don't need to take care of that yourself. + # Extending the `Invite` model The `\Junges\InviteCodes\Models\Invite` is fully extendable and replaceable. You can extend or create a new model to be used instead of the default one, and the only thing you need to do is implement the `\Junges\InviteCodes\Contracts\InviteContract` interface, which contains some required methods for this package to work. diff --git a/src/Facades/InviteCodes.php b/src/Facades/InviteCodes.php index fe06877..eb5a8d7 100644 --- a/src/Facades/InviteCodes.php +++ b/src/Facades/InviteCodes.php @@ -12,7 +12,7 @@ * * @method static $this withoutEvents() Will dispatch no events. * @method static $this redeem(string $code) Redeem an invite code. - * @method static $this create() Create an invite code. + * @method static InviteCodesFactory create() Create an invite code. * @method static $this maxUsages(int $usages = null) Set the max allowed usages for invite codes. * @method static $this restrictUsageTo(string $email) Set the user who can use the invite code. * @method static $this expiresAt($date) Set the invite code expiration date. @@ -21,6 +21,8 @@ * @method static Collection make(int $quantity) Save $quantity invite codes. * @method static void macro($name, $macro) * @method static bool hasMacro($name) + * @method static void quietly(callable $callback) Run the given callback without dispatching events + * @method static void createInviteCodeUsing(?callable $callable) * @method static $this canBeUsedOnce() */ class InviteCodes extends Facade diff --git a/src/Factory.php b/src/Factory.php index 1968213..bbd58c3 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -31,16 +31,33 @@ class Factory implements InviteCodesFactory protected int $max_usages; protected ?string $to = null; protected ?CarbonInterface $expires_at; - protected bool $dispatch_events = true; + protected static bool $dispatch_events = true; + protected static ?\Closure $createInviteCodeUsing = null; + + public static function createInviteCodeUsing(callable $callable): void + { + self::$createInviteCodeUsing = $callable(...); + } /** If used, no events will be dispatched. */ public function withoutEvents(): self { - $this->dispatch_events = false; + self::$dispatch_events = false; return $this; } + public function quietly(callable $callback): void + { + self::$dispatch_events = false; + + $closure = $callback(...); + + $closure(); + + self::$dispatch_events = true; + } + /** * @throws ExpiredInviteCodeException * @throws InvalidInviteCodeException @@ -54,7 +71,7 @@ public function redeem(string $code): Invite $model = app(config('invite-codes.models.invite_model', Invite::class)); /** @var Invite|null $invite */ - $invite = $model->where('code', Str::upper($code))->first(); + $invite = $model->where('code', $code)->first(); if (! $invite instanceof InviteContract || ! $this->inviteCanBeRedeemed($invite)) { throw new InvalidInviteCodeException('Your invite code is invalid'); @@ -139,7 +156,7 @@ public function save(): Invite $model = app(config('invite-codes.models.invite_model', Invite::class)); do { - $code = Str::upper(Str::random(16)); + $code = $this->createInvitationCode(); } while ($model->where('code', $code)->first() instanceof $model); return $model->create([ @@ -207,6 +224,15 @@ private function inviteCanBeRedeemed(Invite $invite): bool private function shouldDispatchEvents(): bool { - return $this->dispatch_events; + return self::$dispatch_events; + } + + private function createInvitationCode(): string + { + if (self::$createInviteCodeUsing instanceof \Closure) { + return call_user_func(self::$createInviteCodeUsing); + } + + return Str::upper(Str::random(16)); } } diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php index 22722f0..9b8ae77 100644 --- a/tests/FactoryTest.php +++ b/tests/FactoryTest.php @@ -3,9 +3,11 @@ namespace Junges\InviteCodes\Tests; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Str; use Junges\InviteCodes\Contracts\InviteContract; use Junges\InviteCodes\Events\InviteRedeemedEvent; use Junges\InviteCodes\Facades\InviteCodes; +use Junges\InviteCodes\Models\Invite; class FactoryTest extends TestCase { @@ -42,4 +44,30 @@ public function test_macro(): void $this->assertInstanceOf(InviteContract::class, $invite); $this->assertTrue($invite->usageRestrictedToEmail('test@example.com')); } + + public function test_it_can_run_quietly(): void + { + Event::fake(); + + Invite::query()->create([ + 'code' => $code = Str::random(), + ]); + + InviteCodes::quietly(static function () use ($code) { + InviteCodes::redeem($code); + }); + + Event::assertNotDispatched(InviteRedeemedEvent::class); + } + + public function test_it_can_customize_how_invite_code_is_created(): void + { + InviteCodes::createInviteCodeUsing(static function () { + return 'PREFIX-12345'; + }); + + $invite = InviteCodes::create()->save(); + + $this->assertSame('PREFIX-12345', $invite->code); + } } From 92a7a4748be885b276c48113364f7ed5418424a1 Mon Sep 17 00:00:00 2001 From: Mateus Junges Date: Sun, 28 Jan 2024 16:03:27 -0300 Subject: [PATCH 2/4] Remove unrelated feature --- src/Facades/InviteCodes.php | 3 +-- src/Factory.php | 21 +++++---------------- tests/FactoryTest.php | 17 ++--------------- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/Facades/InviteCodes.php b/src/Facades/InviteCodes.php index eb5a8d7..465e232 100644 --- a/src/Facades/InviteCodes.php +++ b/src/Facades/InviteCodes.php @@ -21,8 +21,7 @@ * @method static Collection make(int $quantity) Save $quantity invite codes. * @method static void macro($name, $macro) * @method static bool hasMacro($name) - * @method static void quietly(callable $callback) Run the given callback without dispatching events - * @method static void createInviteCodeUsing(?callable $callable) + * @method static void createInviteCodeUsing(?callable $callable = null) * @method static $this canBeUsedOnce() */ class InviteCodes extends Facade diff --git a/src/Factory.php b/src/Factory.php index bbd58c3..ee4d6db 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -31,33 +31,22 @@ class Factory implements InviteCodesFactory protected int $max_usages; protected ?string $to = null; protected ?CarbonInterface $expires_at; - protected static bool $dispatch_events = true; + protected bool $dispatch_events = true; protected static ?\Closure $createInviteCodeUsing = null; - public static function createInviteCodeUsing(callable $callable): void + public static function createInviteCodeUsing(callable $callable = null): void { - self::$createInviteCodeUsing = $callable(...); + self::$createInviteCodeUsing = $callable !== null ? $callable(...) : null; } /** If used, no events will be dispatched. */ public function withoutEvents(): self { - self::$dispatch_events = false; + $this->dispatch_events = false; return $this; } - public function quietly(callable $callback): void - { - self::$dispatch_events = false; - - $closure = $callback(...); - - $closure(); - - self::$dispatch_events = true; - } - /** * @throws ExpiredInviteCodeException * @throws InvalidInviteCodeException @@ -224,7 +213,7 @@ private function inviteCanBeRedeemed(Invite $invite): bool private function shouldDispatchEvents(): bool { - return self::$dispatch_events; + return $this->dispatch_events; } private function createInvitationCode(): string diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php index 9b8ae77..d7e7b35 100644 --- a/tests/FactoryTest.php +++ b/tests/FactoryTest.php @@ -45,21 +45,6 @@ public function test_macro(): void $this->assertTrue($invite->usageRestrictedToEmail('test@example.com')); } - public function test_it_can_run_quietly(): void - { - Event::fake(); - - Invite::query()->create([ - 'code' => $code = Str::random(), - ]); - - InviteCodes::quietly(static function () use ($code) { - InviteCodes::redeem($code); - }); - - Event::assertNotDispatched(InviteRedeemedEvent::class); - } - public function test_it_can_customize_how_invite_code_is_created(): void { InviteCodes::createInviteCodeUsing(static function () { @@ -69,5 +54,7 @@ public function test_it_can_customize_how_invite_code_is_created(): void $invite = InviteCodes::create()->save(); $this->assertSame('PREFIX-12345', $invite->code); + + InviteCodes::createInviteCodeUsing(null); } } From 9a7c080d9e8086d3fd14603d26f2f34bd0fc16f9 Mon Sep 17 00:00:00 2001 From: Mateus Junges Date: Sun, 28 Jan 2024 16:11:07 -0300 Subject: [PATCH 3/4] Fix docblocks --- src/Facades/InviteCodes.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Facades/InviteCodes.php b/src/Facades/InviteCodes.php index 465e232..a3e5954 100644 --- a/src/Facades/InviteCodes.php +++ b/src/Facades/InviteCodes.php @@ -5,24 +5,25 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Facade; use Junges\InviteCodes\Contracts\InviteCodesFactory; +use Junges\InviteCodes\Contracts\InviteContract; use Junges\InviteCodes\Models\Invite; /** * Class Factory. * - * @method static $this withoutEvents() Will dispatch no events. - * @method static $this redeem(string $code) Redeem an invite code. + * @method static InviteCodesFactory withoutEvents() Will dispatch no events. + * @method static InviteContract redeem(string $code) Redeem an invite code. * @method static InviteCodesFactory create() Create an invite code. - * @method static $this maxUsages(int $usages = null) Set the max allowed usages for invite codes. - * @method static $this restrictUsageTo(string $email) Set the user who can use the invite code. - * @method static $this expiresAt($date) Set the invite code expiration date. - * @method static $this expiresIn(int $days) Set the invite code expiration date to $days from now. + * @method static InviteCodesFactory maxUsages(int $usages = null) Set the max allowed usages for invite codes. + * @method static InviteCodesFactory restrictUsageTo(string $email) Set the user who can use the invite code. + * @method static InviteCodesFactory expiresAt($date) Set the invite code expiration date. + * @method static InviteCodesFactory expiresIn(int $days) Set the invite code expiration date to $days from now. * @method static Invite save() Save the invite code. - * @method static Collection make(int $quantity) Save $quantity invite codes. + * @method static Collection make(int $quantity) Save $quantity invite codes. * @method static void macro($name, $macro) * @method static bool hasMacro($name) * @method static void createInviteCodeUsing(?callable $callable = null) - * @method static $this canBeUsedOnce() + * @method static InviteCodesFactory canBeUsedOnce() */ class InviteCodes extends Facade { From 45275bd6d728440d1221eeb62d5513d7bced12bc Mon Sep 17 00:00:00 2001 From: Mateus Junges Date: Sun, 28 Jan 2024 16:11:37 -0300 Subject: [PATCH 4/4] wip --- src/Facades/InviteCodes.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Facades/InviteCodes.php b/src/Facades/InviteCodes.php index a3e5954..9e3614c 100644 --- a/src/Facades/InviteCodes.php +++ b/src/Facades/InviteCodes.php @@ -9,8 +9,6 @@ use Junges\InviteCodes\Models\Invite; /** - * Class Factory. - * * @method static InviteCodesFactory withoutEvents() Will dispatch no events. * @method static InviteContract redeem(string $code) Redeem an invite code. * @method static InviteCodesFactory create() Create an invite code.