Skip to content

Commit

Permalink
Generate command (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinmeijer97 authored Dec 6, 2024
1 parent 59e7932 commit f82c36e
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 58 deletions.
47 changes: 41 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,50 @@ 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:
```
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.
Expand All @@ -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.
26 changes: 26 additions & 0 deletions config/sitemap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

return [
/*
|--------------------------------------------------------------------------
| Sitemap Storage Settings
|--------------------------------------------------------------------------
|
| Configure where the sitemap files should be stored. The disk should be
| configured in your application's filesystems.php config file.
| For public access, use the 'public' disk.
|
*/
'disk' => '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',
];
Empty file removed resources/views/.gitkeep
Empty file.
11 changes: 0 additions & 11 deletions resources/views/sitemaps/index.blade.php

This file was deleted.

2 changes: 1 addition & 1 deletion routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
51 changes: 51 additions & 0 deletions src/Actions/GenerateSitemapAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace Rapidez\Sitemap\Actions;

class GenerateSitemapAction
{
public function createSitemapIndex(array $sitemapIndexes): string
{
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';

foreach ($sitemapIndexes as $sitemapIndex) {
$sitemap .= $this->addUrl($sitemapIndex['loc'] ?? null, $sitemapIndex['lastmod'] ?? null);
}

$sitemap .= '</sitemapindex>';

return $sitemap;
}

public function createSitemapUrlset(array $sitemapUrls): string
{
$sitemap = '<?xml version="1.0" encoding="UTF-8"?>';
$sitemap .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">';

foreach ($sitemapUrls as $sitemapUrl) {
$sitemap .= $this->addUrl($sitemapUrl['loc'] ?? null, $sitemapUrl['lastmod'] ?? null, false);
}

$sitemap .= '</urlset>';

return $sitemap;
}

public function addUrl(?string $url = null, ?string $lastMod = null, bool $isIndex = true): string
{
$sitemap = $isIndex ? '<sitemap>' : '<url>';

if ($url) {
$sitemap .= '<loc>'.url($url).'</loc>';
}

if ($lastMod) {
$sitemap .= '<lastmod>'.substr($lastMod, 0, 10).'</lastmod>';
}

$sitemap .= $isIndex ? '</sitemap>' : '</url>';

return $sitemap;
}
}
26 changes: 26 additions & 0 deletions src/Commands/GenerateSitemap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Rapidez\Sitemap\Commands;

use Illuminate\Console\Command;
use Rapidez\Core\Facades\Rapidez;
use Rapidez\Sitemap\Jobs\GenerateSitemapJob;
use TorMorten\Eventy\Facades\Eventy;

class GenerateSitemap extends Command
{
protected $signature = 'rapidez:sitemap:generate';

protected $description = 'Generate the sitemap and run sitemap generate action';

public function handle(): int
{
Eventy::action('rapidez.sitemap.generate');

foreach (Rapidez::getStores() ?: [] as $store) {
GenerateSitemapJob::dispatch($store['store_id']);
}

return Command::SUCCESS;
}
}
21 changes: 11 additions & 10 deletions src/Http/Controllers/SitemapController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
namespace Rapidez\Sitemap\Http\Controllers;

use Illuminate\Http\Response;
use Rapidez\Sitemap\Models\Sitemap;
use TorMorten\Eventy\Facades\Eventy;
use Illuminate\Support\Facades\Storage;

class SitemapController
{
public function index(): Response
public function __invoke(): Response
{
// Retrieve cached sitemaps for the current store
$sitemaps = Sitemap::getCachedByStoreId();
$storeId = config('rapidez.store');
$disk = Storage::disk(config('rapidez-sitemap.disk', 'public'));
$path = trim(config('rapidez-sitemap.path', 'rapidez-sitemaps'), '/');

// Allow additional sitemaps to be added via the Eventy filter
$sitemaps = Eventy::filter('rapidez.sitemap.'.config('rapidez.store'), $sitemaps); // @phpstan-ignore-line
$sitemapPath = $path.'/'.$storeId.'/sitemap.xml';

// Return XML response
return response()
->view('rapidez-sitemap::sitemaps.index', compact('sitemaps'))
if (! $disk->exists($sitemapPath)) {
abort(404);
}

return response($disk->get($sitemapPath))
->header('Content-Type', 'application/xml');
}
}
49 changes: 49 additions & 0 deletions src/Jobs/GenerateSitemapJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Rapidez\Sitemap\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Rapidez\Core\Facades\Rapidez;
use Rapidez\Sitemap\Actions\GenerateSitemapAction;
use Rapidez\Sitemap\Models\Sitemap;
use TorMorten\Eventy\Facades\Eventy;

class GenerateSitemapJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;

public function __construct(protected int $storeId) {}

public function handle(GenerateSitemapAction $action): void
{
Rapidez::setStore($this->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);
}
}
13 changes: 4 additions & 9 deletions src/Models/Sitemap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
35 changes: 14 additions & 21 deletions src/SitemapServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'
);
}
}

0 comments on commit f82c36e

Please sign in to comment.