Skip to main content

Gluo for Laravel

Use the ByJG components in Laravel with no effort: composer require a component, and its connector wires it into your application.

Gluo (Esperanto for glue) is a family of packages that bind the ByJG components together. This one is the Laravel connector: it adapts those components to Laravel idioms — service providers, configuration, middleware, artisan commands and test helpers — so you can drop them into an existing application without writing the plumbing yourself.

Sponsor Build Status Opensource ByJG Install MCP Server GitHub source GitHub license GitHub release

PackageRole
byjg/gluoScaffold for a standalone REST API project
byjg/gluo-coreThe common framework core shared by Gluo projects
byjg/gluo-laravelConnectors for an existing Laravel application

Why​

A standalone component still needs plumbing before Laravel can use it: a service provider, a config file, a middleware, an artisan command, test helpers. Writing that for every component is the work that keeps people from adopting it.

Gluo for Laravel is that plumbing, already written. Its service provider is auto-discovered, and each connector switches itself on only when its component is installed:

composer require byjg/gluo-laravel byjg/swagger-test # contract tests through the Laravel kernel
php artisan gluo:install

It pulls in no ByJG component itself, so you install only the ones you use.

Pluggable by design​

Requiring this package pulls in no ByJG component at all — only illuminate/*.

You install the components you actually want, and the matching connector activates on its own. A connector whose component is absent simply stays dormant.

composer require byjg/gluo-laravel

# Want OpenAPI contract testing? Add the component; the connector wakes up.
composer require byjg/swagger-test

Available connectors​

ConnectorComponentWhat it gives you
openapibyjg/swagger-testContract testing through the Laravel kernel, plus a runtime validation middleware
statemachinebyjg/statemachineMachines declared as configuration, bound to your Eloquent models, validated in CI

More connectors are added over the same mechanism — see Writing a connector.

Quick start​

composer require byjg/gluo-laravel byjg/swagger-test
php artisan gluo:install

gluo:install publishes config/gluo.php and a ready-to-edit tests/Feature/ApiContractTest.php. Point the configuration at your specification:

GLUO_OPENAPI_SPEC=/var/www/openapi.json

Contract testing​

Every call is validated against the specification — request body, status code and response body. No HTTP server is involved: requests go straight through the Laravel kernel, so the whole middleware stack runs and actingAs(), RefreshDatabase and the rest keep working.

use ByJG\Gluo\Laravel\ApiTools\Testing\InteractsWithOpenApi;
use Tests\TestCase;

class UserApiTest extends TestCase
{
use InteractsWithOpenApi;

public function testCreateUser(): void
{
$request = $this->openApiRequest()
->withMethod('POST')
->withPath('/api/users')
->withRequestBody(['name' => 'John Doe', 'email' => '[email protected]'])
->expectStatus(201);

$this->sendRequest($request);
}
}

If the route returns a field the specification does not declare, omits a required one, or answers with an unexpected status, the test fails with an exception naming exactly what diverged.

Contract testing guide →

Runtime validation​

The same specification can guard the API at runtime through the gluo.openapi middleware:

Route::post('/users', [UserController::class, 'store'])
->middleware('gluo.openapi'); // config defaults

Route::get('/users', [UserController::class, 'index'])
->middleware('gluo.openapi:request,response'); // validate both sides

A request that violates the contract is rejected with 400 before reaching the controller. A response that violates it is logged and rejected with 500, because that is a server-side defect.

Runtime validation guide →

State machines​

Machines are declared as configuration and bound to the models they govern:

class Order extends Model implements StatefulModel
{
use HasStateMachine;

protected $casts = ['status' => OrderState::class];
}
DB::transaction(function () use ($order, $data) {
$order->transitionTo(OrderState::Paid, $data);

$this->somethingElseThatMayThrow(); // rolls back: the receipt never goes out
});

The move is persisted before its action runs, and the action is held until the transaction commits — so a side effect can never escape a write that was rolled back. Conditions and actions are resolved through the container, so they are ordinary services with ordinary dependencies, and a CanTransitionTo validation rule turns an illegal move requested by a client into a 422 instead of an exception.

State machine guide →

Requirements​

  • PHP >=8.3 <8.7
  • Laravel ^12.0 or ^13.0

Documentation​

Installation​

composer require byjg/gluo-laravel

Running Tests​

composer update
vendor/bin/phpunit
composer psalm

Open source ByJG