Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Hal Laravel Package

nocarrier/hal

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require nocarrier/hal
    

    Add to composer.json if not using autoload globally:

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Nocarrier\\": "vendor/nocarrier/hal/src/"
        }
    }
    

    Run composer dump-autoload.

  2. First Use Case: Create a simple HAL resource in a Laravel controller:

    use Nocarrier\Hal;
    
    public function showOrder(Order $order)
    {
        $resource = new Hal('/api/orders/' . $order->id, [
            'id' => $order->id,
            'amount' => $order->amount,
            'status' => $order->status,
        ]);
    
        $resource->addLink('self', '/api/orders/' . $order->id);
        $resource->addLink('customer', '/api/customers/' . $order->user_id);
    
        return response()->json($resource->asJson());
    }
    
  3. Key Starting Points:

    • Hal class constructor takes URI and data array.
    • Use addLink() for relationships (e.g., self, collection, up).
    • Embed resources with addResource() for nested HAL documents.
    • Output with asJson() or asXml().

Implementation Patterns

Common Workflows

1. API Resource Transformation

Use HAL to standardize API responses with hypermedia controls:

public function index()
{
    $collection = new Hal('/api/orders', [], 'orders');
    foreach ($orders as $order) {
        $resource = new Hal('/api/orders/' . $order->id, [
            'id' => $order->id,
            'amount' => $order->amount,
        ]);
        $resource->addLink('self', '/api/orders/' . $order->id);
        $collection->addResource('order', $resource);
    }
    return response()->json($collection->asJson());
}

2. Dynamic Link Generation

Generate links dynamically in controllers or services:

$resource->addLink('edit', route('admin.orders.edit', $order->id));
$resource->addLink('delete', route('admin.orders.destroy', $order->id), [
    'method' => 'DELETE',
    'title' => 'Delete Order',
]);

3. Embedding Related Resources

Fetch and embed related models (e.g., customer for an order):

$orderResource = new Hal('/api/orders/' . $order->id, [...]);
$customerResource = new Hal('/api/customers/' . $order->user_id, [
    'name' => $order->user->name,
]);
$orderResource->addResource('customer', $customerResource);

4. Middleware for HAL Responses

Create middleware to wrap responses in HAL format:

public function handle($request, Closure $next)
{
    $response = $next($request);
    if ($response->isJson()) {
        $data = $response->json();
        $hal = new Hal($request->url(), $data);
        $response->setContent($hal->asJson());
    }
    return $response;
}

5. Formatting with Laravel's Resource Classes

Combine with Laravel's JsonResource for cleaner code:

public function toArray($request)
{
    return [
        'id' => $this->id,
        'amount' => $this->amount,
        '_links' => [
            'self' => ['href' => route('api.orders.show', $this)],
        ],
    ];
}

Then convert to HAL in the controller:

$resource = new Hal('/api/orders/' . $order->id, $order->toArray($request));

Integration Tips

Laravel Service Providers

Bind HAL to the container for dependency injection:

public function register()
{
    $this->app->bind(Hal::class, function () {
        return new Hal('/');
    });
}

API Versioning

Use HAL to include versioned links:

$resource->addLink('self', '/v1/orders/' . $order->id);
$resource->addLink('latest', '/v2/orders/' . $order->id);

Caching HAL Responses

Cache HAL-formatted responses in Laravel:

return Cache::remember("hal.order.{$order->id}", now()->addHours(1), function () use ($order) {
    return response()->json((new Hal(...))->asJson());
});

Testing

Test HAL responses with PHPUnit:

public function testHalResponse()
{
    $response = $this->get('/api/orders/1');
    $hal = json_decode($response->getContent(), true);
    $this->assertArrayHasKey('_links', $hal);
    $this->assertArrayHasKey('self', $hal['_links']);
}

Gotchas and Tips

Pitfalls

1. URI Normalization

  • HAL expects absolute URIs (e.g., /api/orders/1). Relative paths (e.g., ../orders/1) may break links.
  • Fix: Use Laravel's url() or route() helpers to generate absolute paths:
    $resource->addLink('self', url('/api/orders/' . $order->id));
    

2. Link Href vs. Title Confusion

  • addLink() accepts href and title as separate parameters, but the method signature might be confusing:
    // Correct:
    $resource->addLink('self', '/url', ['title' => 'Order Details']);
    
    // Incorrect (will throw error):
    $resource->addLink('self', ['href' => '/url', 'title' => 'Order Details']);
    

3. Nested Resource Overwriting

  • If you add the same resource key twice (e.g., addResource('customer', ...)), the last one wins.
  • Fix: Use unique keys or merge resources carefully.

4. XML Output Quirks

  • The asXml() method may not handle all edge cases (e.g., special characters in data).
  • Fix: Sanitize data before passing to HAL or use htmlspecialchars():
    $data['description'] = htmlspecialchars($order->description);
    

5. Performance with Large Collections

  • Embedding many resources (e.g., 1000+ orders) can bloat the response.
  • Fix: Use pagination or lazy-loading:
    $collection->addResource('order', $resource); // Only add current page's orders
    

Debugging Tips

1. Validate HAL Structure

Use online validators like HAL Explorer to check your output:

// Dump raw HAL for debugging:
dd($resource->asJson());

2. Check for Missing Links

Ensure _links are always included in responses:

if (!isset($hal['_links'])) {
    throw new \RuntimeException('HAL document missing _links section.');
}

3. Log HAL Output

Log HAL responses for debugging:

\Log::debug('HAL Response', [
    'uri' => $resource->getUri(),
    'data' => $resource->asJson(),
]);

Extension Points

1. Custom Link Types

Extend HAL to support custom link relations (e.g., api:update):

$resource->addLink('api:update', '/api/orders/' . $order->id, [
    'method' => 'PATCH',
    'title' => 'Update Order',
]);

2. Add Metadata

Include custom metadata in the _meta section:

$resource->setMeta(['created_at' => $order->created_at->toIsoString()]);

3. Override Serialization

Extend the Hal class to customize JSON/XML output:

class CustomHal extends Hal
{
    public function asJson()
    {
        $json = parent::asJson();
        // Modify $json here (e.g., add timestamps)
        return $json;
    }
}

4. Integrate with Laravel Policies

Use HAL to expose policy-based links:

if ($this->authorize('update', $order)) {
    $resource->addLink('update', route('api.orders.update', $order));
}

5. Add CORS Headers

Combine HAL with CORS middleware:

$response = response()->json($
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
calliostro/spotify-bundle
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle