equidna / laravel-toolkit
Multi-context Laravel helpers, middleware, and utilities.
Installs: 225
Dependents: 6
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/equidna/laravel-toolkit
Requires
- php: ^8.2
- illuminate/support: ^11.21 || ^12.0
- laravel/framework: ^11.21 || ^12.0
- laravel/helpers: ^1.7
Requires (Dev)
README
A Laravel 11/12 package that unifies response patterns, pagination utilities, and route detection across web, API, hook, IoT, and console entry points. The toolkit ships helpers, middleware, traits, and a service provider so host applications can enforce consistent behavior without rewriting boilerplate.
At a Glance
- Context-aware routing: Detect API, hook, IoT, JSON-only, and console flows with configurable matchers.
- Unified responses: Generate JSON, redirects, or console output through a single helper API and swappable strategies.
- Validation-friendly requests:
EquidnaFormRequestsurfaces validation failures as HTTP exceptions instead of redirects. - Pagination utilities: Build length-aware or cursor paginators from arrays, collections, or queries with sanitized query strings.
- Pluggable middleware: Force JSON, disable Debugbar, or exclude requests from session history as needed.
- Composite key support:
HasCompositePrimaryKeyremoves boilerplate for multi-column primary keys.
Compatibility
- PHP: 8.2 or 8.3 (validated in CI)
- Laravel: 11.x or 12.x
- Composer: 2.5+ recommended (for
composer audit)
Installation
composer require equidna/laravel-toolkit php artisan vendor:publish --tag=equidna:config
Auto-discovery registers the service provider. For manual registration add to config/app.php:
'providers' => [ Equidna\Toolkit\Providers\EquidnaLaravelToolkitServiceProvider::class, ],
Configuration
config/equidna.php (publish to your app) exposes the main touchpoints:
return [ 'paginator' => [ 'page_items' => 15, 'strategy' => null, // bind PaginationStrategyInterface to override ], 'route' => [ 'api_matchers' => ['api*', '*-api*'], 'hook_matchers' => ['hooks/*'], 'iot_matchers' => ['iot/*'], 'json_matchers' => [], 'detector' => null, // RouteDetectorInterface 'request_resolver' => null, // RequestResolverInterface ], 'responses' => [ 'allowed_headers' => ['Cache-Control', 'Retry-After'], 'strategies' => [], // console/json/redirect => class names ], ];
- Matchers feed directly into
Request::is(). Align them with your route prefixes (e.g.,services/api/*). - JSON preference is inferred from API/hook/IoT matchers, additional
json_matchers, orRequest::expectsJson(). - To override behavior, either bind the related interface or set the fully qualified class in config; boot will fail fast if a class is missing or does not implement the expected interface.
- Allowed headers (
equidna.responses.allowed_headers): When JSON responses are sent, only headers in this allow-list are passed to the client. By default,Cache-ControlandRetry-Afterare allowed. In non-debug mode, the header filtering is enforced to prevent accidental leakage of sensitive internal headers (e.g., X-Debug-Token). Customize this list in config if your application depends on additional headers for caching or rate-limiting.
Mandatory configuration and failure modes
The service provider validates critical bindings during boot. Laravel throws an InvalidArgumentException when a configured class is missing or does not implement its interface (e.g., RouteDetectorInterface, RequestResolverInterface, PaginationStrategyInterface, ResponseStrategyInterface). This keeps deployments from silently misbehaving.
The toolkit ships default pagination and response strategies; leaving equidna.paginator.strategy and equidna.responses.strategies empty uses the built-ins. Misconfigured or missing classes raise a ConfigurationException at runtime. Ensure paginator.page_items remains a positive integer to avoid boot-time failures.
Swap strategies for org-specific policies
Use container overrides or config to customize detection, pagination, or responses without editing package code:
// In your application's service provider $this->app->singleton(\Equidna\Toolkit\Contracts\RouteDetectorInterface::class, fn($app) => $app->make(App\Routing\SubdomainRouteDetector::class) ); $this->app->singleton('equidna.responses.json_strategy', fn($app) => $app->make(App\Http\Responses\AuditJsonResponse::class) );
Usage
RouteHelper
use Equidna\Toolkit\Helpers\RouteHelper; if (RouteHelper::isApi()) { /* ... */ } if (RouteHelper::wantsJson()) { /* return an API-friendly payload */ }
ResponseHelper
use Equidna\Toolkit\Helpers\ResponseHelper; // JSON for API/hook/IoT, redirect with flash for web, text for console return ResponseHelper::success('Saved', ['id' => $model->id]); // Custom status and headers (filtered through the allow-list for JSON) return ResponseHelper::unprocessableEntity( message: 'Invalid input', errors: ['email' => ['Already taken']], headers: ['Retry-After' => '30'], );
Pagination
use Equidna\Toolkit\Helpers\PaginatorHelper; $paginator = PaginatorHelper::buildPaginator($collection, page: 2, items_per_page: 20, set_full_url: true); PaginatorHelper::appendCleanedRequest($paginator, request());
Cursor and length-aware pagination helpers proxy to the configured PaginationStrategyInterface and accept optional transformations via through().
Composite primary keys
use Equidna\Toolkit\Traits\Database\HasCompositePrimaryKey; class UserRole extends Model { use HasCompositePrimaryKey; public function getKeyName() { return ['user_id', 'role_id']; } }
Middleware
Register in your host app's Http\Kernel when desired:
Http\Middleware\ForceJsonResponse– forces JSON responses for matched requests.Http\Middleware\ExcludeFromHistory– skips adding requests to browser history.Http\Middleware\DisableDebugbar– disables Laravel Debugbar for the request lifecycle.
Exceptions
HTTP-friendly exceptions (src/Exceptions/*) are container-bound by the service provider and render context-aware responses. Use them in controllers/services to standardize error handling (e.g., throw new UnauthorizedException();).
Traits & Requests
Traits\Database\HasCompositePrimaryKey– declare composite key columns viagetKeyName().Traits\Database\Paginator– integrates pagination helpers inside models.Http\Requests\EquidnaFormRequest– extends Laravel'sFormRequestto emit HTTP exceptions instead of redirects on validation failure.
Technical overview
Helpers/— context-aware utilities (RouteHelper, ResponseHelper, PaginatorHelper)Http/Middleware/— ForceJsonResponse, ExcludeFromHistory, DisableDebugbarExceptions/— custom HTTP exceptions auto-bound by the service providerTraits/Database/— HasCompositePrimaryKey, Paginator (for Eloquent)Providers/— EquidnaLaravelToolkitServiceProvider
Development
- Coding standard: PSR-12 (
vendor/bin/phpcs --standard=ruleset.xml). - Static analysis:
vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=512M. - Tests:
vendor/bin/phpunit. - Run
composer audit --lockedbefore releases.
Release hygiene
- Keep
CHANGELOG.mdupdated for every user-facing change and reset theUnreleasedsection when tagging. - Align the package version in
composer.jsonwith the release tag and changelog entry. - Run audits, linters, static analysis, and tests (above) before publishing.
PHPStan note
Running PHPStan against the library can surface trait.unused warnings for Traits/Database/HasCompositePrimaryKey.php and Traits/Database/Paginator.php because they are consumed by downstream apps. Options:
- Leave the warning (informational).
- Add an
ignoreErrorsrule for these messages inphpstan.neon. - Add minimal unit tests or example usage files that reference the traits so PHPStan recognizes they are used.
Enterprise readiness checklist
- Compatibility matrix: PHP 8.2/8.3, Laravel 11/12. Defaults work without custom strategies; failures raise
ConfigurationException. - Quality gates (run before release):
composer install,vendor/bin/phpunit,vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=512M,vendor/bin/phpcs --standard=ruleset.xml,composer audit --locked(add--no-devfor production builds). - Configuration safety: Toolkit ships default pagination/response strategies; boot-time validation prevents misconfiguration.
paginator.page_itemsmust be a positive integer. - Security posture: No remote calls or telemetry; header allow-list enforced for JSON/redirect contexts. Use GitHub issues for security contact until a SECURITY.md is published.
- Release discipline: Semantic Versioning; align
composer.jsonversion, tag, andCHANGELOG.mdentry. Run quality gates and audits before tagging.
License & compliance
- License: MIT (see
LICENSE). - Dependencies: Laravel Framework, Illuminate Support, and Laravel Helpers (MIT licensed).
- Packaging: Ships as a Composer library with no bundled telemetry or proprietary services.