Skip to content

Commit 6ef6609

Browse files
committed
chore: Add make:entity and make:mapper
1 parent a8a0573 commit 6ef6609

10 files changed

+457
-0
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,43 @@ Concise is extensible and flexible, and will most likely serve as the basis for
1919
with many sensible defaults, and a sprinkle of magic that'll simplify using it based on certain conventions.
2020
It also comes with a driver for Laravel's auth functionality,
2121
and route entity binding (route model binding but for concise entities).
22+
23+
## Getting Started
24+
25+
First things first, create yourself an entity.
26+
27+
```shell
28+
php artisan make:entity Blog
29+
```
30+
31+
By default, it is assumed that the entity...
32+
33+
- Is using an `id` field/column as its identifier
34+
- Is using the default connection
35+
- Has a table name that can be determined from the model name (`blogs` in this example)
36+
37+
To change any of these, you can provide the options `--identity`, `--connection` and `--table` respectively.
38+
This command will create two files...
39+
40+
- `app/Entities/Blog.php`
41+
- `app/Mappers/Entities/BlogMapper.php`
42+
43+
Next you'll want to...
44+
45+
- Open up your new `Blog` class, add the fields as properties, and add some getters and setters, if you want.
46+
- Open up your new `BlogMapper` class and update the `toObject()` and `toData()` methods.
47+
48+
Concise entities can also make use of implicit route "model" binding, so there's no need to worry about manually doing
49+
that.
50+
To get a repository for a given entity, you can use the `EntityRepository` contextual attribute, like so.
51+
52+
```php
53+
use Articulate\Concise\Attributes\EntityRepository;
54+
use Articulate\Concise\Contracts\Repository;
55+
56+
class ExampleController {
57+
public function __construct(
58+
#[EntityRepository(Blog::class)] private Repository $blogRepository
59+
) {}
60+
}
61+
```

resources/stubs/entity.stub

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace {{ namespace }};
4+
5+
class {{ class }}
6+
{
7+
private int ${{ identity }};
8+
9+
public function get{{ identityCamel }}(): int
10+
{
11+
return $this->{{ identity }};
12+
}
13+
14+
public function has{{ identityCamel }}(): bool
15+
{
16+
return isset($this->{{ identity }});
17+
}
18+
19+
public function set{{ identityCamel }}(int ${{ identity }}): self
20+
{
21+
$this->{{ identity }} = ${{ identity }};
22+
23+
return $this;
24+
}
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Get the connection the entity should use
3+
*
4+
* @return string|null
5+
*/
6+
public function connection(): ?string
7+
{
8+
return '{{ connection }}';
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Get the name of the identity field
3+
*
4+
* @return string
5+
*/
6+
public function identity(): string
7+
{
8+
return '{{ identity }}';
9+
}

resources/stubs/mapper.entity.stub

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace {{ namespace }};
4+
5+
use Articulate\Concise\EntityMapper;
6+
7+
/**
8+
* @extends \Articulate\Concise\EntityMapper<{{ entityClass }}>
9+
*/
10+
class {{ class }}Mapper extends EntityMapper
11+
{
12+
/**
13+
* Get the class name this mapper represents.
14+
*
15+
* Returns the fully qualified class name of the object that this mapper is
16+
* mapping.
17+
*
18+
* @return class-string<{{ entityClass }}>
19+
*/
20+
public function class(): string
21+
{
22+
return {{ entityClass }}::class;
23+
}
24+
25+
/**
26+
* Convert a raw array to an object representation.
27+
*
28+
* @param array<string, mixed> $data
29+
*
30+
* @return object
31+
*
32+
* @phpstan-return {{ entityClass }}
33+
*/
34+
public function toObject(array $data): object
35+
{
36+
return new {{ entityClass }}();
37+
}
38+
39+
/**
40+
* Convert an object representation to raw data.
41+
*
42+
* @param object $object
43+
*
44+
* @phpstan-param {{ entityClass }} $object
45+
*
46+
* @return array<string, mixed>
47+
*/
48+
public function toData(object $object): array
49+
{
50+
return [];
51+
}
52+
{{ extraMethods }}
53+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Get the entities' database table
3+
*
4+
* @return string
5+
*/
6+
public function table(): string
7+
{
8+
return '{{ table }}';
9+
}

src/Attributes/EntityRepository.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Articulate\Concise\Attributes;
5+
6+
use Articulate\Concise\Concise;
7+
use Articulate\Concise\Contracts\Repository;
8+
use Attribute;
9+
use Illuminate\Container\Container;
10+
use Illuminate\Contracts\Container\ContextualAttribute;
11+
12+
/**
13+
* @template EntityObject of object
14+
*/
15+
#[Attribute(Attribute::TARGET_PARAMETER)]
16+
final readonly class EntityRepository implements ContextualAttribute
17+
{
18+
/**
19+
* @var class-string<EntityObject>
20+
*/
21+
public string $entity;
22+
23+
/**
24+
* @param class-string<EntityObject> $entity
25+
*/
26+
public function __construct(string $entity)
27+
{
28+
$this->entity = $entity;
29+
}
30+
31+
/**
32+
* @param \Articulate\Concise\Attributes\EntityRepository<EntityObject> $attribute
33+
* @param \Illuminate\Container\Container $container
34+
*
35+
* @return \Articulate\Concise\Contracts\Repository<EntityObject>
36+
*
37+
* @throws \Illuminate\Contracts\Container\BindingResolutionException
38+
*/
39+
public function resolve(self $attribute, Container $container): Repository
40+
{
41+
/** @noinspection NullPointerExceptionInspection */
42+
return $container->make(Concise::class)->repository($attribute->entity);
43+
}
44+
}

src/Commands/MakeEntityCommand.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Articulate\Concise\Commands;
5+
6+
use Illuminate\Console\GeneratorCommand;
7+
use Illuminate\Support\Str;
8+
use Symfony\Component\Console\Attribute\AsCommand;
9+
10+
#[AsCommand('make:entity')]
11+
class MakeEntityCommand extends GeneratorCommand
12+
{
13+
/**
14+
* The console command signature.
15+
*
16+
* @var string
17+
*/
18+
protected $signature = 'make:entity {name : The name of the entity}
19+
{--table= : The entity table}
20+
{--identity= : The entity identity field}
21+
{--connection= : The connection the entity uses}';
22+
23+
/**
24+
* The console command description.
25+
*
26+
* @var string
27+
*/
28+
protected $description = 'Create a new entity';
29+
30+
protected $type = 'Entity';
31+
32+
/**
33+
* Execute the console command.
34+
*
35+
* @return bool|null
36+
*
37+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
38+
*/
39+
public function handle(): bool|null
40+
{
41+
if (parent::handle() === false) {
42+
return false;
43+
}
44+
45+
$this->call('make:mapper', [
46+
'name' => $this->argument('name') . 'Mapper',
47+
'class' => $this->argument('name'),
48+
'--identity' => $this->option('identity'),
49+
'--table' => $this->option('table'),
50+
'--connection' => $this->option('connection'),
51+
]);
52+
53+
return null;
54+
}
55+
56+
/**
57+
* Get the stub file for the generator.
58+
*
59+
* @return string
60+
*/
61+
protected function getStub(): string
62+
{
63+
return $this->resolveStubPath('/../../resources/stubs/entity.stub');
64+
}
65+
66+
/**
67+
* Resolve the fully qualified path to the stub.
68+
*
69+
* @param string $stub
70+
*
71+
* @return string
72+
*/
73+
protected function resolveStubPath(string $stub): string
74+
{
75+
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
76+
? $customPath
77+
: __DIR__ . $stub;
78+
}
79+
80+
/**
81+
* Get the default namespace for the class.
82+
*
83+
* @param string $rootNamespace
84+
*
85+
* @return string
86+
*/
87+
protected function getDefaultNamespace($rootNamespace): string
88+
{
89+
return $rootNamespace . '\\Entities';
90+
}
91+
92+
/**
93+
* Build the class with the given name.
94+
*
95+
* @param string $name
96+
*
97+
* @return string
98+
*
99+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
100+
*/
101+
protected function buildClass($name): string
102+
{
103+
/** @var string $identity */
104+
$identity = $this->option('identity') ?? 'id';
105+
106+
return str_replace(
107+
['{{ identityCamel }}', '{{ identity }}'],
108+
[$identity, Str::camel($identity)],
109+
parent::buildClass($name)
110+
);
111+
}
112+
}

0 commit comments

Comments
 (0)