Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
- Bug #256: Fix incorrect .env files used in Docker Compose for production (@aa-chernyh)
- Enh #258: Set locale `C.UTF-8` in `Dockerfile` (@vjik)
- Bug #260: Fix psalm cache directory in configuration file (@vjik)
- Enh #260: Update composer dependencies (@vjik)
- Enh #260, #265: Update composer dependencies and refactor to replace use of deprecated classes (@vjik)
- Chg #265: Refactor `PresenterInterface` and implementations for preparing data only (@vjik)

## 1.1.0 December 22, 2025

Expand Down
16 changes: 8 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"yiisoft/aliases": "^3.1.1",
"yiisoft/config": "^1.6.2",
"yiisoft/data": "^1.0.1",
"yiisoft/data-response": "^2.1.2",
"yiisoft/data-response": "^2.2",
"yiisoft/definitions": "^3.4.1",
"yiisoft/di": "^1.4.1",
"yiisoft/error-handler": "^4.3.2",
Expand All @@ -54,7 +54,7 @@
"yiisoft/log-target-file": "^3.1",
"yiisoft/middleware-dispatcher": "^5.4",
"yiisoft/request-body-parser": "^1.2.1",
"yiisoft/request-provider": "^1.2",
"yiisoft/request-provider": "^1.3",
"yiisoft/router": "^4.0.2",
"yiisoft/router-fastroute": "^4.0.3",
"yiisoft/validator": "^2.5.1",
Expand All @@ -65,19 +65,19 @@
},
"require-dev": {
"codeception/c3": "^2.9",
"codeception/codeception": "^5.3.4",
"codeception/lib-innerbrowser": "^4.0.8",
"codeception/codeception": "^5.3.5",
"codeception/lib-innerbrowser": "^4.1.0",
"codeception/module-asserts": "^3.3.0",
"codeception/module-cli": "^2.0.1",
"codeception/module-db": "^3.2.2",
"codeception/module-phpbrowser": "^3.0.2",
"codeception/module-rest": "^3.4.3",
"friendsofphp/php-cs-fixer": "^3.93.0",
"phpunit/phpunit": "^11.5.49",
"rector/rector": "^2.3.4",
"friendsofphp/php-cs-fixer": "^3.94.2",
"phpunit/phpunit": "^11.5.55",
"rector/rector": "^2.3.8",
"roave/infection-static-analysis-plugin": "^1.40",
"shipmonk/composer-dependency-analyser": "^1.8.4",
"vimeo/psalm": "^6.14.3"
"vimeo/psalm": "^6.15.1"
},
"autoload": {
"psr-4": {
Expand Down
19 changes: 10 additions & 9 deletions config/web/di/application.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

use App\Api\Shared\ExceptionResponderFactory;
use App\Api\Shared\NotFoundMiddleware;
use Yiisoft\DataResponse\Formatter\JsonDataResponseFormatter;
use Yiisoft\DataResponse\Formatter\XmlDataResponseFormatter;
use Yiisoft\DataResponse\Middleware\ContentNegotiator;
use Yiisoft\DataResponse\Middleware\FormatDataResponseAsJson;
use Yiisoft\DataResponse\Formatter\JsonFormatter;
use Yiisoft\DataResponse\Formatter\XmlFormatter;
use Yiisoft\DataResponse\Middleware\ContentNegotiatorDataResponseMiddleware;
use Yiisoft\Definitions\DynamicReference;
use Yiisoft\Definitions\Reference;
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
Expand All @@ -29,11 +28,13 @@
'class' => MiddlewareDispatcher::class,
'withMiddlewares()' => [
[
FormatDataResponseAsJson::class,
static fn() => new ContentNegotiator([
'application/xml' => new XmlDataResponseFormatter(),
'application/json' => new JsonDataResponseFormatter(),
]),
static fn() => new ContentNegotiatorDataResponseMiddleware(
formatters: [
'application/xml' => new XmlFormatter(),
'application/json' => new JsonFormatter(),
],
fallback: new JsonFormatter(),
),
ErrorCatcher::class,
static fn(ExceptionResponderFactory $factory) => $factory->create(),
RequestBodyParser::class,
Expand Down
6 changes: 2 additions & 4 deletions src/Api/Shared/Presenter/AsIsPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@

namespace App\Api\Shared\Presenter;

use Yiisoft\DataResponse\DataResponse;

/**
* @implements PresenterInterface<mixed>
*/
final readonly class AsIsPresenter implements PresenterInterface
{
public function present(mixed $value, DataResponse $response): DataResponse
public function present(mixed $value): mixed
{
return $response->withData($value);
return $value;
}
}
9 changes: 3 additions & 6 deletions src/Api/Shared/Presenter/CollectionPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace App\Api\Shared\Presenter;

use Yiisoft\DataResponse\DataResponse;

/**
* @implements PresenterInterface<iterable>
*/
Expand All @@ -15,13 +13,12 @@ public function __construct(
private PresenterInterface $itemPresenter = new AsIsPresenter(),
) {}

public function present(mixed $value, DataResponse $response): DataResponse
public function present(mixed $value): array
{
$result = [];
foreach ($value as $item) {
$response = $this->itemPresenter->present($item, $response);
$result[] = $response->getData();
$result[] = $this->itemPresenter->present($item);
}
return $response->withData($result);
return $result;
}
}
11 changes: 3 additions & 8 deletions src/Api/Shared/Presenter/FailPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

namespace App\Api\Shared\Presenter;

use Yiisoft\DataResponse\DataResponse;
use Yiisoft\Http\Status;

/**
* @implements PresenterInterface<mixed>
*/
Expand All @@ -15,13 +12,11 @@
public function __construct(
private string $message = 'Unknown error.',
private int|null $code = null,
private int $httpCode = Status::BAD_REQUEST,
private PresenterInterface $presenter = new AsIsPresenter(),
) {}

public function present(mixed $value, DataResponse $response): DataResponse
public function present(mixed $value): mixed
{
$response = $this->presenter->present($value, $response);
$result = [
'status' => 'failed',
'error_message' => $this->message,
Expand All @@ -30,8 +25,8 @@ public function present(mixed $value, DataResponse $response): DataResponse
$result['error_code'] = $this->code;
}
if ($value !== null) {
$result['error_data'] = $response->getData();
$result['error_data'] = $this->presenter->present($value);
}
return $response->withData($result)->withStatus($this->httpCode);
return $result;
}
}
10 changes: 4 additions & 6 deletions src/Api/Shared/Presenter/OffsetPaginatorPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace App\Api\Shared\Presenter;

use Yiisoft\Data\Paginator\OffsetPaginator;
use Yiisoft\DataResponse\DataResponse;

/**
* @implements PresenterInterface<OffsetPaginator>
Expand All @@ -20,14 +19,13 @@ public function __construct(
$this->collectionPresenter = new CollectionPresenter($itemPresenter);
}

public function present(mixed $value, DataResponse $response): DataResponse
public function present(mixed $value): array
{
$collectionResponse = $this->collectionPresenter->present($value->read(), $response);
return $collectionResponse->withData([
'items' => $collectionResponse->getData(),
return [
'items' => $this->collectionPresenter->present($value->read()),
'pageSize' => $value->getPageSize(),
'currentPage' => $value->getCurrentPage(),
'totalPages' => $value->getTotalPages(),
]);
];
}
}
4 changes: 1 addition & 3 deletions src/Api/Shared/Presenter/PresenterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace App\Api\Shared\Presenter;

use Yiisoft\DataResponse\DataResponse;

/**
* @template T
*/
Expand All @@ -14,5 +12,5 @@ interface PresenterInterface
/**
* @param T $value
*/
public function present(mixed $value, DataResponse $response): DataResponse;
public function present(mixed $value): mixed;
}
16 changes: 5 additions & 11 deletions src/Api/Shared/Presenter/SuccessPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

namespace App\Api\Shared\Presenter;

use Yiisoft\DataResponse\DataResponse;
use Yiisoft\Http\Status;

/**
* @implements PresenterInterface<mixed>
*/
Expand All @@ -16,14 +13,11 @@ public function __construct(
private PresenterInterface $presenter = new AsIsPresenter(),
) {}

public function present(mixed $value, DataResponse $response): DataResponse
public function present(mixed $value): array
{
$response = $this->presenter->present($value, $response);
return $response
->withData([
'status' => 'success',
'data' => $response->getData(),
])
->withStatus(Status::OK);
return [
'status' => 'success',
'data' => $this->presenter->present($value),
];
}
}
7 changes: 2 additions & 5 deletions src/Api/Shared/Presenter/ValidationResultPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@

namespace App\Api\Shared\Presenter;

use Yiisoft\DataResponse\DataResponse;
use Yiisoft\Validator\Result;

/**
* @implements PresenterInterface<Result>
*/
final readonly class ValidationResultPresenter implements PresenterInterface
{
public function present(mixed $value, DataResponse $response): DataResponse
public function present(mixed $value): array
{
return $response->withData(
$value->getErrorMessagesIndexedByPath(),
);
return $value->getErrorMessagesIndexedByPath();
}
}
14 changes: 9 additions & 5 deletions src/Api/Shared/ResponseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use App\Api\Shared\Presenter\SuccessPresenter;
use App\Api\Shared\Presenter\ValidationResultPresenter;
use Psr\Http\Message\ResponseInterface;
use Yiisoft\DataResponse\DataResponseFactoryInterface;
use Yiisoft\DataResponse\ResponseFactory\DataResponseFactoryInterface;
use Yiisoft\Http\Status;
use Yiisoft\Validator\Result;

Expand All @@ -24,8 +24,9 @@ public function success(
array|object|null $data = null,
PresenterInterface $presenter = new AsIsPresenter(),
): ResponseInterface {
return (new SuccessPresenter($presenter))
->present($data, $this->dataResponseFactory->createResponse());
return $this->dataResponseFactory->createResponse(
(new SuccessPresenter($presenter))->present($data),
);
}

public function fail(
Expand All @@ -35,8 +36,11 @@ public function fail(
int $httpCode = Status::BAD_REQUEST,
PresenterInterface $presenter = new AsIsPresenter(),
): ResponseInterface {
return (new FailPresenter($message, $code, $httpCode, $presenter))
->present($data, $this->dataResponseFactory->createResponse());
return $this->dataResponseFactory
->createResponse(
(new FailPresenter($message, $code, $presenter))->present($data),
)
->withStatus($httpCode);
}

public function notFound(string $message = 'Not found.'): ResponseInterface
Expand Down
16 changes: 8 additions & 8 deletions tests/Unit/Api/Shared/ExceptionResponderFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
use Codeception\Test\Unit;
use HttpSoft\Message\ResponseFactory as PsrResponseFactory;
use HttpSoft\Message\ServerRequest;
use HttpSoft\Message\StreamFactory;
use LogicException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Yiisoft\DataResponse\DataResponse;
use Yiisoft\DataResponse\DataResponseFactory;
use Yiisoft\DataResponse\DataStream\DataStream;
use Yiisoft\DataResponse\ResponseFactory\DataResponseFactory;
use Yiisoft\Di\Container;
use Yiisoft\ErrorHandler\Exception\UserException;
use Yiisoft\ErrorHandler\Middleware\ExceptionResponder;
Expand All @@ -38,8 +37,9 @@ public function handle(ServerRequestInterface $request): ResponseInterface
};

$response = $this->createExceptionResponder()->process($request, $handler);
$body = $response->getBody();

$this->assertInstanceOf(DataResponse::class, $response);
$this->assertInstanceOf(DataStream::class, $body);
$this->assertSame(
[
'status' => 'failed',
Expand All @@ -48,7 +48,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface
'name' => ['error1'],
],
],
$response->getData(),
$body->getData(),
);
}

Expand All @@ -63,15 +63,16 @@ public function handle(ServerRequestInterface $request): ResponseInterface
};

$response = $this->createExceptionResponder()->process($request, $handler);
$body = $response->getBody();

$this->assertInstanceOf(DataResponse::class, $response);
$this->assertInstanceOf(DataStream::class, $body);
$this->assertSame(
[
'status' => 'failed',
'error_message' => 'Hello, Exception!',
'error_code' => 0,
],
$response->getData(),
$body->getData(),
);
}

Expand All @@ -97,7 +98,6 @@ private function createExceptionResponder(): ExceptionResponder
new ResponseFactory(
new DataResponseFactory(
new PsrResponseFactory(),
new StreamFactory(),
),
),
new Injector(new Container()),
Expand Down
Loading