Skip to content
This repository was archived by the owner on Dec 11, 2023. It is now read-only.

Commit 35bcfcb

Browse files
authored
Rename course use case (#14)
* Add course finder * Rename course
1 parent 24a44a3 commit 35bcfcb

File tree

8 files changed

+200
-0
lines changed

8 files changed

+200
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Mooc\Courses\Application\Find;
6+
7+
use CodelyTv\Mooc\Courses\Domain\Course;
8+
use CodelyTv\Mooc\Courses\Domain\CourseNotExist;
9+
use CodelyTv\Mooc\Courses\Domain\CourseRepository;
10+
use CodelyTv\Mooc\Shared\Domain\Course\CourseId;
11+
12+
final class CourseFinder
13+
{
14+
private $repository;
15+
16+
public function __construct(CourseRepository $repository)
17+
{
18+
$this->repository = $repository;
19+
}
20+
21+
public function __invoke(CourseId $id): Course
22+
{
23+
$course = $this->repository->search($id);
24+
25+
if (null === $course) {
26+
throw new CourseNotExist($id);
27+
}
28+
29+
return $course;
30+
}
31+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Mooc\Courses\Application\Update;
6+
7+
use CodelyTv\Mooc\Courses\Application\Find\CourseFinder;
8+
use CodelyTv\Mooc\Courses\Domain\CourseName;
9+
use CodelyTv\Mooc\Courses\Domain\CourseRepository;
10+
use CodelyTv\Mooc\Shared\Domain\Course\CourseId;
11+
use CodelyTv\Shared\Domain\Bus\Event\EventBus;
12+
13+
final class CourseRenamer
14+
{
15+
private $repository;
16+
private $finder;
17+
private $bus;
18+
19+
public function __construct(CourseRepository $repository, EventBus $bus)
20+
{
21+
$this->repository = $repository;
22+
$this->finder = new CourseFinder($repository);
23+
$this->bus = $bus;
24+
}
25+
26+
public function __invoke(CourseId $id, CourseName $newName): void
27+
{
28+
$course = $this->finder->__invoke($id);
29+
30+
$course->rename($newName);
31+
32+
$this->repository->save($course);
33+
$this->bus->publish(...$course->pullDomainEvents());
34+
}
35+
}

src/Mooc/Courses/Domain/Course.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@ public function duration(): CourseDuration
4343
{
4444
return $this->duration;
4545
}
46+
47+
public function rename(CourseName $newName): void
48+
{
49+
$this->name = $newName;
50+
}
4651
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Mooc\Courses\Domain;
6+
7+
use CodelyTv\Mooc\Shared\Domain\Course\CourseId;
8+
use CodelyTv\Shared\Domain\DomainError;
9+
10+
final class CourseNotExist extends DomainError
11+
{
12+
private $id;
13+
14+
public function __construct(CourseId $id)
15+
{
16+
$this->id = $id;
17+
18+
parent::__construct();
19+
}
20+
21+
public function errorCode(): string
22+
{
23+
return 'course_not_exist';
24+
}
25+
26+
protected function errorMessage(): string
27+
{
28+
return sprintf('The course <%s> does not exist', $this->id->value());
29+
}
30+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Mooc\Courses\Application\Update;
6+
7+
use CodelyTv\Mooc\Courses\Application\Update\CourseRenamer;
8+
use CodelyTv\Mooc\Courses\Domain\CourseNotExist;
9+
use CodelyTv\Tests\Mooc\Courses\CoursesModuleUnitTestCase;
10+
use CodelyTv\Tests\Mooc\Courses\Domain\CourseIdMother;
11+
use CodelyTv\Tests\Mooc\Courses\Domain\CourseMother;
12+
use CodelyTv\Tests\Mooc\Courses\Domain\CourseNameMother;
13+
use CodelyTv\Tests\Shared\Domain\DuplicatorMother;
14+
15+
final class CourseRenamerTest extends CoursesModuleUnitTestCase
16+
{
17+
private $renamer;
18+
19+
protected function setUp(): void
20+
{
21+
parent::setUp();
22+
23+
$this->renamer = new CourseRenamer($this->repository(), $this->eventBus());
24+
}
25+
26+
/** @test */
27+
public function it_should_rename_an_existing_course(): void
28+
{
29+
$course = CourseMother::random();
30+
$newName = CourseNameMother::random();
31+
$renamedCourse = DuplicatorMother::with($course, ['name' => $newName]);
32+
33+
$this->shouldSearch($course->id(), $course);
34+
$this->shouldSave($renamedCourse);
35+
$this->shouldNotPublishDomainEvent();
36+
37+
$this->renamer->__invoke($course->id(), $newName);
38+
}
39+
40+
/** @test */
41+
public function it_should_throw_an_exception_when_the_course_not_exist(): void
42+
{
43+
$this->expectException(CourseNotExist::class);
44+
45+
$id = CourseIdMother::random();
46+
47+
$this->shouldSearch($id, null);
48+
49+
$this->renamer->__invoke($id, CourseNameMother::random());
50+
}
51+
}

tests/src/Mooc/Courses/CoursesModuleUnitTestCase.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use CodelyTv\Mooc\Courses\Domain\Course;
88
use CodelyTv\Mooc\Courses\Domain\CourseRepository;
9+
use CodelyTv\Mooc\Shared\Domain\Course\CourseId;
910
use CodelyTv\Tests\Shared\Infrastructure\PhpUnit\UnitTestCase;
1011
use Mockery\MockInterface;
1112

@@ -22,6 +23,15 @@ protected function shouldSave(Course $course): void
2223
->andReturnNull();
2324
}
2425

26+
protected function shouldSearch(CourseId $id, ?Course $course): void
27+
{
28+
$this->repository()
29+
->shouldReceive('search')
30+
->with($this->similarTo($id))
31+
->once()
32+
->andReturn($course);
33+
}
34+
2535
/** @return CourseRepository|MockInterface */
2636
protected function repository(): MockInterface
2737
{
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Shared\Domain;
6+
7+
use ReflectionObject;
8+
use ReflectionProperty;
9+
use function Lambdish\Phunctional\each;
10+
11+
final class DuplicatorMother
12+
{
13+
public static function with($object, array $newParams)
14+
{
15+
$duplicated = clone $object;
16+
$reflection = new ReflectionObject($duplicated);
17+
18+
each(
19+
static function (ReflectionProperty $property) use ($duplicated, $newParams) {
20+
if (isset($newParams[$property->getName()])) {
21+
$property->setAccessible(true);
22+
$property->setValue($duplicated, $newParams[$property->getName()]);
23+
}
24+
},
25+
$reflection->getProperties()
26+
);
27+
28+
return $duplicated;
29+
}
30+
}

tests/src/Shared/Infrastructure/PhpUnit/UnitTestCase.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ protected function shouldPublishDomainEvent(DomainEvent $domainEvent): void
3434
->andReturnNull();
3535
}
3636

37+
protected function shouldNotPublishDomainEvent(): void
38+
{
39+
$this->eventBus()
40+
->shouldReceive('publish')
41+
->withNoArgs()
42+
->andReturnNull();
43+
}
44+
3745
/** @return EventBus|MockInterface */
3846
protected function eventBus(): MockInterface
3947
{

0 commit comments

Comments
 (0)