From 4ff82c451697e7a9db0fbeff84c65400ed4d283d Mon Sep 17 00:00:00 2001 From: Guilherme Medeiros Date: Thu, 12 Nov 2026 11:12:33 -0300 Subject: [PATCH] Add configurable table and model Adds `table` and `model` config options so recordings can be stored in a custom table and recorded through a custom model that extends the default Barstool model. Defaults match the current behaviour. Co-Authored-By: Claude Opus 5.5 --- README.md | 23 ++++++ config/barstool.php | 13 ++++ .../add_context_to_barstools_table.php.stub | 5 +- ...eated_at_index_to_barstools_table.php.stub | 6 +- .../create_barstools_table.php.stub | 2 +- src/Barstool.php | 14 +++- src/Jobs/RecordBarstoolJob.php | 5 +- src/Models/Barstool.php | 1 + tests/BarstoolTest.php | 72 +++++++++++++++++++ tests/Fixtures/Models/CustomBarstool.php | 12 ++++ 10 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 tests/Fixtures/Models/CustomBarstool.php diff --git a/README.md b/README.md index fd2d8e1..723f329 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,29 @@ BARSTOOL_DB_CONNECTION=barstool The migration and the `Barstool` model both respect this connection. +### Custom table & model + +Recordings are stored in a `barstools` table by default. To use a different name, set `table` before running the migrations: + +```php +'table' => 'saloon_logs', +``` + +You can also have Barstool record through your own model — handy for adding relationships, scopes, or casts. It must extend Barstool's model, and it uses the configured `table` automatically: + +```php +use Saloon\Barstool\Models\Barstool; + +class SaloonLog extends Barstool +{ + // +} +``` + +```php +'model' => App\Models\SaloonLog::class, +``` + ### Pruning old recordings Recordings are kept for 30 days by default, controlled by: diff --git a/config/barstool.php b/config/barstool.php index 328b0c3..0c2c197 100644 --- a/config/barstool.php +++ b/config/barstool.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use Saloon\Barstool\Models\Barstool; + return [ /* @@ -14,6 +16,17 @@ */ 'connection' => env('BARSTOOL_DB_CONNECTION', env('DB_CONNECTION', 'mysql')), + /* + * The database table where recordings will be stored. + */ + 'table' => 'barstools', + + /* + * The model used to store recordings. + * If you use your own model, it must extend Saloon\Barstool\Models\Barstool. + */ + 'model' => Barstool::class, + /* * The number of days to keep recordings for. */ diff --git a/database/migrations/add_context_to_barstools_table.php.stub b/database/migrations/add_context_to_barstools_table.php.stub index 9dbcb7c..12c2d04 100644 --- a/database/migrations/add_context_to_barstools_table.php.stub +++ b/database/migrations/add_context_to_barstools_table.php.stub @@ -21,12 +21,13 @@ return new class extends Migration public function up() { $schema = Schema::connection($this->getConnection()); + $tableName = config('barstool.table', 'barstools'); - if ($schema->hasColumn('barstools', 'context')) { + if ($schema->hasColumn($tableName, 'context')) { return; } - $schema->table('barstools', function (Blueprint $table) { + $schema->table($tableName, function (Blueprint $table) { $table->json('context')->nullable(); }); } diff --git a/database/migrations/add_created_at_index_to_barstools_table.php.stub b/database/migrations/add_created_at_index_to_barstools_table.php.stub index 17ac016..fa29fbc 100644 --- a/database/migrations/add_created_at_index_to_barstools_table.php.stub +++ b/database/migrations/add_created_at_index_to_barstools_table.php.stub @@ -21,12 +21,14 @@ return new class extends Migration public function up() { $schema = Schema::connection($this->getConnection()); + $tableName = config('barstool.table', 'barstools'); - if ($schema->hasIndex('barstools', 'barstools_created_at_index')) { + // Match the index by column so the check holds for any table name + if ($schema->hasIndex($tableName, ['created_at'])) { return; } - $schema->table('barstools', function (Blueprint $table) { + $schema->table($tableName, function (Blueprint $table) { $table->index('created_at'); }); } diff --git a/database/migrations/create_barstools_table.php.stub b/database/migrations/create_barstools_table.php.stub index 99b83d5..273fcf1 100644 --- a/database/migrations/create_barstools_table.php.stub +++ b/database/migrations/create_barstools_table.php.stub @@ -22,7 +22,7 @@ return new class extends Migration { $schema = Schema::connection($this->getConnection()); - $schema->create('barstools', function (Blueprint $table) { + $schema->create(config('barstool.table', 'barstools'), function (Blueprint $table) { $table->id(); $table->uuid('uuid')->index(); $table->timestamp('created_at')->useCurrent()->index(); diff --git a/src/Barstool.php b/src/Barstool.php index 5fe956d..22be9ca 100755 --- a/src/Barstool.php +++ b/src/Barstool.php @@ -322,12 +322,24 @@ private static function persist(RecordingType $type, array $payload, string $uui return; } - Models\Barstool::query()->updateOrCreate( + self::getModelClass()::query()->updateOrCreate( ['uuid' => $uuid], $payload, ); } + /** + * Get the model class used to store recordings. + * + * @internal + * + * @return class-string + */ + public static function getModelClass(): string + { + return config('barstool.model', Models\Barstool::class); + } + private static function shouldQueue(): bool { return config('barstool.queue.enabled', false) === true; diff --git a/src/Jobs/RecordBarstoolJob.php b/src/Jobs/RecordBarstoolJob.php index 81205ed..8acdd17 100644 --- a/src/Jobs/RecordBarstoolJob.php +++ b/src/Jobs/RecordBarstoolJob.php @@ -9,6 +9,7 @@ use Illuminate\Foundation\Queue\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldBeUnique; +use Saloon\Barstool\Barstool as BarstoolRecorder; class RecordBarstoolJob implements ShouldBeUnique, ShouldQueue { @@ -40,7 +41,7 @@ public function handle(): void if ($this->type === RecordingType::REQUEST) { // updateOrCreate keeps retries idempotent - a job that fails after its // insert committed must not create a duplicate row on the next attempt. - Barstool::query() + BarstoolRecorder::getModelClass()::query() ->updateOrCreate( ['uuid' => $this->uuid], $this->data, @@ -49,7 +50,7 @@ public function handle(): void return; } - $barstool = Barstool::query() + $barstool = BarstoolRecorder::getModelClass()::query() ->where('uuid', $this->uuid) ->first(); diff --git a/src/Models/Barstool.php b/src/Models/Barstool.php index 082a6be..7461015 100644 --- a/src/Models/Barstool.php +++ b/src/Models/Barstool.php @@ -57,6 +57,7 @@ public function __construct(array $attributes = []) parent::__construct($attributes); $this->setConnection(config('barstool.connection')); + $this->setTable(config('barstool.table', 'barstools')); } /** diff --git a/tests/BarstoolTest.php b/tests/BarstoolTest.php index 97152e8..398cff6 100644 --- a/tests/BarstoolTest.php +++ b/tests/BarstoolTest.php @@ -7,10 +7,12 @@ use Saloon\Http\Faking\MockClient; use Saloon\Barstool\Models\Barstool; use Saloon\Http\Faking\MockResponse; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Artisan; use Saloon\Barstool\Enums\RecordingType; +use Illuminate\Database\Schema\Blueprint; use Saloon\Http\Connectors\NullConnector; use Saloon\Barstool\Jobs\RecordBarstoolJob; use Saloon\Http\Response as SaloonResponse; @@ -25,6 +27,7 @@ use Saloon\Barstool\Barstool as BarstoolRecorder; use Saloon\Exceptions\Request\FatalRequestException; use Saloon\Barstool\Tests\Fixtures\Requests\PostRequest; +use Saloon\Barstool\Tests\Fixtures\Models\CustomBarstool; use Saloon\Barstool\Tests\Fixtures\Requests\GetFileRequest; use Saloon\Barstool\Tests\Fixtures\Requests\SoloUserRequest; use Saloon\Barstool\Tests\Fixtures\Connectors\RandomConnector; @@ -117,6 +120,75 @@ expect($migration->getConnection())->toBe('mysql'); }); +it('can change the table name', function () { + config()->set('barstool.enabled', true); + config()->set('barstool.table', 'saloon_logs'); + + $migration = include __DIR__.'/../database/migrations/create_barstools_table.php.stub'; + $migration->up(); + + MockClient::global([ + SoloUserRequest::class => MockResponse::make(body: ['data' => 'ok'], status: 200), + ]); + + (new SoloUserRequest)->send(); + + expect(Barstool::make()->getTable())->toBe('saloon_logs'); + + assertDatabaseCount('saloon_logs', 1); + assertDatabaseCount('barstools', 0); +}); + +it('runs the upgrade migrations against the configured table', function () { + config()->set('barstool.table', 'saloon_logs'); + + $schema = Schema::connection(config('barstool.connection')); + + // A table from before the context column and created_at index were added + $schema->create('saloon_logs', function (Blueprint $table) { + $table->id(); + $table->timestamp('created_at'); + }); + + $contextMigration = include __DIR__.'/../database/migrations/add_context_to_barstools_table.php.stub'; + $contextMigration->up(); + + $indexMigration = include __DIR__.'/../database/migrations/add_created_at_index_to_barstools_table.php.stub'; + $indexMigration->up(); + + // Running it again must detect the existing index instead of adding a duplicate + $indexMigration->up(); + + expect($schema->hasColumn('saloon_logs', 'context'))->toBeTrue(); + expect($schema->hasIndex('saloon_logs', ['created_at']))->toBeTrue(); +}); + +it('can use a custom model', function (bool $queued) { + Event::fake([ + 'eloquent.created: '.CustomBarstool::class, + 'eloquent.updated: '.CustomBarstool::class, + ]); + + config()->set('barstool.enabled', true); + config()->set('barstool.model', CustomBarstool::class); + config()->set('barstool.queue.enabled', $queued); + config()->set('queue.default', 'sync'); + + MockClient::global([ + SoloUserRequest::class => MockResponse::make(body: ['data' => 'ok'], status: 200), + ]); + + (new SoloUserRequest)->send(); + + Event::assertDispatched('eloquent.created: '.CustomBarstool::class); + Event::assertDispatched('eloquent.updated: '.CustomBarstool::class); + + expect(CustomBarstool::sole())->response_status->toBe(200); +})->with([ + 'synchronously' => false, + 'via the queue' => true, +]); + it('can change the number of days to keep recordings for', function () { // Check the default value is 30 expect(config('barstool.keep_for_days'))->toBe(30); diff --git a/tests/Fixtures/Models/CustomBarstool.php b/tests/Fixtures/Models/CustomBarstool.php new file mode 100644 index 0000000..18ab718 --- /dev/null +++ b/tests/Fixtures/Models/CustomBarstool.php @@ -0,0 +1,12 @@ +