refactor(application): align UpdateBrand on FindBrand polymorphic output pattern
This commit is contained in:
parent
3951d7fc95
commit
c85379b50e
6 changed files with 73 additions and 37 deletions
|
|
@ -6,9 +6,9 @@ namespace Application\UseCases\Brand\Update;
|
|||
|
||||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
use Shared\DDD\UseCases\Input as UseCaseInput;
|
||||
use Shared\Models\ValueObjects\Email;
|
||||
use Shared\Models\ValueObjects\PicturePath;
|
||||
use Shared\Models\ValueObjects\Rcs;
|
||||
use Shared\ValueObjects\Email;
|
||||
use Domain\Models\ValueObjects\PicturePath;
|
||||
use Shared\ValueObjects\Rcs;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final readonly class Input implements UseCaseInput
|
||||
|
|
@ -21,8 +21,8 @@ final readonly class Input implements UseCaseInput
|
|||
) {
|
||||
Assert::true(
|
||||
$this->logo instanceof PicturePath
|
||||
|| $this->rcs instanceof Rcs
|
||||
|| $this->contact instanceof Email,
|
||||
|| $this->rcs instanceof Rcs
|
||||
|| $this->contact instanceof Email,
|
||||
'At least one field must be provided to update the brand.',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\UseCases\Brand\Update\Output;
|
||||
|
||||
final readonly class BrandNotFound implements Output
|
||||
{
|
||||
}
|
||||
|
|
@ -2,16 +2,15 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\UseCases\Brand\Update;
|
||||
namespace Application\UseCases\Brand\Update\Output;
|
||||
|
||||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
use Domain\Models\ValueObjects\Brand\BrandName;
|
||||
use Shared\DDD\UseCases\Output as UseCaseOutput;
|
||||
use Shared\Models\ValueObjects\Email;
|
||||
use Shared\Models\ValueObjects\PicturePath;
|
||||
use Shared\Models\ValueObjects\Rcs;
|
||||
use Domain\Models\ValueObjects\PicturePath;
|
||||
use Shared\ValueObjects\Email;
|
||||
use Shared\ValueObjects\Rcs;
|
||||
|
||||
final readonly class Output implements UseCaseOutput
|
||||
final readonly class BrandUpdated implements Output
|
||||
{
|
||||
public function __construct(
|
||||
public BrandName $name,
|
||||
|
|
@ -20,6 +19,7 @@ final readonly class Output implements UseCaseOutput
|
|||
public Rcs $rcs,
|
||||
public Email $contact,
|
||||
public \DateTimeInterface $createdAt,
|
||||
public ?\DateTimeInterface $updatedAt = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
11
src/Application/UseCases/Brand/Update/Output/Output.php
Normal file
11
src/Application/UseCases/Brand/Update/Output/Output.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Application\UseCases\Brand\Update\Output;
|
||||
|
||||
use Shared\DDD\UseCases\Output as UseCaseOutput;
|
||||
|
||||
interface Output extends UseCaseOutput
|
||||
{
|
||||
}
|
||||
|
|
@ -4,18 +4,21 @@ declare(strict_types=1);
|
|||
|
||||
namespace Application\UseCases\Brand\Update;
|
||||
|
||||
use Application\UseCases\Brand\Update\Output\BrandNotFound;
|
||||
use Application\UseCases\Brand\Update\Output\BrandUpdated;
|
||||
use Application\UseCases\Brand\Update\Output\Output;
|
||||
use Domain\Models\Entities\Brand;
|
||||
use Domain\Models\ValueObjects\PicturePath;
|
||||
use Domain\Ports\Repositories\BrandRepository;
|
||||
use Shared\DDD\Exceptions\NotFoundException;
|
||||
use Shared\DDD\UseCases\Input as BaseInput;
|
||||
use Shared\DDD\UseCases\Transactional;
|
||||
use Shared\DDD\UseCases\UseCase;
|
||||
use Shared\Models\ValueObjects\Email;
|
||||
use Shared\Models\ValueObjects\PicturePath;
|
||||
use Shared\Models\ValueObjects\Rcs;
|
||||
use Shared\ValueObjects\Email;
|
||||
use Shared\ValueObjects\Rcs;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/** @implements UseCase<\Application\UseCases\Brand\Update\Input, \Application\UseCases\Brand\Update\Output> */
|
||||
final readonly class UpdateBrand implements UseCase
|
||||
/** @implements UseCase<\Application\UseCases\Brand\Update\Input, \Application\UseCases\Brand\Update\Output\Output> */
|
||||
final readonly class UpdateBrand implements UseCase, Transactional
|
||||
{
|
||||
public function __construct(
|
||||
private BrandRepository $repository,
|
||||
|
|
@ -30,7 +33,7 @@ final readonly class UpdateBrand implements UseCase
|
|||
$brand = $this->repository->findByCode($input->code);
|
||||
|
||||
if (! $brand instanceof Brand) {
|
||||
throw new NotFoundException();
|
||||
return new BrandNotFound();
|
||||
}
|
||||
|
||||
if ($input->logo instanceof PicturePath) {
|
||||
|
|
@ -47,13 +50,14 @@ final readonly class UpdateBrand implements UseCase
|
|||
|
||||
$this->repository->save($brand);
|
||||
|
||||
return new Output(
|
||||
return new BrandUpdated(
|
||||
name: $brand->name,
|
||||
code: $brand->code,
|
||||
logo: $brand->logo,
|
||||
rcs: $brand->rcs,
|
||||
contact: $brand->contact,
|
||||
createdAt: $brand->createdAt(),
|
||||
updatedAt: $brand->updatedAt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Application\UseCases\Brand\Update;
|
||||
|
||||
use Application\UseCases\Brand\Update\Input;
|
||||
use Application\UseCases\Brand\Update\Output;
|
||||
use Application\UseCases\Brand\Update\Output\BrandNotFound;
|
||||
use Application\UseCases\Brand\Update\Output\BrandUpdated;
|
||||
use Application\UseCases\Brand\Update\Output\Output;
|
||||
use Application\UseCases\Brand\Update\UpdateBrand;
|
||||
use Domain\Models\Entities\Brand;
|
||||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
use Domain\Models\ValueObjects\Brand\BrandId;
|
||||
use Domain\Models\ValueObjects\Brand\BrandName;
|
||||
use Domain\Models\ValueObjects\PicturePath;
|
||||
use Domain\Ports\Repositories\BrandRepository;
|
||||
use Shared\DDD\Exceptions\NotFoundException;
|
||||
use Shared\DDD\UseCases\Input as BaseInput;
|
||||
use Shared\Models\ValueObjects\Email;
|
||||
use Shared\Models\ValueObjects\PicturePath;
|
||||
use Shared\Models\ValueObjects\Rcs;
|
||||
use Shared\ValueObjects\Email;
|
||||
use Shared\ValueObjects\Rcs;
|
||||
|
||||
covers(UpdateBrand::class);
|
||||
covers(UpdateBrand::class, Input::class, BrandUpdated::class, BrandNotFound::class);
|
||||
|
||||
$existingBrand = static fn (BrandCode $code): Brand => Brand::hydrate(
|
||||
BrandId::generate(),
|
||||
|
|
@ -46,7 +47,8 @@ describe('update brand use case', function () use ($existingBrand): void {
|
|||
&& $saved->rcs === $newRcs
|
||||
&& $saved->contact === $newContact
|
||||
&& $saved->code === $brand->code
|
||||
&& $saved->name === $brand->name,
|
||||
&& $saved->name === $brand->name
|
||||
&& $saved->updatedAt !== null,
|
||||
));
|
||||
|
||||
$useCase = new UpdateBrand($repository);
|
||||
|
|
@ -55,8 +57,9 @@ describe('update brand use case', function () use ($existingBrand): void {
|
|||
$result = $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($result)->toBeInstanceOf(Output::class)
|
||||
->and($result->code)->toBe($brand->code);
|
||||
expect($result)->toBeInstanceOf(BrandUpdated::class)
|
||||
->and($result->code)->toBe($brand->code)
|
||||
->and($result->updatedAt)->not->toBeNull();
|
||||
});
|
||||
|
||||
it('updates only the contact and leaves logo/rcs untouched', function () use ($existingBrand): void {
|
||||
|
|
@ -73,19 +76,24 @@ describe('update brand use case', function () use ($existingBrand): void {
|
|||
&& $saved->logo === $brand->logo
|
||||
&& $saved->rcs === $brand->rcs
|
||||
&& $saved->code === $brand->code
|
||||
&& $saved->name === $brand->name,
|
||||
&& $saved->name === $brand->name
|
||||
&& $saved->updatedAt !== null,
|
||||
));
|
||||
|
||||
$useCase = new UpdateBrand($repository);
|
||||
|
||||
// WHEN
|
||||
$useCase->execute($input);
|
||||
$result = $useCase->execute($input);
|
||||
|
||||
// THEN handled via the save() expectation above
|
||||
expect(true)->toBeTrue();
|
||||
// THEN
|
||||
expect($result)->toBeInstanceOf(BrandUpdated::class)
|
||||
->and($result->code)->toBe($brand->code)
|
||||
->and($result->updatedAt)->not->toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when the brand does not exist', function (): void {
|
||||
describe('update brand use case validation rules', function (): void {
|
||||
it('returns BrandNotFound when the brand does not exist', function (): void {
|
||||
// GIVEN
|
||||
$code = new BrandCode('AAA01');
|
||||
$input = new Input(code: $code, contact: new Email('contact-new@example.com'));
|
||||
|
|
@ -96,11 +104,15 @@ describe('update brand use case', function () use ($existingBrand): void {
|
|||
|
||||
$useCase = new UpdateBrand($repository);
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): Output => $useCase->execute($input))
|
||||
->toThrow(NotFoundException::class);
|
||||
});
|
||||
// WHEN
|
||||
$result = $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($result)->toBeInstanceOf(BrandNotFound::class);
|
||||
});
|
||||
});
|
||||
|
||||
describe('update brand use case assertions', function (): void {
|
||||
it('rejects an input of the wrong type', function (): void {
|
||||
// GIVEN
|
||||
$repository = \Mockery::mock(BrandRepository::class);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue