jfheinrich-eu / laravel-make-commands
This package is aimed to be a suite of artisan commands and tools to help make the work easier.
Requires
- eventsauce/object-hydrator: 1.4.0
- laravel/framework: 10.48.22
- monolog/monolog: 3.7.0
- spatie/laravel-package-tools: 1.16.4
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: 1.17.2
- nunomaduro/collision: 7.10.0
- orchestra/testbench: 8.19.0
- orchestra/testbench-core: 8.19.0
- php-mock/php-mock-mockery: 1.4.1
- phpstan/phpstan: 1.12.0
- phpstan/phpstan-mockery: 1.1.2
- phpstan/phpstan-phpunit: 1.4.0
- phpunit/phpunit: 10.5.30
- dev-main
- dev-develop
- 3.3.0-rc.13
- 3.3.0-rc.12
- 3.3.0-rc.11
- 3.3.0-rc.10
- 3.3.0-rc.9
- 3.3.0-rc.8
- 3.3.0-rc.7
- 3.3.0-rc.6
- 3.3.0-rc.5
- 3.3.0-rc.4
- 3.3.0-rc.3
- 3.3.0-rc.2
- 3.3.0-rc.1
- 3.2.3
- 3.2.3-rc.1
- 3.2.2
- 3.2.2-rc.6
- 3.2.2-rc.5
- 3.2.2-rc.4
- 3.2.2-rc.3
- 3.2.2-rc.2
- 3.2.2-rc.1
- 3.2.1
- 3.2.1-rc.2
- 3.2.1-rc.1
- 3.2.0
- 3.2.0-rc.2
- 3.2.0-rc.1
- 3.1.0
- 3.1.0-rc.4
- 3.1.0-rc.3
- 3.1.0-rc.2
- 3.1.0-rc.1
- 3.0.0
- 3.0.0-rc.3
- 3.0.0-rc.2
- 3.0.0-rc.1
- 2.0.0
- 2.0.0-rc.1
- 1.2.1-rc1
- 1.2.0
- 1.1.0
- 1.0.0
- 1.0.0-rc.1
This package is auto-updated.
Last update: 2024-10-24 16:04:55 UTC
README
Laravel Make Commands
This package is aimed to be a suite of artisan commands and tools to help make the work easier.
- Installation
- Make interface (make-commands:interface)
- Make repository (make-commands:repository)
- Make a service (make-commands:service)
- Data transfer object (DTO) (make-commands:dto)
- JSON database seeder
- Create JSON datafiles from database (make-commands:seeder-data)
- Extend Eloquent model to use views
- IDE - Helper Support for View model
- Credits
Installation
$ composer require jfheinrich-eu/laravel-make-commands
To publish the assets and config file, run following command:
$ php artisan make-commands:install
This install the config file to [Project root]/app/config/make-commands.php and the stubs to [Project root]/stubs/make-commands.
To install only the config file, use this command:
$ php artisan vendor:publish --tag make-commands-config
To install only the stubs, use this command:
$ php artisan vendor:publish --tag make-commands-assets
Make interface (make-commands:interface)
Creates a new interface in app\Contracts
php artisan make-commands:interface Interface
Example
$ php artisan make-commands:interface TestInterface $ cat app/Contracts/TestInterface.php <?php declare(strict_types=1); namespace App\Contracts; interface TestInterface { }
Make repository (make-commands:repository)
Creates a new repository.
Optionally you can give the model on which the repository should based on. In this case, the command creates a repository with
- a protected property for the model, which can be created by dependency injection, over the constructor.
- it create a CRUD skeleton
-
all() : returns all records as a Eloquent collection
-
create(): creates a new record and returns then new record as Eloquent Model. The data for the record will be given by the generic
RepositoryDto
class as attribute collection. -
update(): updates a existing record and returns boolean. The data to update will be given by the generic
RepositoryDto
class as attribute collection. -
delete(): deletes a existing record and returns boolean. The deletion runs over the primary key.
-
find(): returns a Eloquent model, found by primary key. Is there no record with given primary key, a ModelNotFoundException will be thrown.
-
The generic class RepositoryDto
:
<?php declare(strict_types=1); namespace JfheinrichEu\LaravelMakeCommands\Dto; use Illuminate\Support\Collection; /** * @property int $id * @property Collection<int,array<string,mixed>> $attributes */ final class RepositoryDto extends DataTransferObject { /** * @param null|int $id * @param null|Collection<int,array<string,mixed>> $attributes */ public function __construct( protected ?int $id = null, protected ?Collection $attributes = null ) { } }
The attributes property of RepositoryDto gets the required columns from the model as illuminate\Support\Collection.
This collection can be constructed like this:
$dto->setAttributes(collect(['id' => 42, 'email' => 'dummy@localhost.tld']);
Usage:
$ php artisan make-commands:repository <Repository name> --model=<Modelname>
Example
$ php artisan make-commands:repository UserRepository --model=User
app/Repositories/UserRepository.php
<?php declare(strict_types=1); namespace App\Repositories use Illuminate\Database\Eloquent\Collection; use App\Models\User; use Illuminate\Database\Eloquent\Model; use JfheinrichEu\LaravelMakeCommands\Dto\RepositoryDto; use Illuminate\Database\Eloquent\ModelNotFoundException; class TestRepository { public function __construct(protected User $user) {} public function all(): Collection { return $this->user::all(); } /** * @inheritdoc */ public function create(RepositoryDto $dto): Model|User { return $this->user::create($dto->attributes->toArray()); } /** * @inheritdoc */ public function update(RepositoryDto $dto): bool { return $this->user->whereId($dto->id) ->update($dto->attributes->toArray()); } /** * @inheritdoc */ public function delete(int $id): bool { return $this->user->whereId($id)->delete($id); } /** * @inheritdoc */ public function find(int $id): Model| User { $model = $this->user->find($id) if (null == $model) { throw new ModelNotFoundException("Resource not found"); } return $model; } }
Make a service (make-commands:service)
Creates a new service class, which can optionally implement a existing interface and can based on a existing repository.
php artisan make-commands:service name [--interface=Interface] [--repository=Repository]
Example
Use existing interface App\Contracts\UserPostInterface
<?php declare(strict_types=1); namespace App\Contracts; interface UserPostInterface { public function get( string|array|null $title, ?int $userId = 0): array|string; }
$ php artisan make-commands:service UserPostService --interface=UserPostInterface --repository=UserRepository
app/Services/UserPostService.php
<?php declare(strict_types=1); namespace App\Services; use App\Contracts\UserPostInterface; use App\Repositories\UserRepository; class UserPostService implements UserPostInterface { public function __construct(protected UserRepository $userRepository) { } public function get(array|string|null $title,?int $userId = 0): array|string { // Implementation } }
Data transfer object (DTO) (make-commands:dto)
Based on the great work of Steve McDougall with his package laraval-data-object-tools.
I extend this package with implementation of „JsonSerializable“.
Usage
To generate a new DTO all you need to do is run the following artisan command:
php artisan make-commands:dto MyDto
This will generate the following class: app/Dto/MyDto.php
. By default this class
will be a final
class that implements a DtoContract
, which extends JsonSerializable
, which enforces two methods
toArray
so that you can easily cast your DTOs to arraysJsonSerialize
so that you can easily serialize your DTOs
If you are using PHP 8.2 however, you will by default get a readonly
class generated, so that you do not have
to declare each property as readonly.
Example
$ php artisan make-commands:dto MyDto
app/Dto/MyDto.php
<?php declare(strict_types=1); namespace App\Dto; use JfheinrichEu\LaravelMakeCommands\Dto\DataTransferObject; final class MyDto extends DataTransferObject { public function __construct( // ) {} }
Work with the hydration functionality
To work with the hydration functionality you can either use Laravels DI container, or the ready made facade.
Using the container:
class StoreController { public function __construct( private readonly HydratorContract $hydrator, ) {} public function __invoke(StoreRequest $request) { $model = Model::query()->create( attributes: $this->hydrator->fill( class: ModelObject::class, parameters: $request->validated(), )->toArray(), ); } }
To work with the facade, you can do something very similar:
class StoreController { public function __invoke(StoreRequest $request) { $model = Model::query()->create( attributes: Hydrator::fill( class: ModelObject::class, parameters: $request->validated(), )->toArray(), ); } }
Object Hydration
Under the hood this package uses an EventSauce package, created by Frank de Jonge. It is possibly the best package I have found to hydrate objects nicely in PHP. You can find the documentation here if you would like to see what else you are able to do with the package to suit your needs.
JSON database seeder
The database seeder in this package
- DatabaseJsonSeeder
- JsonSeeder
are designed to seed a table from a json data file.
Usage
All what you have to do is to
integrate DatabaseJsonSeeder
in Database\Seeder\DatabaseSeeder
<?php declare(strict_types=1); namespace Database\Seeders; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use JfheinrichEu\LaravelMakeCommands\Support\Database\Seeder\DatabaseJsonSeeder; class DatabaseSeeder extends DatabaseJsonSeeder { use WithoutModelEvents; protected array $nonJsonSeeders = [ // Database\Seeders\MyNonJsonSeeder::class, ]; /** * Seed the application's database. * * @return void */ public function run(): void { parent::setCommand($this->command)->setContainer($this->container); parent::run(); // run the non JSON seeders $this->call($this->nonJsonSeeders); } }
After that, enter the models to be considered by the JsonSeeder into config/make-commands.php
and configure the paths to the seeder classes and the seeder data files,
below the key seeders
.
The order of the models must be specified according to the dependencies.
The JsonSeeder classes will be created automatically if they do not exist yet.
<?php declare(strict_types=1); return [ /* * List of all commands to be registered. */ 'commands' => [ JfheinrichEu\LaravelMakeCommands\Console\Commands\DtoMakeCommand::class, JfheinrichEu\LaravelMakeCommands\Console\Commands\InterfaceMakeCommand::class, JfheinrichEu\LaravelMakeCommands\Console\Commands\RepositoryMakeCommand::class, JfheinrichEu\LaravelMakeCommands\Console\Commands\ServiceMakeCommand::class, ], 'seeders' => [ // Path to the seeder classes, must match the namespace Database\Seeders. 'path-seeder' => database_path('seeders'), // The directory where the data files goes in. 'path-datafiles' => database_path('seeders/data'), // The models which will be used by the JsonSeeder. 'models' => [ App\Models\User::class, App\Models\Right::class, ], ], ];
Now you need to create the data files in JSON format. The file name must match the table name of the model.
Here is an example for the User Model.
database/seeders/data/users.json
[ { "id": 1, "name": "Bastard Operator from hell", "email": "bofh@jfheinrich.eu", "email_verified_at": "2023-01-01 12:00:00", "password": "$2y$10$9wMEaiSx1KpwmHnbpH33pecbGd/FrRaY5SJXoqhdZ4mZnRVZmv0Ke", "two_factor_secret": null, "two_factor_recovery_codes": null, "remember_token": null }, { "id": 2, "name": "backend", "email": "backend@jfheinrich.eu", "email_verified_at": "2023-01-01 13:00:00", "password": "$2y$10$VvIem6s7FgZIvdSiGqwG4.i0nEYYC.quHyb6SQkeALxa8lZXQnf6K", "two_factor_secret": null, "two_factor_recovery_codes": null, "remember_token": null } ]
After that, you can run the seeders with
php artisan db:seed
Create JSON datafiles from database (make-commands:seeder-data)
You can create the seeder JSON datafile directly from the database.
Use therefor the command
php artisan make-commands:seeder-data [Model…]
Example
$ php artisan make-commands:seeder-data \App\Models\User \App\Models\UserPost App\Models\User(users.json) .............................................................................................................. RUNNING App\Models\User(users.json) ........................................................................................................ 74.14 ms DONE App\Models\Right(rights.json) ............................................................................................................ RUNNING App\Models\Right(rights.json) ....................................................................................................... 2.43 ms DONE
This creates the files users.json
and user_posts.json
into the configured seeder data directory.
Extend Eloquent model to use views
The UseView
Trait allows to create eloquent models based on Views
, which are
- selectable
- updatable
- insertable
are.
Eloquent models must exist for the underlying tables of the View
.
The Model
must extend ViewModel
and must define at least the three attributes:
- protected $table = 'Name of the view'
- protected $mainTable = 'Table that serves as the main table'.
- protected $baseTables = ['All underlying tables']
The ViewModel
adds the following properties and methods to the Model
.
- static method create(array<string,mixed> $attributes): ViewModel|Collection<int, ViewModel>
- public function insert(array<string,mixed>|array<int,array<string,mixed>> $values): bool
- public function update(array<string,mixed> $attributes, array<string,mixed> $options): bool
- public function delete(): bool|null
- public function truncateView(): void
- public function getModelByTableName(string $table): Model
- public function getMainTable(): string
- public function getBaseTables(): string[]
- public function getMainTableAttributes(): string[]
- public function getAllTableAttributes(): array<string,string[]>
- Attribute bool is_view
Example
<?php declare(strict_types=1); namespace App\Models; use JfheinrichEu\LaravelMakeCommands\Models\ViewModel; class MyView extends ViewModel { /** * @var string */ protected $table = 'my_view'; /** @var string */ protected string $mainTable = 'data_table1'; /** @var string[] */ protected array $baseTables = [ 'data_table1', 'data_table2', 'data_table3', ]; ... }
IDE - Helper Support for View model
Because Eloquent and Doctrine did not run back the columns of a view, two problems arise:
- Artisan model:show does not return the columns of the view
- IDE - Helper: does not recognize the columns either and thus does not generate annotations for the columns.
For the second point there is the artisan
command make-commands:view-model-hook
in this package which creates a hook class in the directory app/Support/IdeHelper. This must then only be entered in the IDE-Helper config file under hooks.