PHPackages                             hradigital/php-exceptions-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. hradigital/php-exceptions-laravel

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

hradigital/php-exceptions-laravel
=================================

Laravel wrapper for hradigital/php-exceptions, wiring Laravel's error handling and providing a JSON Renderer for these Exceptions.

1.0.0(1mo ago)00GPL-3.0-or-laterPHPPHP ^8.1CI passing

Since May 5Pushed 1mo agoCompare

[ Source](https://github.com/HRADigital/php-exceptions-laravel)[ Packagist](https://packagist.org/packages/hradigital/php-exceptions-laravel)[ Docs](https://github.com/HRADigital/php-exceptions-laravel)[ RSS](/packages/hradigital-php-exceptions-laravel/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (1)Dependencies (8)Versions (2)Used By (0)

php-exceptions-laravel
======================

[](#php-exceptions-laravel)

[![Latest Stable Version](https://camo.githubusercontent.com/b4af34ceac24c7c3ffbe0fbd0d0aaa84cb20a4e3eac94b2f745fb4c0169cd548/68747470733a2f2f706f7365722e707567782e6f72672f6872616469676974616c2f7068702d657863657074696f6e732d6c61726176656c2f762f737461626c65)](https://packagist.org/packages/hradigital/php-exceptions-laravel)[![Total Downloads](https://camo.githubusercontent.com/3173e04fcd529bcf59317404eebc943f867ff485450dad6247285ea6a518c20c/68747470733a2f2f706f7365722e707567782e6f72672f6872616469676974616c2f7068702d657863657074696f6e732d6c61726176656c2f646f776e6c6f616473)](https://packagist.org/packages/hradigital/php-exceptions-laravel)[![PHP Version Require](https://camo.githubusercontent.com/e6438d3d4bd583ce13f6bee4c8373aa7db9bc777883a0f8c33447789d6f67be0/68747470733a2f2f706f7365722e707567782e6f72672f6872616469676974616c2f7068702d657863657074696f6e732d6c61726176656c2f726571756972652f706870)](https://packagist.org/packages/hradigital/php-exceptions-laravel)[![License](https://camo.githubusercontent.com/e6a95035891f74786afe37a21e4f218b1bfaca4cc5d2a8d56eb99535abc128c2/68747470733a2f2f706f7365722e707567782e6f72672f6872616469676974616c2f7068702d657863657074696f6e732d6c61726176656c2f6c6963656e7365)](https://github.com/HRADigital/php-exceptions-laravel/blob/master/LICENSE)[![CI](https://github.com/HRADigital/php-exceptions-laravel/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/HRADigital/php-exceptions-laravel/actions/workflows/ci.yml)

Laravel wiring + JSON renderer for [`hradigital/php-exceptions`](https://packagist.org/packages/hradigital/php-exceptions).

The base library ships platform-agnostic domain exceptions (`AbstractBaseException` and the `Client/`+`Server/` trees, aligned with HTTP 4xx/5xx semantics). This package translates them into a uniform JSON response at the HTTP boundary and auto-registers itself with Laravel's exception handler, but **only for API requests** — browser requests still fall through to Laravel's default error pages.

Package`hradigital/php-exceptions-laravel`Namespace`HraDigital\Components\ExceptionRenderer`RequiresPHP `^8.1`, `hradigital/php-exceptions` `^1.0`Laravel`^10.0` · `^11.0` · `^12.0`LicenseGPL-3.0-or-later---

Installation
------------

[](#installation)

```
composer require hradigital/php-exceptions-laravel
```

Registration
------------

[](#registration)

The service provider is auto-discovered — no manual registration needed in most apps. If auto-discovery is disabled for this package, register it explicitly:

**Laravel 11 / 12** — `bootstrap/providers.php`:

```
return [
    HraDigital\Components\ExceptionRenderer\ExceptionsServiceProvider::class,
];
```

**Laravel 10 (and below)** — `config/app.php`:

```
'providers' => ServiceProvider::defaultProviders()->merge([
    HraDigital\Components\ExceptionRenderer\ExceptionsServiceProvider::class,
])->toArray(),
```

What the provider does
----------------------

[](#what-the-provider-does)

On `boot()`, the provider:

1. Binds `ExceptionRenderer` as a singleton in the container (so app code can resolve it and add custom strategies).
2. Hooks into Laravel's exception handler via `renderable()`, intercepting any thrown `AbstractBaseException`.

The hook **only renders** when the incoming request is treated as an API request:

SignalDetected via`Accept` header negotiates JSON`$request->expectsJson()` / `wantsJson()`Request body is JSON`$request->isJson()`URL path matches `api/*` (or is exactly `api`)`$request->is('api/*')`Matched route name starts with `api.``$request->route()?->getName()`For any other request, the callback returns `null`, so Laravel's default handler keeps rendering its usual HTML / Whoops / Blade error pages.

Response shapes
---------------

[](#response-shapes)

### Default

[](#default)

Any `AbstractBaseException` rendered via `DefaultRenderer`:

```
{
    "message": "Resource not found.",
    "code": 404,
    "data": { "id": 42 }
}
```

`data` is omitted when the exception was raised without structured payload (`hasData() === false`).

### Input / validation failures

[](#input--validation-failures)

Exceptions implementing `HraDigital\Components\Exceptions\Client\Request\RequestFailureInterface` are rendered via `InputFailureRenderer`:

```
{
    "message": "Invalid input.",
    "code": 422,
    "rules": { "email": ["required"] },
    "failed": [
        { "fieldName": "email", "message": "email is required" }
    ]
}
```

`rules` mirrors `getFailures()` (the field-keyed rule list). `failed` is the flattened, per-message list derived from `getFailedMessages()` — one entry per `{fieldName, message}` pair, preserving field order.

Adding a custom renderer strategy
---------------------------------

[](#adding-a-custom-renderer-strategy)

Resolve the singleton and prepend a strategy. The first strategy whose `supports()` returns `true` wins; the bundled `DefaultRenderer` is the always-matching fallback.

```
use HraDigital\Components\ExceptionRenderer\ExceptionRenderer;
use HraDigital\Components\ExceptionRenderer\Renderers\ExceptionRendererInterface;
use HraDigital\Components\Exceptions\AbstractBaseException;
use Symfony\Component\HttpFoundation\JsonResponse;

final class TenantQuotaRenderer implements ExceptionRendererInterface
{
    public function supports(AbstractBaseException $exception): bool
    {
        return $exception instanceof \App\Exceptions\TenantQuotaExceeded;
    }

    public function renderAsJson(AbstractBaseException $exception): JsonResponse
    {
        return new JsonResponse([
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'tenant' => $exception->getData()['tenant'] ?? null,
        ], $exception->getCode());
    }
}

// In a service provider's boot():
app(ExceptionRenderer::class)->add(new TenantQuotaRenderer());
```

`add()` always prepends, so later registrations override earlier ones.

Public API
----------

[](#public-api)

Class / interfacePurpose`ExceptionRenderer`Strategy dispatcher; `renderAsJson()`, `add()`, `getStrategies()`, factory.`Renderers\ExceptionRendererInterface`Contract every strategy implements (`supports()` + `renderAsJson()`).`Renderers\DefaultRenderer`Always-matching fallback; emits the default response shape.`Renderers\InputFailureRenderer`Matches `RequestFailureInterface`; emits validation-failure shape.`ExceptionsServiceProvider`Singleton binding + Laravel `renderable()` hook with API-request gating.Local development
-----------------

[](#local-development)

```
composer install
composer ci          # lint + phpcs + phpstan + phpunit
composer test        # phpunit only
composer cs          # PSR-12 check
composer cs:fix      # PSR-12 autofix
composer stan        # phpstan (level 6)
```

Continuous Integration
----------------------

[](#continuous-integration)

`.github/workflows/ci.yml` runs on every push and PR to `master`. It executes `lint → phpcs → phpstan → phpunit` against the supported PHP × Laravel matrix:

Laravel 10Laravel 11Laravel 128.1✓8.2✓✓✓8.3✓✓8.4✓The workflow pins `illuminate/*` and `orchestra/testbench` per matrix cell with `composer require --no-update` before installing.

Testing
-------

[](#testing)

The `tests/` suite covers every class in the package:

- `ExceptionRendererTest` — factory wiring, fallback path, strategy routing, prepend ordering, custom-constructor wiring.
- `Renderers/DefaultRendererTest` — status/message/code mapping, conditional `data` key.
- `Renderers/InputFailureRendererTest` — flattening of `failed`, `rules` mirroring, empty-payload behaviour.
- `ExceptionsServiceProviderTest` — Testbench-based: singleton binding, end-to-end HTTP rendering for JSON / `api/*` requests, and direct callback invocation proving non-API requests pass through (`null` return).
- `Support/Stubs` — shared anonymous-class factories for a generic exception and a `RequestFailureInterface` exception, excluded from the test suite.

License
-------

[](#license)

GPL-3.0-or-later — see [LICENSE](LICENSE). This matches the upstream `hradigital/php-exceptions` license.

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance93

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 90% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

35d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/34803364423357ae62828a472022dc7af10a4b64833418f8b8f4e905f6fa3fc8?d=identicon)[hradigital](/maintainers/hradigital)

---

Top Contributors

[![hugo-azevedo-tg](https://avatars.githubusercontent.com/u/123745232?v=4)](https://github.com/hugo-azevedo-tg "hugo-azevedo-tg (9 commits)")[![HRADigital](https://avatars.githubusercontent.com/u/8511557?v=4)](https://github.com/HRADigital "HRADigital (1 commits)")

---

Tags

jsonlaravelexceptionsrendererhradigital

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/hradigital-php-exceptions-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/hradigital-php-exceptions-laravel/health.svg)](https://phpackages.com/packages/hradigital-php-exceptions-laravel)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/reverb

Laravel Reverb provides a real-time WebSocket communication backend for Laravel applications.

1.6k12.9M69](/packages/laravel-reverb)[moonshine/moonshine

Laravel administration panel

1.3k239.9k72](/packages/moonshine-moonshine)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76318.2M110](/packages/laravel-mcp)[illuminate/routing

The Illuminate Routing package.

1239.0M2.8k](/packages/illuminate-routing)[spatie/laravel-export

Create a static site bundle from a Laravel app

670139.5k6](/packages/spatie-laravel-export)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
