egeloen/json-builder
PHP 5.6+ library to build JSON using Symfony PropertyAccess paths. Set nested values, arrays, and raw/unescaped values while retaining control over escaping. Produces JSON strings from a fluent builder API with strong test coverage.
Installation:
composer require egeloen/json-builder
Ensure vendor/autoload.php is included in your Laravel project.
First Use Case: Build a simple JSON response with controlled escaping:
use Ivory\JsonBuilder\JsonBuilder;
$builder = new JsonBuilder();
$json = $builder
->setValues(['user' => ['name' => 'John', 'age' => 30]])
->setValue('[user][bio]', '{"raw": "json"}', false) // Disable escaping for raw JSON
->build();
JsonBuilder class: Core class for building JSON.setValues() vs setValue(): Understand the difference in escaping behavior.build() method: Finalizes the JSON structure.reset() method: Clears the builder state for reuse.Building API Responses:
$builder = new JsonBuilder();
$builder
->setValues(['data' => []])
->setValue('[data][0]', ['id' => 1, 'name' => 'Item 1'])
->setValue('[meta][total]', 1, false); // Disable escaping for numeric metadata
return response()->json($builder->build());
Dynamic JSON Construction:
Use setValue() with dynamic paths for nested structures:
$path = '[user][roles][' . $roleIndex . ']';
$builder->setValue($path, $roleName, false);
Integration with Laravel Collections: Convert a collection to JSON with custom escaping:
$builder = new JsonBuilder();
$builder->setValues($users->toArray());
$builder->setValue('[metadata][count]', $users->count(), false);
return $builder->build();
Conditional JSON Fields:
if ($hasPermissions) {
$builder->setValue('[user][permissions]', $permissions, false);
}
Service Providers:
Bind JsonBuilder to the container for dependency injection:
$this->app->bind(JsonBuilder::class, function () {
return new JsonBuilder();
});
Middleware: Use the builder in middleware to transform request/response data:
public function handle($request, Closure $next) {
$response = $next($request);
if ($response->isJson()) {
$builder = app(JsonBuilder::class);
$data = json_decode($response->getContent(), true);
$builder->setValues($data);
$response->setContent($builder->build());
}
return $response;
}
Form Requests: Validate and build JSON responses from form data:
public function rules() {
return ['name' => 'required', 'email' => 'email'];
}
public function withValidator($validator) {
$builder = new JsonBuilder();
$builder->setValues($validator->errors()->toArray());
return $builder->build();
}
Escaping Behavior:
setValues() always escapes values (like json_encode).setValue(..., false) disables escaping for that specific value.false flag can lead to malformed JSON if passing raw strings/objects.Path Syntax:
[user][roles][0]).[user][roles] when roles is not an array) will throw errors.Stateful Builder:
reset() before reuse:
$builder->reset(); // Critical for multi-use scenarios
JSON Options:
JSON_FORCE_OBJECT for nested arrays).Performance:
setValue() calls. Use setValues() for bulk operations.Validate JSON Output:
Use json_last_error() to debug malformed JSON:
$json = $builder->build();
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException('Invalid JSON: ' . json_last_error_msg());
}
Inspect Builder State: Temporarily log the internal state:
$reflection = new \ReflectionClass(JsonBuilder::class);
$property = $reflection->getProperty('values');
$property->setAccessible(true);
var_dump($property->getValue($builder));
Custom Escaping: Extend the builder for custom escaping logic:
$builder->setValue('[custom]', $value, false);
$json = $builder->build();
$json = str_replace('"custom"', 'custom', $json); // Example: Unquote keys
Laravel Facade: Create a facade for convenience:
// JsonBuilderFacade.php
namespace App\Facades;
use Ivory\JsonBuilder\JsonBuilder;
use Illuminate\Support\Facades\Facade;
class JsonBuilderFacade extends Facade {
protected static function getFacadeAccessor() {
return JsonBuilder::class;
}
}
Register in config/app.php:
'aliases' => [
'JsonBuilder' => App\Facades\JsonBuilderFacade::class,
],
Testing: Mock the builder in tests to isolate JSON logic:
$mockBuilder = Mockery::mock(JsonBuilder::class);
$mockBuilder->shouldReceive('build')->andReturn('{"test": true}');
Configuration:
Set default JSON options in a config file (e.g., config/json-builder.php):
return [
'options' => JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES,
];
Load in AppServiceProvider:
$builder = new JsonBuilder();
$builder->setJsonEncodeOptions(config('json-builder.options'));
$this->app->singleton(JsonBuilder::class, function () use ($builder) {
return $builder;
});
Edge Cases:
null values explicitly:
$builder->setValue('[user][address]', null, false); // Avoids JSON null escaping
JSON_THROW_ON_ERROR for strict validation:
$builder->setJsonEncodeOptions(JSON_THROW_ON_ERROR);
How can I help you explore Laravel packages today?