rayzenai / url-manager
Laravel URL management package with Filament integration for SEO-friendly URLs, redirects, and sitemap generation
Requires
- php: ^8.3
- filament/filament: ^4.0
- google/apiclient: ^2.0
- illuminate/support: ^11.0|^12.0
- jenssegers/agent: ^2.6
- spatie/laravel-package-tools: ^1.16
- spatie/laravel-sitemap: ^7.0
README
A comprehensive Laravel package for managing URLs, redirects, and sitemaps with Filament admin panel integration.
Features
- π Dynamic URL Management - Manage all your application URLs from a central location
- π 301/302 Redirects - Create and manage URL redirects with configurable status codes
- πΊοΈ Automatic Sitemap Generation - Generate XML sitemaps with support for large sites
- π Visit Tracking - Track URL visits and analytics
- π¨ Filament Integration - Full-featured admin panel for URL management
- π·οΈ SEO Metadata - Manage meta tags and Open Graph data
- π Performance Optimized - Efficient database queries with proper indexing
- π Redirect Loop Protection - Automatic detection and prevention of circular redirects
Requirements
- PHP 8.2+
- Laravel 11.0+ or 12.0+
- Filament 4.0+
Installation
Step 1: Install via Composer
composer require rayzenai/url-manager
Step 2: Publish Configuration
php artisan vendor:publish --tag=url-manager-config
Step 3: Run Migrations
php artisan vendor:publish --tag=url-manager-migrations php artisan migrate
This will create two tables:
urls
- For managing URLs and redirectsgoogle_search_console_settings
- For storing Google Search Console credentials securely
Step 4: Register with Filament
Add the plugin to your Filament panel configuration (typically in app/Providers/Filament/AdminPanelProvider.php
):
use RayzenAI\UrlManager\UrlManagerPlugin; public function panel(Panel $panel): Panel { return $panel // ... other configuration ->plugin(UrlManagerPlugin::make()); }
Step 5: Configure Your Models
Add the HasUrl
trait to any model that needs URL management:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use RayzenAI\UrlManager\Traits\HasUrl; class Product extends Model { use HasUrl; protected $fillable = [ 'name', 'slug', 'description', 'is_active', // Required for URL visibility control // ... other fields ]; /** * Define the URL path for this model * Required by HasUrl trait */ public function webUrlPath(): string { return 'products/' . $this->slug; } /** * Define Open Graph tags for SEO * Optional but recommended */ public function ogTags(): array { return [ 'title' => $this->name, 'description' => $this->description, 'image' => $this->image_url, 'type' => 'product', ]; } /** * Define sitemap change frequency * Optional - defaults to 'weekly' */ public function getSitemapChangefreq(): string { return 'daily'; } }
Step 6: Register Routes (Optional)
If you want to use the package's URL handling, add this to your routes/web.php
:
// Add at the END of your routes file (must be last) Route::fallback([\RayzenAI\UrlManager\Http\Controllers\UrlController::class, 'handle']);
Usage
Creating URLs for Existing Models
Generate URLs for all models that use the HasUrl trait:
php artisan urls:generate
Or for a specific model:
php artisan urls:generate "App\Models\Product"
Creating Redirects
Via Filament Admin
- Navigate to the URLs section in your Filament admin panel
- Click "Create Redirect"
- Enter the source and destination URLs
- Choose redirect type (301 or 302)
Programmatically
use RayzenAI\UrlManager\Models\Url; // Create a permanent redirect Url::createRedirect('old-page', 'new-page', 301); // Create a temporary redirect Url::createRedirect('summer-sale', 'products/sale', 302);
Generating Sitemaps
Generate a sitemap with all active URLs:
php artisan sitemap:generate
For large sites (>10,000 URLs), the package automatically creates multiple sitemap files with an index.
Submitting Sitemaps to Search Engines
Since Google deprecated the ping endpoint in June 2023 and Bing has also discontinued their ping service, API credentials are now required for automated sitemap submission.
Setting up Google Search Console API
Prerequisites:
- A verified property in Google Search Console
- A Google Cloud Project with billing enabled (API has free tier)
Using Service Account Authentication
-
Create a Google Cloud Project:
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable these APIs:
- "Google Search Console API"
- "Search Console API" (if available)
- Note: The "Indexing API" is separate and not needed for sitemap submission
-
Create a Service Account:
- Navigate to "IAM & Admin" > "Service Accounts"
- Click "Create Service Account"
- Give it a name like "sitemap-submitter"
- Click "Create and Continue"
- Skip the optional role assignment (click "Continue")
- Click "Done"
- Find your new service account in the list and click on it
- Go to the "Keys" tab
- Click "Add Key" > "Create New Key"
- Select "JSON" format
- Click "Create" to download the JSON credentials file
- Keep this file secure - it contains credentials for API access
-
Add Service Account to Search Console:
- Go to Google Search Console
- Select your property
- Go to Settings > Users and permissions
- Click "Add user"
- Enter the service account email (found in the JSON file, looks like
service-account@project.iam.gserviceaccount.com
) - Select "Owner" permission level
- Click "Add"
-
Configure in Admin Panel:
- Navigate to the Google Search Console settings page in your Filament admin panel
- Toggle "Enable Google Search Console Integration" to ON
- Enter your site URL or domain property:
- For URL-prefix property:
https://www.yoursite.com
(must match exactly) - For Domain property:
sc-domain:yoursite.com
(recommended - covers all subdomains and protocols)
- For URL-prefix property:
- Add your Service Account credentials:
- Open your downloaded JSON file in a text editor
- Copy the entire JSON content
- Paste it into the "Service Account JSON" field
- The service account email will be automatically extracted
- Click "Save Settings"
- Use "Test Connection" to verify the setup is working
Why Database Storage?
- Credentials are encrypted and stored securely in your database
- Survives deployments (no need to re-upload files)
- No file system dependencies
- Easier to manage in production environments
Submitting Sitemaps
Once configured, you can submit sitemaps in multiple ways:
-
Via Admin Panel:
- Go to URLs management page in Filament
- Click "Submit to Search Engines" button
-
Via Command Line:
php artisan sitemap:submit
-
Programmatically:
use RayzenAI\UrlManager\Services\GoogleSearchConsoleService; // Submit to Google only $result = GoogleSearchConsoleService::submitGoogleSitemap(); // Submit to all search engines (Google + Bing note) $result = GoogleSearchConsoleService::submitToAllSearchEngines();
Troubleshooting Google Search Console
Service Account Issues:
- "API not configured" error: Ensure you've enabled the Google Search Console API in your Google Cloud Project
- "Site not verified" error: Make sure the service account email is added as a user in Search Console with "Owner" permissions
- "Invalid credentials" error: Check that the JSON file path is correct and the file is readable by the web server
- 403 Forbidden errors:
- The service account may not have proper permissions. Verify it's added to Search Console with "Owner" role
- Check if you're using the correct property format. If you have a domain property in Search Console, use
sc-domain:yoursite.com
format - Verify the exact property format in Search Console matches what you've configured
- Invalid JSON error: Ensure you're copying the complete JSON content from the credentials file, including all brackets
General Issues:
- "Invalid site URL" error: The site URL must match exactly with how it's registered in Search Console (including www/non-www, https/http)
- No sitemaps found: The site may not have any sitemaps submitted yet. Use "Submit Sitemap Now" button to submit
- Rate limiting: Google Search Console API has quotas. Check your Google Cloud Console for usage limits
- Connection test passes but submission fails: Check that sitemap.xml exists and is accessible at the expected URL
Note on Bing
Bing has also discontinued their ping endpoint. Sitemaps must be manually submitted through Bing Webmaster Tools.
Accessing URLs in Your Application
// Get the full URL for a model $product = Product::find(1); echo $product->webUrl(); // https://yoursite.com/products/my-product // Get the admin URL echo $product->adminUrl(); // /admin/products/1/edit // Check if a model's URL is active if ($product->url && $product->url->status === 'active') { // URL is active }
Visit Tracking
The package provides two ways to track visits:
Method 1: Using Middleware (Recommended for Livewire & API Routes)
Register the middleware in your application:
For Laravel 11 - Add to bootstrap/app.php
:
->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'track-url-visits' => \RayzenAI\UrlManager\Http\Middleware\TrackUrlVisits::class, ]); })
For Laravel 10 and below - Add to app/Http/Kernel.php
:
protected $middlewareAliases = [ // ... 'track-url-visits' => \RayzenAI\UrlManager\Http\Middleware\TrackUrlVisits::class, ];
Then apply the middleware to your routes:
// For Livewire components Route::get('/property/{slug}', PropertyDetails::class) ->middleware('track-url-visits') ->name('property'); // For API routes Route::middleware(['track-url-visits'])->group(function () { Route::get('/api/products/{slug}', [ProductController::class, 'show']); Route::get('/api/categories/{slug}', [CategoryController::class, 'show']); }); // Works with any route type - controllers, closures, Livewire, Inertia, etc. Route::middleware(['auth', 'track-url-visits'])->group(function () { Route::get('/dashboard', Dashboard::class); Route::get('/profile', [ProfileController::class, 'show']); });
The middleware automatically:
- Matches the request path against URLs in the database
- Records visits asynchronously via queued jobs
- Captures IP address, user agent, referrer, and authenticated user ID
- Works transparently without modifying your controllers or components
Method 2: Using Fallback Route
If you use the package's fallback route controller:
// Add at the END of your routes/web.php Route::fallback([\RayzenAI\UrlManager\Http\Controllers\UrlController::class, 'handle']);
Visits are automatically tracked for any URL managed by the package.
Accessing Visit Data
$url = $product->url; echo $url->visits; // Total visits echo $url->last_visited_at; // Last visit timestamp
Configuration
The configuration file config/url-manager.php
allows you to customize:
return [ // Database table name 'table_name' => 'urls', // URL types available in your application 'types' => [ 'product' => 'Product', 'category' => 'Category', 'page' => 'Page', // Add your custom types ], // Maximum redirect chain depth (prevents infinite loops) 'max_redirect_depth' => 5, // Visit tracking 'track_visits' => true, 'visit_queue' => 'low', // Queue for visit tracking jobs // Sitemap configuration 'sitemap' => [ 'enabled' => true, 'path' => public_path('sitemap.xml'), 'max_urls_per_file' => 10000, 'default_changefreq' => 'weekly', 'default_priority' => 0.5, 'priorities' => [ 'product' => 0.8, 'category' => 0.9, 'page' => 0.6, ], ], // Filament admin panel 'filament' => [ 'enabled' => true, 'navigation_group' => 'System', 'navigation_icon' => 'heroicon-o-link', 'navigation_sort' => 100, ], ];
Advanced Features
Custom URL Types
Register custom URL types in your configuration:
'types' => [ 'product' => 'Product', 'article' => 'Article', 'custom_type' => 'Custom Type', ],
SEO Metadata
Models can provide SEO metadata through the getSeoMetadata()
method:
public function getSeoMetadata(): array { return [ 'title' => $this->seo_title ?? $this->name, 'description' => $this->seo_description ?? $this->description, 'keywords' => $this->seo_keywords, 'og_image' => $this->featured_image, 'og_type' => 'article', 'twitter_card' => 'summary_large_image', ]; }
Event Handling
Listen for URL events in your application:
// In a service provider or event listener Event::listen('url-manager.url.visited', function ($url, $model) { // Log visit, send analytics, etc. Log::info("URL visited: {$url->slug}"); });
Multiple Sitemap Support
For sites with more than 10,000 URLs, the package automatically generates multiple sitemap files:
sitemap.xml (index file)
sitemap-0.xml (first 10,000 URLs)
sitemap-1.xml (next 10,000 URLs)
...
Filament Admin Panel
The package includes a complete Filament resource with:
- URL Listing - Search, filter, and sort URLs
- Create/Edit Forms - Manage URL details and metadata
- Redirect Creation - Quick action to create 301/302 redirects
- Sitemap Generation - Generate and view sitemaps
- Bulk Actions - Activate/deactivate multiple URLs
- Visit Statistics - View visit counts and last visited times
Dashboard Widgets
The package provides two dashboard widgets:
- URL Stats Overview - Displays total URLs, redirects, and visit statistics
- Top URLs Table - Shows the 10 most visited URLs with their metrics
Best Practices
- Always include
is_active
field in models using HasUrl trait - Implement
webUrlPath()
method to define URL structure - Use meaningful slugs for SEO optimization
- Set up redirects when changing URL structures
- Generate sitemaps regularly (via cron job)
- Monitor redirect chains to avoid deep nesting
- Use appropriate HTTP status codes (301 for permanent, 302 for temporary)
Testing
Run the package tests:
composer test
Troubleshooting
URLs not generating for models
Ensure your model:
- Uses the
HasUrl
trait - Has an
is_active
field - Implements the
webUrlPath()
method
Sitemap not accessible
Check that:
- Sitemap generation is enabled in config
- The public directory is writable
- Routes are properly registered
Redirects not working
Verify:
- The fallback route is registered last in
routes/web.php
- No other routes are conflicting
- Redirect depth limit hasn't been exceeded
Contributing
Contributions are welcome! Please submit pull requests with tests.
License
MIT License. See LICENSE file for details.
Support
For issues and questions, please use the GitHub issue tracker.
Credits
Created by Kiran Timsina at RayzenAI.