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

Uri Template Laravel Package

rize/uri-template

RFC 6570 URI Template implementation for PHP. Expand templates into URLs and extract variables from matching URIs. Supports all expression types/levels, path segment and query expansions, plus base URI and default parameters—handy for building API endpoints.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation: Add to composer.json:

    "require": {
        "rize/uri-template": "^0.4"
    }
    

    Run composer update.

  2. Basic Expansion:

    use Rize\UriTemplate;
    $uri = new UriTemplate();
    echo $uri->expand('/users/{id}', ['id' => 123]);
    // Output: `/users/123`
    
  3. First Use Case: Dynamically generate API endpoints in Laravel controllers:

    $apiUrl = new UriTemplate('https://api.example.com/{version}');
    $endpoint = $apiUrl->expand('/users/{id}', ['version' => 'v1', 'id' => 42]);
    

Implementation Patterns

Core Workflows

1. API Client Integration

  • Base URL + Defaults:
    $client = new UriTemplate('https://api.twitter.com/{version}', ['version' => '1.1']);
    $tweetUrl = $client->expand('/statuses/show/{id}.json', ['id' => 12345]);
    // Output: `https://api.twitter.com/1.1/statuses/show/12345.json`
    
  • Laravel Service Provider: Bind the template in AppServiceProvider:
    $this->app->singleton('api.uri', fn() => new UriTemplate(config('api.base_url'), config('api.defaults')));
    

2. Dynamic Route Generation

  • Laravel Route Model Binding:
    Route::get('/users/{user:username}/posts/{post:slug}', function (UriTemplate $uri, $user, $post) {
        $uri->expand('/users/{username}/posts/{slug}', ['username' => $user, 'slug' => $post]);
    });
    
  • Microservice Communication: Use for inter-service URLs in a distributed system:
    $serviceUrl = new UriTemplate('http://{service}.service.local/{resource}');
    $url = $serviceUrl->expand('/users/{id}', ['service' => 'auth', 'id' => 1]);
    

3. Query Parameter Handling

  • Complex Queries:
    $query = new UriTemplate();
    $url = $query->expand('/search/{?q*,limit,offset}', [
        'q' => ['php', 'laravel'],
        'limit' => 10,
        'offset' => 0
    ]);
    // Output: `/search/?q=php&q=laravel&limit=10&offset=0`
    
  • Nested Arrays (Custom % Modifier):
    $url = $query->expand('/filter/{?filters%}', [
        'filters' => ['user[role]' => 'admin', 'user[status]' => 'active']
    ]);
    // Output: `/filter/?filters%5Buser%5D%5Brole%5D=admin&filters%5Buser%5D%5Bstatus%5D=active`
    

4. URI Extraction (Inbound Parsing)

  • Request Validation:
    $template = '/users/{username}/posts/{post_id}';
    $params = (new UriTemplate())->extract($template, request()->path());
    // Extracts `username` and `post_id` from the URL.
    
  • Strict Mode for Security:
    $params = (new UriTemplate())->extract('/{?required*,optional}', request()->query(), true);
    // Returns `null` if `required` params are missing.
    

Integration Tips

Laravel-Specific Patterns

  1. Middleware for URI Validation:

    public function handle(Request $request, Closure $next) {
        $template = '/api/{version}/{resource}';
        $params = (new UriTemplate())->extract($template, $request->path(), true);
        if (!$params) abort(404);
        return $next($request);
    }
    
  2. Form Request Validation:

    public function rules() {
        $uri = new UriTemplate();
        $params = $uri->extract('/{?page,per_page}', $this->queryString);
        return [
            'page' => ['required_if' => $params['page'] ?? null, 'integer'],
            'per_page' => ['required_if' => $params['per_page'] ?? null, 'integer'],
        ];
    }
    
  3. API Resource Serialization:

    public function toArray($request) {
        $uri = new UriTemplate();
        $params = $uri->extract('/users/{user}', $request->path());
        return [
            'user_id' => $params['user'],
            'url' => route('users.show', $params['user']),
        ];
    }
    

Performance Considerations

  • Reuse Instances: Instantiate UriTemplate once per request or service (e.g., in a service container) to avoid parsing overhead.

    // In AppServiceProvider
    $this->app->singleton(UriTemplate::class, fn() => new UriTemplate());
    
  • Cache Templates: For frequently used templates (e.g., API endpoints), cache the compiled patterns:

    $cache = Cache::remember('uri_template_compiled', 60, fn() => new UriTemplate($template));
    

Gotchas and Tips

Common Pitfalls

  1. Strict Mode Misuse:

    • Issue: extract() returns null in strict mode if the URI doesn’t match the template exactly.
    • Fix: Use !== null checks or provide fallback logic:
      $params = (new UriTemplate())->extract($template, $uri, true);
      if ($params === null) {
          return response()->json(['error' => 'Invalid URI'], 400);
      }
      
  2. Query Parameter Encoding:

    • Issue: The % modifier encodes [] as %5B%5D, which may not match client expectations.
    • Fix: Decode manually if needed:
      $decoded = str_replace('%5B', '[', str_replace('%5D', ']', $uri));
      
  3. Default Parameters Override:

    • Issue: Defaults set in the constructor are merged with expansion params, which can lead to unexpected values.
    • Fix: Explicitly pass all required params:
      $uri = new UriTemplate('https://api.example.com/{version}', ['version' => 'v1']);
      $url = $uri->expand('/{resource}', ['resource' => 'users', 'version' => 'v2']); // Overrides default
      
  4. Nested Array Extraction:

    • Issue: The % modifier may not handle deeply nested arrays as expected.
    • Fix: Pre-process data before extraction:
      $flatFilters = [];
      foreach ($request->input('filters', []) as $key => $value) {
          $flatFilters["filters[$key]"] = $value;
      }
      $params = (new UriTemplate())->extract('/{?filters%}', http_build_query($flatFilters));
      
  5. PHP 8.1+ Requirement:

    • Issue: Older PHP versions (e.g., 7.4) will fail with type errors.
    • Fix: Update your Laravel version (9+) or use a lower package version (~0.3) if stuck on PHP 7.4.

Debugging Tips

  1. Validate Templates: Use the extract method with strict => true to test templates against real URIs:

    $params = (new UriTemplate())->extract('/{?page,per_page}', '/search?page=2', true);
    // Debug: `var_dump($params)` to see extracted values.
    
  2. Inspect Expansion: Log intermediate steps to debug complex templates:

    $uri = new UriTemplate();
    $parts = explode('/', $template);
    foreach ($parts as $part) {
        log("Processing part: $part");
    }
    
  3. Handle Edge Cases:

    • Empty Arrays: Ensure your template accounts for empty query params:
      $url = $uri->expand('/{?tags*}', ['tags' => []]); // Output: `/`
      
    • Reserved Characters: Escape special chars in params:
      $params = ['query' => 'hello world'];
      $url = $uri->expand('/search/{?q}', $params); // Output: `/search/?q=hello+world`
      

Extension Points

  1. Custom Modifiers: Extend the library by implementing your own modifiers (e.g., for date formatting):
    // Hypothetical: Add a `:date` modifier to format timestamps.
    $uri->expand('/events/{date:date}', ['date' => '202
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity