diff --git a/composer.json b/composer.json index 2488156..be77ddb 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ ], "require": { "php": "^8.4", - "illuminate/contracts": "^12.0", + "illuminate/contracts": "^12.1", "spatie/laravel-package-tools": "^1.16" }, "require-dev": { diff --git a/config/stateful-resources.php b/config/stateful-resources.php index 3ac44ad..89d1543 100644 --- a/config/stateful-resources.php +++ b/config/stateful-resources.php @@ -1,5 +1,31 @@ [ + ...State::cases(), + // + ], + + /* + |-------------------------------------------------------------------------- + | Default State + |-------------------------------------------------------------------------- + | + | This state will be used when no state is explicitly set on the resource. + | If not set, the first state in the states array will be used. + | + */ + 'default_state' => State::Full, ]; diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 64faefc..fe72342 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -37,8 +37,15 @@ export default defineConfig({ text: 'Basics', items: [ { text: 'Installation', link: '/installation' }, + { text: 'Basic Usage', link: '/basic-usage' }, ] }, + { + text: 'Advanced Usage', + items: [ + { text: 'Extending States', link: '/extending-states' } + ] + } ], socialLinks: [ diff --git a/docs/pages/basic-usage.md b/docs/pages/basic-usage.md new file mode 100644 index 0000000..ddbc767 --- /dev/null +++ b/docs/pages/basic-usage.md @@ -0,0 +1,162 @@ +# Basic Usage + +Laravel Stateful Resources allows you to create dynamic API responses by changing the structure of your JSON resources based on different states. This is especially useful when you need to return different levels of detail for the same model depending on the context. + +## Generating a Stateful Resource + +The package provides an Artisan command to quickly generate a new stateful resource: + +```bash +php artisan make:stateful-resource UserResource +``` + +This command creates a new resource class in `app/Http/Resources/` that extends `StatefulJsonResource`: + +```php + + */ + public function toArray(Request $request): array + { + return parent::toArray($request); + } +} +``` + +## Built-in States + +The package comes with three built-in states defined in the `State` enum: + +- **`Full`** - For all available attributes +- **`Table`** - For attributes suitable for table/listing views +- **`Minimal`** - For only essential attributes + +See the [Extending States](extending-states.md) documentation for how to configure this and add custom states. + +## Using States in Resources + +Inside your stateful resource, you can use conditional methods to control which attributes are included based on the current state: + +```php + $this->id, + 'name' => $this->name, + 'email' => $this->whenState(State::Full, $this->email), + 'profile' => $this->whenStateIn([State::Full], [ + 'bio' => $this->bio, + 'avatar' => $this->avatar, + 'created_at' => $this->created_at, + ]), + 'role' => $this->whenStateIn([State::Full, State::Table], $this->role), + 'last_login' => $this->unlessState(State::Minimal, $this->last_login_at), + ]; + } +} +``` + +You can also use the string representation of states instead of enum cases: + +```php +'email' => $this->whenState('full', $this->email), +'name' => $this->unlessState('minimal', $this->full_name), +``` + +## Available Conditional Methods + +The package provides several methods to conditionally include attributes: + +### `whenState` + +Include a value only when the current state matches the specified state: + +```php +'email' => $this->whenState(State::Full, $this->email), +'admin_notes' => $this->whenState(State::Full, $this->admin_notes, 'N/A'), +``` + +### `unlessState` + +Include a value unless the current state matches the specified state: + +```php +'public_info' => $this->unlessState(State::Minimal, $this->public_information), +``` + +### `whenStateIn` + +Include a value when the current state is one of the specified states: + +```php +'detailed_info' => $this->whenStateIn([State::Full, State::Table], [ + 'department' => $this->department, + 'position' => $this->position, +]), +``` + +### `unlessStateIn` + +Include a value unless the current state is one of the specified states: + +```php +'sensitive_data' => $this->unlessStateIn([State::Minimal, State::Table], $this->sensitive_info), +``` + +### Magic Conditionals + +You can also use magic methods with for cleaner syntax: + +```php +'email' => $this->whenStateFull($this->email), +'name' => $this->unlessStateMinimal($this->full_name), +``` + +## Using Stateful Resources + +### Setting the State Explicitly + +Use the static `state()` method to create a resource with a specific state: + +```php +$user = UserResource::state(State::Minimal)->make($user); +``` + +### Using Magic Methods + +You can also use magic methods for a more fluent syntax: + +```php +// This is equivalent to the explicit state(State::Minimal) call +$user = UserResource::minimal()->make($user); +``` + +### Default State + +If no state is specified, the resource will use the default state. You can change the default state in the package's configuration file: `config/stateful-resources.php`. + +```php +// Uses the default state +$user = UserResource::make($user); +``` diff --git a/docs/pages/extending-states.md b/docs/pages/extending-states.md new file mode 100644 index 0000000..8c6a152 --- /dev/null +++ b/docs/pages/extending-states.md @@ -0,0 +1,80 @@ +# Extending States + +You may find yourself being too limited with the three State states included in the package's `State` enum. +This package allows you to register custom states that you can then use in your resources. + +## Registering Custom States + +Before using a custom state, register it in the package's `stateful-resources.states` configuration: + +```php + [ + ...State::cases(), // The built-in states + 'custom', // Your custom state as a string + ...CustomResourceState::cases(), // Or as cases of a custom enum + ], +]; +``` + +## Creating a Custom State Enum + +Instead of using strings, you may want to create your own state enum to define custom states. This enum should implement the `ResourceState` interface provided by the package. + +```php + $this->id, + 'name' => $this->name, + 'email' => $this->whenState(CustomResourceState::Extended, $this->email), + 'debug_info' => $this->whenStateDebug([ + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]), + 'avatar' => $this->unlessState('custom', $this->avatar), + ]; + } +} +``` + +You can then apply the custom states to your resource in the same way you would with the built-in states: + +```php +// Using the static method +UserResource::state(CustomResourceState::Compact)->make($user); + +// Using the magic method (if the state name matches the case name) +UserResource::compact()->make($user); +``` diff --git a/docs/pages/installation.md b/docs/pages/installation.md index a74362b..a49857d 100644 --- a/docs/pages/installation.md +++ b/docs/pages/installation.md @@ -3,7 +3,7 @@ ## Requirements - PHP \>= 8.4 -- Laravel 12.x +- Laravel \>= 12.1 ## Installation diff --git a/src/Builder.php b/src/Builder.php index f8abb45..a459ff7 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -2,19 +2,35 @@ namespace Farbcode\StatefulResources; -use Farbcode\StatefulResources\Enums\ResourceState; +use Farbcode\StatefulResources\Concerns\ResolvesState; +use Farbcode\StatefulResources\Contracts\ResourceState; use Illuminate\Support\Facades\Context; +/** + * Builder for creating resource instances with a specific state. + * + * @internal + */ class Builder { + use ResolvesState; + private string $resourceClass; - private ResourceState $state; + private string $state; - public function __construct(string $resourceClass, ResourceState $state) + public function __construct(string $resourceClass, string|ResourceState $state) { + $state = $this->resolveState($state); + + $registeredState = app(StateRegistry::class)->tryFrom($state); + + if ($registeredState === null) { + throw new \InvalidArgumentException("State \"{$state}\" is not registered."); + } + $this->resourceClass = $resourceClass; - $this->state = $state; + $this->state = $registeredState; } /** diff --git a/src/Concerns/ResolvesState.php b/src/Concerns/ResolvesState.php new file mode 100644 index 0000000..ccd7406 --- /dev/null +++ b/src/Concerns/ResolvesState.php @@ -0,0 +1,25 @@ +value : $state; + + if (app(StateRegistry::class)->tryFrom($stateString) === null) { + throw new \InvalidArgumentException("State \"{$stateString}\" is not registered."); + } + + return $stateString; + } +} diff --git a/src/Concerns/StatefullyLoadsAttributes.php b/src/Concerns/StatefullyLoadsAttributes.php index 78cc36f..75cda5c 100644 --- a/src/Concerns/StatefullyLoadsAttributes.php +++ b/src/Concerns/StatefullyLoadsAttributes.php @@ -2,41 +2,29 @@ namespace Farbcode\StatefulResources\Concerns; -use Farbcode\StatefulResources\Enums\ResourceState; +use Farbcode\StatefulResources\Contracts\ResourceState; +use Farbcode\StatefulResources\StateRegistry; use Illuminate\Http\Resources\ConditionallyLoadsAttributes; -use Illuminate\Http\Resources\MergeValue; -use Illuminate\Http\Resources\MissingValue; /** * @see \Illuminate\Http\Resources\ConditionallyLoadsAttributes - * - * @method MissingValue|mixed whenStateMinimal(mixed $value, mixed $default = null) - * @method MissingValue|mixed unlessStateMinimal(mixed $value, mixed $default = null) - * @method MissingValue|mixed whenStateFull(mixed $value, mixed $default = null) - * @method MissingValue|mixed unlessStateFull(mixed $value, mixed $default = null) - * @method MissingValue|mixed whenStateTable(mixed $value, mixed $default = null) - * @method MissingValue|mixed unlessStateTable(mixed $value, mixed $default = null) - * @method MergeValue|mixed mergeWhenStateMinimal(mixed $value, mixed $default = null) - * @method MergeValue|mixed mergeUnlessStateMinimal(mixed $value, mixed $default = null) - * @method MergeValue|mixed mergeWhenStateFull(mixed $value, mixed $default = null) - * @method MergeValue|mixed mergeUnlessStateFull(mixed $value, mixed $default = null) - * @method MissingValue|mixed whenStateTable(mixed $value, mixed $default = null) - * @method MissingValue|mixed unlessStateTable(mixed $value, mixed $default = null) */ trait StatefullyLoadsAttributes { - use ConditionallyLoadsAttributes; + use ConditionallyLoadsAttributes, ResolvesState; /** * Retrieve a value if the current state matches the given state. * - * @param ResourceState $state + * @param string|ResourceState $state * @param mixed $value * @param mixed $default * @return \Illuminate\Http\Resources\MissingValue|mixed */ protected function whenState($state, $value, $default = null) { + $state = $this->resolveState($state); + if (func_num_args() === 3) { return $this->when($this->getState() === $state, $value, $default); } @@ -47,13 +35,15 @@ protected function whenState($state, $value, $default = null) /** * Retrieve a value unless the current state matches the given state. * - * @param ResourceState $state + * @param string|ResourceState $state * @param mixed $value * @param mixed $default * @return \Illuminate\Http\Resources\MissingValue|mixed */ protected function unlessState($state, $value, $default = null) { + $state = $this->resolveState($state); + if (func_num_args() === 3) { return $this->unless($this->getState() === $state, $value, $default); } @@ -64,13 +54,15 @@ protected function unlessState($state, $value, $default = null) /** * Retrieve a value if the current state is one of the given states. * - * @param array $states + * @param array $states * @param mixed $value * @param mixed $default * @return \Illuminate\Http\Resources\MissingValue|mixed */ protected function whenStateIn(array $states, $value, $default = null) { + $states = array_map(fn ($state) => $this->resolveState($state), $states); + $condition = in_array($this->getState(), $states, true); if (func_num_args() === 3) { @@ -83,13 +75,15 @@ protected function whenStateIn(array $states, $value, $default = null) /** * Retrieve a value unless the current state is one of the given states. * - * @param array $states + * @param array $states * @param mixed $value * @param mixed $default * @return \Illuminate\Http\Resources\MissingValue|mixed */ protected function unlessStateIn(array $states, $value, $default = null) { + $states = array_map(fn ($state) => $this->resolveState($state), $states); + $condition = in_array($this->getState(), $states, true); if (func_num_args() === 3) { @@ -102,13 +96,15 @@ protected function unlessStateIn(array $states, $value, $default = null) /** * Merge a value if the current state matches the given state. * - * @param ResourceState $state + * @param string|ResourceState $state * @param mixed $value * @param mixed $default * @return \Illuminate\Http\Resources\MergeValue|mixed */ protected function mergeWhenState($state, $value, $default = null) { + $state = $this->resolveState($state); + if (func_num_args() === 3) { return $this->mergeWhen($this->getState() === $state, $value, $default); } @@ -119,13 +115,15 @@ protected function mergeWhenState($state, $value, $default = null) /** * Merge a value unless the current state matches the given state. * - * @param ResourceState $state + * @param string|ResourceState $state * @param mixed $value * @param mixed $default * @return \Illuminate\Http\Resources\MergeValue|mixed */ protected function mergeUnlessState($state, $value, $default = null) { + $state = $this->resolveState($state); + if (func_num_args() === 3) { return $this->mergeUnless($this->getState() === $state, $value, $default); } @@ -137,7 +135,7 @@ protected function mergeUnlessState($state, $value, $default = null) * Get the current state of the resource. * This method should be implemented by the class using this trait. */ - abstract protected function getState(): ResourceState; + abstract protected function getState(): string; public function __call($method, $parameters) { @@ -155,7 +153,13 @@ public function __call($method, $parameters) continue; } - return $this->{$singleStateMethod}(ResourceState::from($state), ...$parameters); + $stateInstance = app(StateRegistry::class)->tryFrom($state); + + if ($stateInstance === null) { + continue; + } + + return $this->{$singleStateMethod}($stateInstance, ...$parameters); } } diff --git a/src/Console/Commands/StatefulResourceMakeCommand.php b/src/Console/Commands/StatefulResourceMakeCommand.php new file mode 100644 index 0000000..3a17233 --- /dev/null +++ b/src/Console/Commands/StatefulResourceMakeCommand.php @@ -0,0 +1,75 @@ +resolveStubPath('/stubs/stateful-resource.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return __DIR__.$stub; + } + + /** + * Get the default namespace for the class. + * + * @param string $rootNamespace + * @return string + */ + protected function getDefaultNamespace($rootNamespace) + { + return $rootNamespace.'\Http\Resources'; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + ['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the resource already exists'], + ]; + } +} diff --git a/src/Console/Commands/stubs/stateful-resource.stub b/src/Console/Commands/stubs/stateful-resource.stub new file mode 100644 index 0000000..a6718a0 --- /dev/null +++ b/src/Console/Commands/stubs/stateful-resource.stub @@ -0,0 +1,19 @@ + + */ + public function toArray(Request $request): array + { + return parent::toArray($request); + } +} diff --git a/src/Contracts/ResourceState.php b/src/Contracts/ResourceState.php new file mode 100644 index 0000000..b8c08ab --- /dev/null +++ b/src/Contracts/ResourceState.php @@ -0,0 +1,10 @@ +states[] = $state; + } + + /** + * Try to find a state by value across all registered states. + */ + public function tryFrom(string $value): ?string + { + if (in_array($value, $this->states, true)) { + return $value; + } + + return null; + } + + /** + * Get all available states from all registered states. + * + * @return string[] List of states. + */ + public function all(): array + { + return $this->states; + } + + /** + * Clear all registered states. + */ + public function clear(): void + { + $this->states = []; + } + + public function getDefaultState(): string + { + $explicitDefault = config('stateful-resources.default_state'); + + if ($explicitDefault instanceof ResourceState) { + $explicitDefault = $explicitDefault->value; + } + + if ($explicitDefault !== null) { + $state = $this->tryFrom($explicitDefault); + if ($state !== null) { + return $state; + } + } + + if (empty($this->states)) { + throw new InvalidArgumentException('No states registered in the StateRegistry.'); + } + + return $this->states[0]; + } +} diff --git a/src/StatefulJsonResource.php b/src/StatefulJsonResource.php index f55488c..8dcd62c 100755 --- a/src/StatefulJsonResource.php +++ b/src/StatefulJsonResource.php @@ -3,25 +3,20 @@ namespace Farbcode\StatefulResources; use Farbcode\StatefulResources\Concerns\StatefullyLoadsAttributes; -use Farbcode\StatefulResources\Enums\ResourceState; +use Farbcode\StatefulResources\Contracts\ResourceState; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\Facades\Context; -/** - * @method static \Farbcode\StatefulResources\Builder minimal() - * @method static \Farbcode\StatefulResources\Builder table() - * @method static \Farbcode\StatefulResources\Builder full() - */ abstract class StatefulJsonResource extends JsonResource { use StatefullyLoadsAttributes; - private ResourceState $state; + private string $state; /** * Create a new stateful resource builder with a specific state. */ - public static function state(ResourceState $state): Builder + public static function state(string|ResourceState $state): Builder { return new Builder(static::class, $state); } @@ -29,7 +24,7 @@ public static function state(ResourceState $state): Builder /** * Retrieve the state of the stateful resource. */ - protected function getState(): ResourceState + protected function getState(): string { return $this->state; } @@ -41,7 +36,9 @@ protected function getState(): ResourceState */ public function __construct($resource) { - $this->state = Context::get('resource-state-'.static::class, ResourceState::Full); + $defaultState = app(StateRegistry::class)->getDefaultState(); + + $this->state = Context::get('resource-state-'.static::class, $defaultState); parent::__construct($resource); } @@ -56,7 +53,7 @@ public function __construct($resource) */ public static function __callStatic($method, $parameters) { - $state = ResourceState::tryFrom($method); + $state = app(StateRegistry::class)->tryFrom($method); if ($state === null) { return parent::__callStatic($method, $parameters); diff --git a/src/StatefulResourcesServiceProvider.php b/src/StatefulResourcesServiceProvider.php index ee288fe..b248fd7 100644 --- a/src/StatefulResourcesServiceProvider.php +++ b/src/StatefulResourcesServiceProvider.php @@ -2,6 +2,7 @@ namespace Farbcode\StatefulResources; +use Farbcode\StatefulResources\Contracts\ResourceState; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -16,6 +17,27 @@ public function configurePackage(Package $package): void */ $package ->name('stateful-resources') + ->hasCommands([ + Console\Commands\StatefulResourceMakeCommand::class, + ]) ->hasConfigFile(); } + + public function bootingPackage(): void + { + $states = config()->array('stateful-resources.states'); + + $this->app->singleton(StateRegistry::class, function () use ($states) { + $registry = new StateRegistry; + + foreach ($states as $state) { + if ($state instanceof ResourceState) { + $state = $state->value; + } + $registry->register($state); + } + + return $registry; + }); + } } diff --git a/tests/Feature/CustomStatesTest.php b/tests/Feature/CustomStatesTest.php new file mode 100644 index 0000000..bff8187 --- /dev/null +++ b/tests/Feature/CustomStatesTest.php @@ -0,0 +1,32 @@ +createOne(); +}); + +it('can use a custom user-defined state', function () { + /** @var TestCase $this */ + $cat = Cat::firstOrFail(); + + $resource = CatResource::state('custom')->make($cat)->toJson(); + + expect($resource)->toBeJson(); + + expect($resource)->json()->toEqual([ + 'id' => $cat->id, + 'name' => $cat->name, + 'custom_field' => 'custom_value', + ]); +}); + +it('cannot use an unregistered state', function () { + /** @var TestCase $this */ + $cat = Cat::firstOrFail(); + + expect(fn () => CatResource::state('non_existent')->make($cat)->toJson()) + ->toThrow(InvalidArgumentException::class, 'State "non_existent" is not registered.'); +}); diff --git a/tests/Feature/DefaultStatesTest.php b/tests/Feature/DefaultStatesTest.php new file mode 100644 index 0000000..e02eb31 --- /dev/null +++ b/tests/Feature/DefaultStatesTest.php @@ -0,0 +1,75 @@ +createOne(); +}); + +it('can return a stateful resource with the default state', function () { + /** @var TestCase $this */ + $cat = Cat::firstOrFail(); + + $resource = CatResource::make($cat)->toJson(); + + expect($resource)->toBeJson(); + + expect($resource)->json()->toEqual([ + 'id' => $cat->id, + 'name' => $cat->name, + 'breed' => $cat->breed, + 'fluffyness' => $cat->fluffyness, + 'color' => $cat->color, + ]); + +}); + +it('can return a stateful resource with the correct "full" state', function () { + /** @var TestCase $this */ + $cat = Cat::firstOrFail(); + + $resource = CatResource::state(State::Full)->make($cat)->toJson(); + + expect($resource)->toBeJson(); + + expect($resource)->json()->toEqual([ + 'id' => $cat->id, + 'name' => $cat->name, + 'breed' => $cat->breed, + 'fluffyness' => $cat->fluffyness, + 'color' => $cat->color, + ]); +}); + +it('can use a stateful resource with the "minimal" state', function () { + /** @var TestCase $this */ + $cat = Cat::firstOrFail(); + + $resource = CatResource::state(State::Minimal)->make($cat)->toJson(); + + expect($resource)->toBeJson(); + + expect($resource)->json()->toEqual([ + 'id' => $cat->id, + 'name' => $cat->name, + ]); +}); + +it('can use a stateful resource with the "table" state', function () { + /** @var TestCase $this */ + $cat = Cat::firstOrFail(); + + $resource = CatResource::state(State::Table)->make($cat)->toJson(); + + expect($resource)->toBeJson(); + + expect($resource)->json()->toEqual([ + 'id' => $cat->id, + 'name' => $cat->name, + 'breed' => $cat->breed, + ]); +}); diff --git a/tests/Feature/MakeCommandTest.php b/tests/Feature/MakeCommandTest.php new file mode 100644 index 0000000..7c28256 --- /dev/null +++ b/tests/Feature/MakeCommandTest.php @@ -0,0 +1,25 @@ +testResourcePath = app_path('Http/Resources/TestResource.php'); +}); + +afterEach(function () { + if (File::exists($this->testResourcePath)) { + File::delete($this->testResourcePath); + } +}); + +it('can create a new stateful resource', function () { + /** @var TestCase $this */ + $this->artisan('make:stateful-resource', [ + 'name' => 'TestResource', + ]) + ->assertExitCode(0) + ->expectsOutputToContain('created successfully.'); + + $this->assertFileExists(app_path('Http/Resources/TestResource.php')); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php index 52176b1..4140942 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,13 +2,15 @@ namespace Farbcode\StatefulResources\Tests; +use Farbcode\StatefulResources\Enums\State; use Farbcode\StatefulResources\StatefulResourcesServiceProvider; +use Illuminate\Foundation\Testing\RefreshDatabase; use Orchestra\Testbench\Concerns\WithWorkbench; use Orchestra\Testbench\TestCase as Orchestra; class TestCase extends Orchestra { - use WithWorkbench; + use RefreshDatabase, WithWorkbench; protected $loadEnvironmentVariables = false; @@ -26,7 +28,10 @@ protected function getPackageProviders($app) protected function getEnvironmentSetUp($app) { - // + $app['config']->set('stateful-resources.states', [ + ...State::cases(), + 'custom', + ]); } /** diff --git a/workbench/app/Http/Resources/CatResource.php b/workbench/app/Http/Resources/CatResource.php new file mode 100644 index 0000000..92f0e67 --- /dev/null +++ b/workbench/app/Http/Resources/CatResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'breed' => $this->whenStateIn([State::Full, State::Table], $this->breed), + 'fluffyness' => $this->whenStateIn([State::Full], $this->fluffyness), + 'color' => $this->whenStateIn([State::Full], $this->color), + 'custom_field' => $this->whenState('custom', 'custom_value'), + ]; + } +} diff --git a/workbench/app/Models/Cat.php b/workbench/app/Models/Cat.php new file mode 100644 index 0000000..14c38e4 --- /dev/null +++ b/workbench/app/Models/Cat.php @@ -0,0 +1,27 @@ + + */ + protected $fillable = ['name', 'breed', 'fluffyness', 'color']; +} diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php index 5cb54b7..e8cec9c 100644 --- a/workbench/app/Providers/WorkbenchServiceProvider.php +++ b/workbench/app/Providers/WorkbenchServiceProvider.php @@ -19,8 +19,6 @@ public function register(): void */ public function boot(): void { - $this->publishes([ - __DIR__.'/../../../workbench/test-data' => public_path('test-data'), - ], 'test-assets'); + // } } diff --git a/workbench/database/factories/CatFactory.php b/workbench/database/factories/CatFactory.php new file mode 100644 index 0000000..2a95b5e --- /dev/null +++ b/workbench/database/factories/CatFactory.php @@ -0,0 +1,76 @@ + + */ +class CatFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var class-string + */ + protected $model = Cat::class; + + protected $names = [ + 'Whiskers', + 'Mittens', + 'Shadow', + 'Simba', + 'Luna', + 'Oliver', + 'Bella', + 'Charlie', + 'Lucy', + 'Max', + ]; + + protected $breeds = [ + 'Siamese', + 'Persian', + 'Maine Coon', + 'Bengal', + 'British Shorthair', + 'Ragdoll', + 'Sphynx', + 'Norwegian Forest', + 'Scottish Fold', + 'Abyssinian', + ]; + + protected $fluffyness = [ + 'superfluffy', + 'fluffy', + 'not-fluffy', + ]; + + protected $color = [ + 'black', + 'white', + 'gray', + 'orange', + 'brown', + ]; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->randomElement($this->names), + 'breed' => $this->faker->randomElement($this->breeds), + 'fluffyness' => $this->faker->randomElement($this->fluffyness), + 'color' => $this->faker->randomElement($this->color), + ]; + } +} diff --git a/workbench/database/migrations/2025_07_17_142334_create_cats_table.php b/workbench/database/migrations/2025_07_17_142334_create_cats_table.php new file mode 100644 index 0000000..356e819 --- /dev/null +++ b/workbench/database/migrations/2025_07_17_142334_create_cats_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('name'); + $table->string('breed'); + $table->string('fluffyness'); + $table->string('color'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cats'); + } +}; diff --git a/workbench/database/seeders/DatabaseSeeder.php b/workbench/database/seeders/DatabaseSeeder.php index f10adbb..832a4dc 100644 --- a/workbench/database/seeders/DatabaseSeeder.php +++ b/workbench/database/seeders/DatabaseSeeder.php @@ -3,6 +3,7 @@ namespace Workbench\Database\Seeders; use Illuminate\Database\Seeder; +use Workbench\Database\Factories\CatFactory; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Workbench\Database\Factories\UserFactory; @@ -19,5 +20,7 @@ public function run(): void 'name' => 'Test User', 'email' => 'test@example.com', ]); + + CatFactory::new()->times(10)->create(); } }