diff --git a/README.md b/README.md index 2be56fa..0c3258c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,19 @@ Rapidez sitemap index through Eventy filters composer require rapidez/sitemap ``` +## Configuration + +You can publish the configuration file with: +```bash +php artisan vendor:publish --tag=config --provider="Rapidez\Sitemap\SitemapServiceProvider" +``` + +This will create a `config/rapidez-sitemap.php` file where you can configure: +- `disk`: The Laravel filesystem disk to use (defaults to 'public') +- `path`: The path within the disk where sitemaps will be stored (defaults to root) + +Make sure the specified disk is properly configured in your application's `config/filesystems.php`. + ## Views You can publish the views with: @@ -15,6 +28,30 @@ You can publish the views with: php artisan vendor:publish --tag=rapidez-sitemap-views ``` +## Sitemap Generation + +To generate the sitemap manually, use: +```bash +php artisan rapidez:sitemap:generate +``` + +This command will generate `sitemap.xml` for the default store and `sitemap_{store_id}.xml` for additional stores in your configured storage location. + +If you'd like to schedule the sitemap generation you can add the `rapidez:sitemap:generate` command in `routes/console.php`, for more information see [Task Scheduling](https://laravel.com/docs/11.x/scheduling) + +```php +Schedule::command('rapidez:sitemap:generate')->twiceDaily(0, 12); +``` + +## Hooking into the Generation Action + +When the `rapidez:sitemap:generate` command runs, an Eventy action `rapidez.sitemap.generate` is triggered. + +You can hook into this generation process by adding an action in the `boot` method of your service provider: +```php +Eventy::addAction('rapidez.sitemap.generate', fn() => DoMagicHere(), 20, 1); +``` + ## Adding Additional Sitemap Indexes with Eventy If you have additional indexes, such as CMS pages or other custom routes, you can dynamically add them to your sitemap index based on the Store ID using the `rapidez.site.{storeId}` filter provided by Eventy. @@ -27,16 +64,14 @@ use TorMorten\Eventy\Facades\Eventy; Eventy::addFilter('rapidez.sitemap.{storeId}', function ($sitemaps) { // Add your custom sitemap URL here $sitemaps[] = [ - 'loc' => url('/some-dynamic-url.xml'), - 'lastmod' => now()->toDateTimeString(), + 'url' => 'your-custom-sitemap.xml', + 'lastmod' => now()->toDateString(), ]; - + return $sitemaps; -}); +}, 20, 1); ``` -With this filter in place, the URL `/some-dynamic-url.xml` will be added to your sitemap index, allowing you to dynamically include additional sections of your site, such as CMS-generated pages, product categories, or other custom data sources. - ## License GNU General Public License v3. Please see [License File](LICENSE) for more information. diff --git a/config/sitemap.php b/config/sitemap.php new file mode 100644 index 0000000..1bf49b0 --- /dev/null +++ b/config/sitemap.php @@ -0,0 +1,26 @@ + 'public', + + /* + |-------------------------------------------------------------------------- + | Sitemap Base Path + |-------------------------------------------------------------------------- + | + | The base path where sitemap files will be stored on the specified disk. + | Store-specific sitemaps will be stored in {path}/{store_id}/sitemap.xml + | + */ + 'path' => 'rapidez-sitemaps', +]; diff --git a/resources/views/.gitkeep b/resources/views/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/resources/views/sitemaps/index.blade.php b/resources/views/sitemaps/index.blade.php deleted file mode 100644 index 3912ae5..0000000 --- a/resources/views/sitemaps/index.blade.php +++ /dev/null @@ -1,11 +0,0 @@ - - - @foreach ($sitemaps ?? [] as $sitemap) - @if(($sitemap['loc'] ?? false) && ($sitemap['lastmod'] ?? false)) - - {{ $sitemap['loc'] }} - {{ $sitemap['lastmod'] }} - - @endif - @endforeach - diff --git a/routes/web.php b/routes/web.php index fd32816..a6faeec 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,4 +3,4 @@ use Illuminate\Support\Facades\Route; use Rapidez\Sitemap\Http\Controllers\SitemapController; -Route::get('/sitemap.xml', [SitemapController::class, 'index'])->name('sitemap.index'); +Route::get('sitemap.xml', SitemapController::class); diff --git a/src/Actions/GenerateSitemapAction.php b/src/Actions/GenerateSitemapAction.php new file mode 100644 index 0000000..5aef8ce --- /dev/null +++ b/src/Actions/GenerateSitemapAction.php @@ -0,0 +1,51 @@ +'; + $sitemap .= ''; + + foreach ($sitemapIndexes as $sitemapIndex) { + $sitemap .= $this->addUrl($sitemapIndex['loc'] ?? null, $sitemapIndex['lastmod'] ?? null); + } + + $sitemap .= ''; + + return $sitemap; + } + + public function createSitemapUrlset(array $sitemapUrls): string + { + $sitemap = ''; + $sitemap .= ''; + + foreach ($sitemapUrls as $sitemapUrl) { + $sitemap .= $this->addUrl($sitemapUrl['loc'] ?? null, $sitemapUrl['lastmod'] ?? null, false); + } + + $sitemap .= ''; + + return $sitemap; + } + + public function addUrl(?string $url = null, ?string $lastMod = null, bool $isIndex = true): string + { + $sitemap = $isIndex ? '' : ''; + + if ($url) { + $sitemap .= ''.url($url).''; + } + + if ($lastMod) { + $sitemap .= ''.substr($lastMod, 0, 10).''; + } + + $sitemap .= $isIndex ? '' : ''; + + return $sitemap; + } +} diff --git a/src/Commands/GenerateSitemap.php b/src/Commands/GenerateSitemap.php new file mode 100644 index 0000000..473117a --- /dev/null +++ b/src/Commands/GenerateSitemap.php @@ -0,0 +1,26 @@ +view('rapidez-sitemap::sitemaps.index', compact('sitemaps')) + if (! $disk->exists($sitemapPath)) { + abort(404); + } + + return response($disk->get($sitemapPath)) ->header('Content-Type', 'application/xml'); } } diff --git a/src/Jobs/GenerateSitemapJob.php b/src/Jobs/GenerateSitemapJob.php new file mode 100644 index 0000000..6b01e63 --- /dev/null +++ b/src/Jobs/GenerateSitemapJob.php @@ -0,0 +1,49 @@ +storeId); + + // Get sitemaps for the store + $sitemaps = Sitemap::getByStoreId($this->storeId); + + // Allow additional sitemaps via Eventy filter + /** @phpstan-ignore-next-line */ + $sitemaps = Eventy::filter('rapidez.sitemap.'.$this->storeId, $sitemaps); + + // Generate sitemap content + $sitemapContent = $action->createSitemapIndex((array) $sitemaps); + + // Get storage disk and path from config + $disk = Storage::disk(config('rapidez-sitemap.disk', 'public')); + $basePath = trim(config('rapidez-sitemap.path', 'rapidez-sitemaps'), '/'); + + // Generate store-specific path + $storePath = $basePath.'/'.$this->storeId; + + // Ensure directory exists and save sitemap + $disk->put($storePath.'/sitemap.xml', $sitemapContent); + + // Clear cache + Cache::forget('rapidez.sitemaps.'.$this->storeId); + } +} diff --git a/src/Models/Sitemap.php b/src/Models/Sitemap.php index bbb2cbf..7178941 100644 --- a/src/Models/Sitemap.php +++ b/src/Models/Sitemap.php @@ -3,7 +3,6 @@ namespace Rapidez\Sitemap\Models; use Exception; -use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; use Rapidez\Core\Models\Model; use Rapidez\Core\Models\Scopes\ForCurrentStoreWithoutLimitScope; @@ -21,15 +20,11 @@ protected static function booting() static::addGlobalScope(new ForCurrentStoreWithoutLimitScope('sitemap_id')); } - public static function getCachedByStoreId(): ?array + public static function getByStoreId(int $storeId): ?array { - $cacheKey = 'sitemaps.'.config('rapidez.store'); - - return Cache::remember($cacheKey, now()->addDay(), function () { - return self::get() - ->flatMap(fn ($sitemap) => self::getSitemapsFromIndex($sitemap->toArray())) - ->toArray(); - }); + return self::get() + ->flatMap(fn ($sitemap) => self::getSitemapsFromIndex($sitemap->toArray())) + ->toArray(); } protected static function getSitemapsFromIndex(array $sitemap): array diff --git a/src/SitemapServiceProvider.php b/src/SitemapServiceProvider.php index 598c5ef..4fc51c0 100644 --- a/src/SitemapServiceProvider.php +++ b/src/SitemapServiceProvider.php @@ -3,37 +3,30 @@ namespace Rapidez\Sitemap; use Illuminate\Support\ServiceProvider; +use Rapidez\Sitemap\Commands\GenerateSitemap; class SitemapServiceProvider extends ServiceProvider { public function boot(): void { - $this - ->bootRoutes() - ->bootViews() - ->bootPublishables(); - } - - public function bootRoutes(): self - { - $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); + $this->publishes([ + __DIR__.'/../config/sitemap.php' => config_path('rapidez-sitemap.php'), + ], 'config'); - return $this; - } + if ($this->app->runningInConsole()) { + $this->commands([ + GenerateSitemap::class, + ]); + } - public function bootViews(): self - { + $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); $this->loadViewsFrom(__DIR__.'/../resources/views', 'rapidez-sitemap'); - - return $this; } - public function bootPublishables(): self + public function register(): void { - $this->publishes([ - __DIR__.'/../resources/views' => resource_path('views/vendor/rapidez-sitemap'), - ], 'rapidez-sitemap-views'); - - return $this; + $this->mergeConfigFrom( + __DIR__.'/../config/sitemap.php', 'rapidez-sitemap' + ); } }