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

Urljoin Laravel Package

busybee/urljoin

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require busybee/urljoin
    

    Or manually include src/urljoin.php in your project.

  2. First Use Case: Join a base URL with a relative path to generate an absolute URL:

    use BusyBee\UrlJoin\UrlJoin;
    
    $baseUrl = 'https://example.com/path/to/page';
    $relativeUrl = '../another-page';
    $absoluteUrl = UrlJoin::join($baseUrl, $relativeUrl);
    
    // Output: https://example.com/path/another-page
    
  3. Where to Look First:

    • README: For installation and basic usage.
    • Tests: tests.php in the repository for edge cases and expected behavior.
    • Source: src/UrlJoin.php for implementation details (e.g., handling edge cases like ../ underflow).

Implementation Patterns

Core Workflows

  1. URL Normalization: Use UrlJoin::join() to resolve relative URLs against a base URL in:

    • API Responses: Dynamically generate absolute URLs for redirects or API endpoints.
    • Frontend Assets: Join base paths (e.g., /assets/) with relative asset paths (e.g., images/logo.png).
    • Form Actions: Construct absolute URLs for form submissions when the base path is dynamic (e.g., multi-tenant apps).
    // Example: Dynamic API endpoint
    $base = route('api.base');
    $endpoint = UrlJoin::join($base, 'users/profile');
    
  2. Environment-Specific Bases: Store base URLs in config (e.g., config('app.url')) and reuse UrlJoin across environments:

    $base = config('app.url');
    $absolute = UrlJoin::join($base, '/relative-path');
    
  3. Validation Layer: Combine with Laravel’s Url::isValid() to sanitize joined URLs:

    $joinedUrl = UrlJoin::join($base, $relative);
    if (!Url::isValid($joinedUrl)) {
        abort(400, 'Invalid URL generated');
    }
    

Integration Tips

  • Service Providers: Bind UrlJoin as a singleton for dependency injection:

    $this->app->singleton('urlJoiner', function () {
        return new UrlJoin();
    });
    

    Then inject it into controllers/services:

    public function __construct(private UrlJoin $urlJoiner) {}
    
  • Blade Directives: Create a custom Blade directive for frontend templates:

    Blade::directive('join', function ($expression) {
        return "<?php echo BusyBee\UrlJoin\UrlJoin::join($expression[0], $expression[1]); ?>";
    });
    

    Usage:

    <a href="{{ join(['route(\"home\")', 'subpage']) }}">Link</a>
    
  • Middleware: Normalize URLs in incoming requests (e.g., rewrite relative paths in query params):

    public function handle($request, Closure $next) {
        $request->merge([
            'normalized_url' => UrlJoin::join(
                $request->get('base'),
                $request->get('relative')
            )
        ]);
        return $next($request);
    }
    

Gotchas and Tips

Pitfalls

  1. Protocol Handling:

    • Issue: Joining https://example.com with //subdomain.example.com may drop the protocol.
    • Fix: Explicitly handle protocol-relative URLs by stripping // before joining:
      $base = str_replace('//', '/', $base); // Normalize protocol-relative URLs
      
  2. Empty Paths:

    • Issue: Joining https://example.com with / may not behave as expected (e.g., returns https://example.com/).
    • Tip: Use rtrim($base, '/') to avoid double slashes in edge cases.
  3. Query Strings/Fragments:

    • Issue: UrlJoin does not preserve query strings or fragments (e.g., #section or ?id=1).
    • Workaround: Use parse_url() + http_build_query() to manually reattach them:
      $baseParts = parse_url($base);
      $relativeParts = parse_url($relative, PHP_URL_QUERY | PHP_URL_FRAGMENT);
      $joined = UrlJoin::join($base, $relative);
      parse_str($relativeParts['query'] ?? '', $query);
      $joined .= http_build_query($query) . ($relativeParts['fragment'] ?? '');
      
  4. Windows Paths:

    • Issue: Backslashes (\) in URLs (e.g., C:\path\file.html) may cause parsing errors.
    • Fix: Normalize paths to forward slashes:
      $base = str_replace('\\', '/', $base);
      

Debugging

  • Test Edge Cases: Use the package’s test suite (tests.php) to validate behavior for inputs like:

    • ../ underflow (e.g., https://example.com/a/../../bhttps://example.com/b).
    • Mixed protocols (e.g., http://example.com + https://subdomain.example.com).
    • Trailing slashes (e.g., https://example.com/ + path vs. https://example.com + /path).
  • Logging: Log joined URLs during development to catch silent failures:

    \Log::debug('Joined URL', ['base' => $base, 'relative' => $relative, 'result' => $joined]);
    

Extension Points

  1. Custom Normalization: Extend UrlJoin by subclassing and overriding join():

    class CustomUrlJoin extends UrlJoin {
        public function join($base, $other) {
            $base = $this->normalizeBase($base);
            return parent::join($base, $other);
        }
    
        protected function normalizeBase($url) {
            // Custom logic (e.g., append default port)
            return str_replace('http://', 'http://localhost:8000', $url);
        }
    }
    
  2. Laravel Facade: Create a facade for convenience:

    // app/Facades/UrlJoiner.php
    namespace App\Facades;
    use Illuminate\Support\Facades\Facade;
    class UrlJoiner extends Facade {
        protected static function getFacadeAccessor() {
            return 'urlJoiner';
        }
    }
    

    Usage:

    use App\Facades\UrlJoiner;
    $url = UrlJoiner::join($base, $relative);
    
  3. Performance:

    • Caching: Cache joined URLs if the base/relative pairs are static (e.g., navigation links).
    • Memoization: Use Laravel’s Cache::remember():
      $url = Cache::remember("urljoin_{$base}_{$relative}", now()->addHours(1), function () use ($base, $relative) {
          return UrlJoin::join($base, $relative);
      });
      
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.
make-dev/orca
dmstr/symfony-system-resources-bundle
dmstr/symfony-job-queue-bundle
dmstr/openapi-json-schema-bundle
dmstr/keycloak-security-bundle
dmstr/doctrine-audit-log-bundle
dmstr/api-platform-utils-bundle
dmstr/api-configuration-bundle
chrisdev/ux-components
baks-dev/finances
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle