genj/frontend-url-bundle
Symfony bundle for generating frontend URLs from your backend configuration. Define route mappings once and use its services and Twig helpers to build consistent, environment-aware links without hardcoding paths in templates.
First, the honest reality check: genj/frontend-url-bundle is a Symfony bundle, not a Laravel package. It won't composer require cleanly into a Laravel app, it registers itself through AppKernel/Bundle classes and a DI extension, and its last release was December 2015 (Symfony 2.x / PHP 5.x era). With 0 stars, 0 dependents, and an archived repository, treat it as reference material, not a dependency. What you actually want is the pattern: generating absolute URLs to a customer-facing frontend from an admin app that may live on a different host, route tree, or even codebase.
Where to look first (if you're evaluating the Symfony source):
DependencyInjection/Configuration.php — the config keys (frontend base URL/host, route map).DependencyInjection/GenjFrontendUrlExtension.php — how the generator service is wired.Twig/ — the template helper (frontend_url(...)-style function).Resources/config/services.yml — the public service alias.Minimal Laravel port (the first thing to build):
// config/frontend.php
return [
'host' => env('FRONTEND_URL', 'https://shop.example.com'),
'routes' => [
// admin route name => frontend route name
'admin.products.show' => 'shop.products.show',
],
];
// app/Support/FrontendUrl.php
namespace App\Support;
use Illuminate\Support\Facades\Route;
class FrontendUrl
{
public function to(string $adminRoute, array $params = []): string
{
$route = config("frontend.routes.$adminRoute", $adminRoute);
return route($route, $params, true); // relies on the frontend route being loaded
}
}
Register it as a singleton and expose frontend_url() in a helpers file. Your first use case is almost always this: an admin order/product/content detail screen with a "View on site" link, plus the same URL embedded in notification emails and API payloads.
1. Host override instead of a second router. The cleanest Laravel equivalent doesn't replicate route names at all — it swaps the host on an absolute URL:
function frontend_url(string $path = '/', array $query = []): string
{
$url = url($path); // current app's route
return str_replace(config('app.url'), config('frontend.host'), $url);
}
This works when admin and frontend are the same Laravel app served on two domains. Use route-name mapping (above) only when they're genuinely separate apps.
2. Route-name indirection for decoupling. Keep a map so a frontend route rename is a one-line config change, not a grep across Blade templates. This is the core value the bundle was chasing.
3. Blade directive and mail-friendly helper.
<a href="{{ frontend_url(route('admin.orders.show', $order, false)) }}" target="_blank">
View customer page
</a>
For queued mail/notifications, generate the URL before dispatch or ensure frontend.host is in the worker's cached config — a classic source of broken links.
4. Signed preview links for unpublished content.
The natural extension of the pattern: URL::temporarySignedRoute() on the frontend route, minted from admin, verified on the frontend. Far more useful than a plain URL helper.
5. Locale/subdomain aware hosts.
'hosts' => [
'en' => 'https://example.com',
'de' => 'https://example.de',
],
Resolve by app()->getLocale() or the entity's locale. The 2015 bundle had no notion of this; expect to build it yourself.
6. Environment matrix in tests.
Set FRONTEND_URL=https://frontend.test in phpunit.xml so assertions stay deterministic, and bind a fake generator in feature tests for anything that emails links.
php artisan config:cache bakes frontend.host into the cached config — run it on deploy, and remember queue:restart after changing hosts, or queued jobs keep old URLs.route(..., false) into an email, it silently points at the admin host. Add a test asserting str_starts_with($url, config('frontend.host')).route() only fails at render time inside a Blade loop — easy to miss until production. Fail fast: throw a descriptive exception in your generator when the mapped route isn't registered.{order} vs {id} across admin/frontend route definitions produce confusing UrlGenerationExceptions. Keep parameter names identical in the map, or normalize in the generator.url('/path') and a hardcoded host concat will double or drop slashes. Use rtrim/ltrim once, in one place.ASSET_URL config — don't conflate them.FrontendUrlGenerator) and bind it in a service provider. That's the one good habit from the bundle's DI design, and it makes swapping in a CDN host, locale-aware host, or a signed-URL variant a container change rather than a template rewrite.php artisan tinker → config('frontend.routes'), confirm the route exists with php artisan route:list --name=shop.products, and log the generated URL on first deploy of each new link type.How can I help you explore Laravel packages today?