Repository navigation
Laravel Integration
StrObj needs no service provider, facade or configuration to work in Laravel.
Each StringObjects instance wraps one document and holds no static state, so it
is safe in queues, Horizon workers and Octane.
The examples target Laravel 10 and later; they use only stable framework APIs.
composer require uuur86/strobj<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use InvalidArgumentException;
use StrObj\StringObjects;
final class WebhookController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
try {
$payload = StringObjects::instance($request->getContent(), [
'validation' => ['rules' => [
['path' => 'event', 'pattern' => '/^(order\.paid|order\.refunded)$/', 'required' => true],
['path' => 'data/order/id', 'pattern' => '/^\d+$/', 'required' => true],
['path' => 'data/order/items/*/sku', 'pattern' => '/^[A-Z0-9-]{3,30}$/', 'required' => true],
]],
'filters' => [
'data/order/id' => ['type' => 'int'],
],
]);
} catch (InvalidArgumentException $exception) {
return response()->json(['message' => 'The request body must be a JSON object.'], 400);
}
if (!$payload->isValid()) {
return response()->json(['message' => 'The payload is invalid.'], 422);
}
$orderId = $payload->get('data/order/id'); // int
$coupon = $payload->get('data/order/coupon', null); // null when missing
$skus = $payload->get('data/order/items/*/sku'); // list of SKUs
// ...
return response()->json(['received' => true]);
}
}-
$request->getContent()passes the raw body, so StrObj decodes the JSON and reports invalid documents with exception code 22. - When the request was already parsed, pass the array instead:
StringObjects::instance($request->json()->all()). - Laravel limits request sizes at the web server and PHP level (
post_max_size). Keep those limits in place; StrObj does not limit document size.
Move options out of controllers so they can be reused and unit tested:
<?php
namespace App\Payloads;
use InvalidArgumentException;
use StrObj\StringObjects;
final class OrderPayload
{
private const OPTIONS = [
'validation' => [
'patterns' => ['sku' => '/^[A-Z0-9-]{3,30}$/'],
'rules' => [
['path' => 'order/id', 'pattern' => '/^\d+$/', 'required' => true],
['path' => 'order/items/*/sku', 'pattern' => 'sku', 'required' => true],
['path' => 'order/items/*/qty', 'pattern' => '/^[1-9]\d{0,3}$/', 'required' => true],
],
],
'filters' => [
'order/id' => ['type' => 'int'],
'order/items/*/qty' => ['type' => 'int'],
],
];
private StringObjects $data;
private function __construct(StringObjects $data)
{
$this->data = $data;
}
/** @throws InvalidPayload */
public static function fromJson(string $json): self
{
try {
$data = StringObjects::instance($json, self::OPTIONS);
} catch (InvalidArgumentException $exception) {
throw new InvalidPayload('The order document is not valid JSON.', 0, $exception);
}
if (!$data->isValid()) {
throw new InvalidPayload('The order document does not match the expected format.');
}
return new self($data);
}
public function id(): int
{
return $this->data->get('order/id');
}
/** @return list<int> */
public function quantities(): array
{
return $this->data->get('order/items/*/qty');
}
}<?php
namespace App\Payloads;
use RuntimeException;
final class InvalidPayload extends RuntimeException
{
}Render the domain exception once, in bootstrap/app.php (Laravel 11 and later):
<?php
use App\Payloads\InvalidPayload;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
//
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (InvalidPayload $exception) {
return response()->json(['message' => $exception->getMessage()], 422);
});
})
->create();Only the withExceptions() callback is new; keep the rest of your existing file.
On Laravel 10, register the same callback in App\Exceptions\Handler::register()
with $this->renderable(...).
If several classes share patterns, keep them in config/strobj.php:
<?php
return [
'patterns' => [
'sku' => '/^[A-Z0-9-]{3,30}$/',
'iso_date' => '/^\d{4}-\d{2}-\d{2}$/',
],
];$options = ['validation' => ['patterns' => config('strobj.patterns'), 'rules' => $rules]];Configuration files are trusted code. Never merge request data into options.
For a JSON column cast to array, wrap the attribute and write the result back:
$settings = StringObjects::instance($user->settings ?? []);
$settings->set('notifications/email', false);
$user->settings = $settings->toArray();
$user->save();Writes from requests should go through a whitelist of allowed paths (see Security Guide).
- Create a new instance per job or request; do not store instances in static properties or singletons that outlive a request.
- To reuse a fetched document, cache its JSON with
Cache::remember()and create a new instance from it; see Caching documents. - The optional
middleware.memory_limitguard measures the whole worker process. Leave it unset in long-running workers, or set it above the worker's normal peak.
public function test_quantities_are_cast_to_integers(): void
{
$payload = OrderPayload::fromJson('{"order":{"id":"7","items":[{"sku":"AB-1","qty":"2"}]}}');
$this->assertSame(7, $payload->id());
$this->assertSame([2], $payload->quantities());
}
public function test_invalid_documents_are_rejected(): void
{
$this->expectException(InvalidPayload::class);
OrderPayload::fromJson('{"order":{"id":"x"}}');
}Use Laravel's validator for form requests and database-aware rules (exists,
unique). Use StrObj when you work with deeply nested JSON documents (webhooks,
third-party API responses, JSON columns), need path-based reads with explicit
defaults, or need the same logic outside the framework.
StrObj · MIT License · Repository · Changelog · Questions: contact@fyndsoft.com
StrObj
- Home
- Installation
- Getting Started
- Values, Defaults and Missing Fields
- Validation
- Filters
- Error Handling
Integration
Operations