Skip to content
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Packages\Features\CommandUseCases\UseCase\User;

use App\Packages\Domains\Interface\UserRepositroyInterface;
use App\Packages\Domains\User\Factory\UserEntityFactory;
use App\Packages\Features\CommandUseCases\UseCommand\User\UpdateUserCommand;
use App\Packages\Features\QueryUseCases\Dto\User\UserDto;

final class UpdateUserUseCase
{
public function __construct(
private readonly UserRepositroyInterface $userRepository,
) {}

public function handle(UpdateUserCommand $command): UserDto
{
$entity = UserEntityFactory::build([
'id' => $command->id,
'first_name' => $command->firstName,
'last_name' => $command->lastName,
'email' => $command->email,
'age_range' => $command->ageRange,
'subscription_tier' => $command->subscriptionTier,
'subscription_expires_at' => $command->subscriptionExpiresAt,
]);

return $this->userRepository->updateUser($entity);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace App\Packages\Features\CommandUseCases\UseCommand\User;

use InvalidArgumentException;

final class UpdateUserCommand
{
private const REQUIRED_KEYS = [
'id',
'first_name',
'last_name',
'email',
'age_range',
'subscription_tier',
];

private function __construct(
public readonly int $id,
public readonly string $firstName,
public readonly string $lastName,
public readonly string $email,
public readonly string $ageRange,
public readonly string $subscriptionTier,
public readonly ?string $subscriptionExpiresAt,
) {}

public static function fromArray(array $data): self
{
foreach (self::REQUIRED_KEYS as $key) {
if (!array_key_exists($key, $data)) {
throw new InvalidArgumentException("Missing required key: {$key}");
}
}

return new self(
id: (int) $data['id'],
firstName: $data['first_name'],
lastName: $data['last_name'],
email: $data['email'],
ageRange: $data['age_range'],
subscriptionTier: $data['subscription_tier'],
subscriptionExpiresAt: $data['subscription_expires_at'] ?? null,
);
}
}
2 changes: 1 addition & 1 deletion src/app/Packages/Features/Controller/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace App\Packages\Features\Controller;

use App\Http\Controllers\Controller;
use App\Packages\Features\CommandUseCases\Factory\ViewModel\UserViewModelFactory;
use App\Packages\Features\QueryUseCases\Factory\ViewModel\UserViewModelFactory;
use App\Packages\Features\CommandUseCases\UseCase\User\CreateUserUseCase;
use App\Packages\Features\CommandUseCases\UseCommand\User\CreateUserCommand;
use App\Packages\Features\QueryUseCases\UseCase\User\GetUserByIdUseCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

namespace App\Packages\Features\CommandUseCases\Factory\ViewModel;
namespace App\Packages\Features\QueryUseCases\Factory\ViewModel;

use App\Packages\Features\CommandUseCases\ViewModel\User\UserViewModel;
use App\Packages\Features\QueryUseCases\ViewModel\User\UserViewModel;
use App\Packages\Features\QueryUseCases\Dto\User\UserDto;

class UserViewModelFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php

namespace App\Packages\Features\Tests\CommandUseCases;
namespace App\Packages\Features\QueryUseCases\Tests\ViewModel;

use App\Packages\Features\CommandUseCases\Factory\ViewModel\UserViewModelFactory;
use App\Packages\Features\CommandUseCases\ViewModel\User\UserViewModel;
use App\Packages\Features\QueryUseCases\Factory\ViewModel\UserViewModelFactory;
use App\Packages\Features\QueryUseCases\ViewModel\User\UserViewModel;
use App\Packages\Features\QueryUseCases\Dto\User\UserDto;
use Faker\Factory as FakerFactory;
use PHPUnit\Framework\TestCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

namespace App\Packages\Features\Tests\CommandUseCases;
namespace App\Packages\Features\QueryUseCases\Tests\ViewModel;

use App\Packages\Features\CommandUseCases\ViewModel\User\UserViewModel;
use App\Packages\Features\QueryUseCases\ViewModel\User\UserViewModel;
use App\Packages\Features\QueryUseCases\Dto\User\UserDto;
use Faker\Factory as FakerFactory;
use PHPUnit\Framework\TestCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace App\Packages\Features\CommandUseCases\ViewModel\User;
namespace App\Packages\Features\QueryUseCases\ViewModel\User;

use App\Packages\Features\QueryUseCases\Dto\User\UserDto;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace App\Packages\Features\Tests\CommandUseCases;

use App\Packages\Features\CommandUseCases\UseCommand\User\UpdateUserCommand;
use Faker\Factory as FakerFactory;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;

class UpdateUserCommandTest extends TestCase
{
private function validData(array $overrides = []): array
{
$faker = FakerFactory::create();

return array_merge([
'id' => $faker->unique()->randomNumber(5),
'first_name' => $faker->firstName(),
'last_name' => $faker->lastName(),
'email' => $faker->unique()->safeEmail(),
'age_range' => $faker->randomElement(['teens', '20s', '30s', '40s', '50s', '60plus']),
'subscription_tier' => $faker->randomElement(['free', 'premium']),
], $overrides);
}

public function test_fromArray_creates_command_with_valid_data(): void
{
$data = $this->validData();
$command = UpdateUserCommand::fromArray($data);

$this->assertSame((int) $data['id'], $command->id);
$this->assertSame($data['first_name'], $command->firstName);
$this->assertSame($data['last_name'], $command->lastName);
$this->assertSame($data['email'], $command->email);
$this->assertSame($data['age_range'], $command->ageRange);
$this->assertSame($data['subscription_tier'], $command->subscriptionTier);
$this->assertNull($command->subscriptionExpiresAt);
}

public function test_fromArray_sets_subscription_expires_at_when_provided(): void
{
$command = UpdateUserCommand::fromArray($this->validData([
'subscription_expires_at' => '2027-01-01 00:00:00',
]));

$this->assertSame('2027-01-01 00:00:00', $command->subscriptionExpiresAt);
}

#[\PHPUnit\Framework\Attributes\DataProvider('missingKeyProvider')]
public function test_fromArray_throws_when_required_key_is_missing(string $missingKey): void
{
$data = $this->validData();
unset($data[$missingKey]);

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("Missing required key: {$missingKey}");

UpdateUserCommand::fromArray($data);
}

public static function missingKeyProvider(): array
{
return [
['id'],
['first_name'],
['last_name'],
['email'],
['age_range'],
['subscription_tier'],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace App\Packages\Features\Tests\CommandUseCases;

use App\Packages\Domains\Interface\UserRepositroyInterface;
use App\Packages\Domains\User\UserEntity;
use App\Packages\Features\CommandUseCases\UseCase\User\UpdateUserUseCase;
use App\Packages\Features\CommandUseCases\UseCommand\User\UpdateUserCommand;
use App\Packages\Features\QueryUseCases\Dto\User\UserDto;
use Faker\Factory as FakerFactory;
use Mockery;
use Mockery\MockInterface;
use PHPUnit\Framework\TestCase;

class UpdateUserUseCaseTest extends TestCase
{
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}

private function buildCommand(): UpdateUserCommand
{
$faker = FakerFactory::create();

return UpdateUserCommand::fromArray([
'id' => $faker->unique()->randomNumber(5),
'first_name' => $faker->firstName(),
'last_name' => $faker->lastName(),
'email' => $faker->unique()->safeEmail(),
'age_range' => $faker->randomElement(['teens', '20s', '30s', '40s', '50s', '60plus']),
'subscription_tier' => 'free',
]);
}

private function buildDto(): UserDto
{
$faker = FakerFactory::create();

return new UserDto(
id: $faker->unique()->randomNumber(5),
firstName: $faker->firstName(),
lastName: $faker->lastName(),
email: $faker->unique()->safeEmail(),
ageRange: 'teens',
subscriptionTier: 'free',
subscriptionExpiresAt: null,
);
}

public function test_handle_passes_entity_to_repository_and_returns_dto(): void
{
$command = $this->buildCommand();
$dto = $this->buildDto();

/** @var UserRepositroyInterface|MockInterface $repository */
$repository = Mockery::mock(UserRepositroyInterface::class);
$repository->shouldReceive('updateUser')
->once()
->with(Mockery::type(UserEntity::class))
->andReturn($dto);

$result = (new UpdateUserUseCase($repository))->handle($command);

$this->assertSame($dto, $result);
}
}
Loading