Skip to content

Commit bdfbfc1

Browse files
committed
Merge branch 'release/5.0.0-rc.2'
2 parents d10c56d + e71f3d6 commit bdfbfc1

25 files changed

+577
-73
lines changed

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file. This projec
55

66
## Unreleased
77

8+
## [5.0.0-rc.2] - 2025-10-14
9+
10+
### Added
11+
12+
- Added a static `tryFrom()` method to the `IntegerId`, `StringId` and `Uuid` identifier classes. This method
13+
returns `null` if the value provided cannot be cast to the identifier type.
14+
- The static `from()` method on all the identifier classes now accepts `null` but throws in this scenario. This allows
15+
it to be used where the value you are casting is possibly null.
16+
- New methods on the `ListOfErrors` interface:
17+
- `find()` to find the first matching error.
18+
- `sole()` to get the first error, but only if exactly one error exists.
19+
- `any()` to determine if any of the errors match.
20+
- `every()` to determine if every error matches.
21+
- `filter()` to get a new list containing only matching errors.
22+
- The `FailedResultException` now has a message when the result has an error code but no error message.
23+
- The `FakeExceptionReporter` now has `none()` and `expect()` helper methods, to prevent exceptions from being swallowed
24+
in tests.
25+
- **BREAKING**: Changes to the result interface to support the following features. Although technically breaking, this
26+
will not affect the majority of implementations are the concrete result class provided by this package has been
27+
updated.
28+
- Can now pass a default value to the `Result::error()` method. This default value can either be a string or a
29+
closure that receives the error code. (Errors always have codes if they do not have messages.)
30+
- Added `Result::code()` method to get the first error code in the result's errors.
31+
32+
### Deprecated
33+
34+
- The `ListOfErrors::contains()` method is deprecated and will be removed in 6.0. Use the new `any()` method instead.
35+
- Calling the `ListOfErrors::first()` method with arguments is deprecated and will be removed in 6.0. Use the new
36+
`find()` method instead.
37+
838
## [5.0.0-rc.1] - 2025-10-09
939

1040
### Added
@@ -500,6 +530,8 @@ All notable changes to this project will be documented in this file. This projec
500530

501531
Initial release.
502532

533+
[5.0.0-rc.2]: https://github.com/cloudcreativity/ddd-modules/compare/v5.0.0-rc.1...v5.0.0-rc.2
534+
503535
[5.0.0-rc.1]: https://github.com/cloudcreativity/ddd-modules/compare/v4.1.0...v5.0.0-rc.1
504536

505537
[4.1.0]: https://github.com/cloudcreativity/ddd-modules/compare/v4.0.0...v4.1.0

deptrac.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,18 @@ deptrac:
3838
value: ^Deprecated$
3939
ruleset:
4040
Toolkit:
41+
- Attributes
4142
Domain:
4243
- Toolkit
44+
- Attributes
4345
Application:
4446
- Toolkit
4547
- Domain
4648
- PSR Log
49+
- Attributes
4750
Infrastructure:
4851
- Toolkit
4952
- Domain
5053
- Application
5154
- PSR Log
55+
- Attributes

phpunit.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
cacheDirectory=".phpunit.cache"
1212
backupStaticProperties="false"
1313
failOnWarning="true"
14-
failOnDeprecation="true"
14+
failOnDeprecation="false"
1515
failOnNotice="true"
1616
>
1717
<coverage/>

src/Contracts/Toolkit/Loggable/ContextFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface ContextFactory
2121
* Make log context for the provided object.
2222
*
2323
* @param Message|Result<mixed> $object
24-
* @return array<array-key, mixed>
24+
* @return array<string, mixed>
2525
*/
2626
public function make(Message|Result $object): array;
2727
}

src/Contracts/Toolkit/Result/ListOfErrors.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,51 @@ interface ListOfErrors extends ListIterator
2424
/**
2525
* Get the first error in the list, or the first matching error.
2626
*
27-
* @param Closure(Error): bool|UnitEnum|null $matcher
27+
* @param (Closure(Error): bool)|UnitEnum|null $matcher
2828
*/
2929
public function first(Closure|UnitEnum|null $matcher = null): ?Error;
3030

31+
/**
32+
* Find the first matching error in the list.
33+
*
34+
* @param (Closure(Error): bool)|UnitEnum $matcher
35+
*/
36+
public function find(Closure|UnitEnum $matcher): ?Error;
37+
38+
/**
39+
* Get the first error in the list, but only if exactly one error exists. Otherwise, throw an exception.
40+
*
41+
* @param (Closure(Error): bool)|UnitEnum|null $matcher
42+
*/
43+
public function sole(Closure|UnitEnum|null $matcher = null): Error;
44+
3145
/**
3246
* Does the list contain a matching error?
3347
*
34-
* @param Closure(Error): bool|UnitEnum $matcher
48+
* @param (Closure(Error): bool)|UnitEnum $matcher
49+
* @deprecated 6.0 use any() instead.
3550
*/
3651
public function contains(Closure|UnitEnum $matcher): bool;
3752

53+
/**
54+
* Does the list contain at least one matching error?
55+
*
56+
* @param (Closure(Error): bool)|UnitEnum $matcher
57+
*/
58+
public function any(Closure|UnitEnum $matcher): bool;
59+
60+
/**
61+
* Do all errors in the list match the provided matcher?
62+
*
63+
* @param (Closure(Error): bool)|UnitEnum $matcher
64+
*/
65+
public function every(Closure|UnitEnum $matcher): bool;
66+
67+
/**
68+
* @param (Closure(Error): bool)|UnitEnum $matcher
69+
*/
70+
public function filter(Closure|UnitEnum $matcher): self;
71+
3872
/**
3973
* Get all the unique error codes in the list.
4074
*

src/Contracts/Toolkit/Result/Result.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,25 @@
1212

1313
namespace CloudCreativity\Modules\Contracts\Toolkit\Result;
1414

15+
use Closure;
1516
use CloudCreativity\Modules\Contracts\Toolkit\Result\ListOfErrors as IListOfErrors;
1617
use CloudCreativity\Modules\Toolkit\Result\FailedResultException;
1718
use CloudCreativity\Modules\Toolkit\Result\Meta;
19+
use UnitEnum;
1820

1921
/**
2022
* @template-covariant TValue
2123
*/
2224
interface Result
2325
{
26+
/**
27+
* Is the result a success?
28+
*/
2429
public function didSucceed(): bool;
2530

31+
/**
32+
* Is the result a failure?
33+
*/
2634
public function didFail(): bool;
2735

2836
/**
@@ -53,9 +61,17 @@ public function safe(): mixed;
5361
public function errors(): IListOfErrors;
5462

5563
/**
56-
* Get an error message string.
64+
* Get the first error message, if there is one.
65+
*
66+
* @param (Closure(UnitEnum): non-empty-string)|non-empty-string|null $default the default value to use if there is no error message.
67+
* @return ($default is Closure|string ? non-empty-string : null)
68+
*/
69+
public function error(Closure|string|null $default = null): ?string;
70+
71+
/**
72+
* Get the first error code, if there is one.
5773
*/
58-
public function error(): ?string;
74+
public function code(): ?UnitEnum;
5975

6076
/**
6177
* Get the result meta.

src/Testing/FakeDomainEventDispatcher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function count(): int
3535
}
3636

3737
/**
38-
* Expect a single event to be dispatched and return it.
38+
* Get the first event in the list, but only if exactly one event exists. Otherwise, throw an exception.
3939
*/
4040
public function sole(): DomainEvent
4141
{

src/Testing/FakeExceptionReporter.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,46 @@ final class FakeExceptionReporter implements ExceptionReporter, Countable
2424
*/
2525
public array $reported = [];
2626

27+
private ?int $expected = null;
28+
2729
public function report(Throwable $ex): void
2830
{
31+
if ($this->expected === count($this->reported)) {
32+
throw $ex;
33+
}
34+
2935
$this->reported[] = $ex;
3036
}
3137

38+
/**
39+
* Expect no exceptions to be reported.
40+
*
41+
* Useful in tests where you do not expect any exceptions to be reported.
42+
* This will cause the fake reporter to re-throw any exceptions rather
43+
* than swallowing them.
44+
*/
45+
public function none(): void
46+
{
47+
$this->expect(0);
48+
}
49+
50+
/**
51+
* Set the amount of exceptions expected.
52+
*
53+
* This will cause the fake reporter to re-throw any exceptions
54+
* once the expected amount has been reported. This is primarily
55+
* useful for debugging, because if your test causes too many
56+
* exceptions, you will see the first unexpected exception.
57+
*
58+
* Note that this does not help if the reporter receives less
59+
* exceptions than expected. You should use assertions
60+
* in your test to verify the count of reported exceptions.
61+
*/
62+
public function expect(int $count): void
63+
{
64+
$this->expected = $count;
65+
}
66+
3267
public function count(): int
3368
{
3469
return count($this->reported);

src/Toolkit/Contracts.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class Contracts
1919
/**
2020
* Assert that the provided precondition is true.
2121
*
22-
* @param Closure(): string|string $message
22+
* @param (Closure(): string)|string $message
2323
* @phpstan-assert true $precondition
2424
*/
2525
public static function assert(bool $precondition, Closure|string $message = ''): void

src/Toolkit/Identifiers/Guid.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
{
2626
use IsIdentifier;
2727

28-
public static function from(Identifier $value): self
28+
public static function from(?Identifier $value): self
2929
{
3030
if ($value instanceof self) {
3131
return $value;
@@ -34,6 +34,15 @@ public static function from(Identifier $value): self
3434
throw new ContractException('Unexpected identifier type, received: ' . get_debug_type($value));
3535
}
3636

37+
public static function tryFrom(?Identifier $value): ?self
38+
{
39+
if ($value instanceof self) {
40+
return $value;
41+
}
42+
43+
return null;
44+
}
45+
3746
/**
3847
* Create a GUID with an integer id.
3948
*/

0 commit comments

Comments
 (0)