currentDateTimeString = Carbon::now()->toDateTimeString(); } protected function getAppQueries(): array { return []; } protected function getAppStaticUrls(): array { return []; } public function generate(): void { $index = []; $queries = array_merge( [ app(CustomPage::class)->select(['id', 'title', 'slug']), ], $this->getAppQueries(), ); foreach ($queries as $query) { $resourceName = str_replace( '_', '-', $query->getModel()->getTable(), ); $index[$resourceName] = $this->createSitemapForResource( $query, $resourceName, ); } $this->makeStaticMap(); $this->makeIndex($index); } protected function createSitemapForResource( Builder $model, string $name ): int { $model ->orderBy('id') ->chunk($this->queryChunkSize, function ($records) use ($name) { $modelName = ucfirst(Str::snake($records->first()::MODEL_TYPE)); $methodName = "add{$modelName}Line"; foreach ($records as $record) { // allow extending class to override line adding for specific model if (method_exists($this, $methodName)) { $this->$methodName( $this->getModelUrl($record), $this->getModelUpdatedAt($record), $name, ); } else { $this->addNewLine( $this->getModelUrl($record), $this->getModelUpdatedAt($record), $name, ); } } }); if ($this->currentLineCount) { $this->save("$name-sitemap-{$this->currentResourceSitemapCount}"); } $numberOfSitemapsGenerated = $this->currentResourceSitemapCount; $this->currentResourceSitemapCount = 0; $this->currentLineCount = 0; return $numberOfSitemapsGenerated; } protected function addNewLine(string $url, string $updatedAt, string $name) { if (!$this->currentXml) { $this->startNewXmlFile(); } if ($this->currentLineCount === 50000) { $this->save("$name-sitemap-{$this->currentResourceSitemapCount}"); $this->startNewXmlFile(); } $updatedAt = $this->formatDate($updatedAt); $line = "\t" . "\n\t\t" . htmlspecialchars($url) . "\n\t\t" . $updatedAt . "\n\t\tweekly\n\t\t1.00\n\t\n"; $this->currentXml .= $line; $this->currentLineCount++; } protected function save(string $fileName) { $this->currentXml .= "\n"; File::ensureDirectoryExists(public_path('storage/sitemaps')); File::put( public_path("storage/sitemaps/$fileName.xml"), $this->currentXml, ); $this->currentXml = null; $this->currentLineCount = 0; $this->currentResourceSitemapCount++; } protected function startNewXmlFile() { $this->currentXml = '' . "\n" . '' . "\n"; } protected function makeStaticMap(): void { $urls = array_merge( ['', 'login', 'register', 'contact'], $this->getAppStaticUrls(), ); $urls = array_map(function ($data) { if (is_string($data)) { return [ 'path' => $data, 'updated_at' => $this->currentDateTimeString, ]; } else { return $data; } }, $urls); foreach ($urls as $url) { $this->addNewLine( url($url['path']), $url['updated_at'], 'static-urls', ); } $this->save('static-urls-sitemap'); } protected function makeIndex(array $index): void { $baseUrl = url('storage/sitemaps'); $string = '' . "\n" . '' . "\n"; foreach ($index as $resourceName => $resourceSitemapCount) { // sitemap count starts from 1 for ($i = 0; $i <= $resourceSitemapCount - 1; $i++) { $url = "{$baseUrl}/{$resourceName}-sitemap-$i.xml"; $string .= "\t\n" . "\t\t$url\n" . "\t\t{$this->formatDate()}\n" . "\t\n"; } } $string .= "\t\n\t\t{$baseUrl}/static-urls-sitemap.xml\n\t\t{$this->formatDate()}\n\t\n"; $string .= ''; File::put(public_path('storage/sitemaps/sitemap-index.xml'), $string); } protected function getModelUrl(Model $model): string { $resourceName = Str::camel(class_basename($model)); return app(AppUrlGenerator::class)->$resourceName($model); } protected function formatDate(string $date = null): string { if (!$date) { $date = $this->currentDateTimeString; } return date('Y-m-d\TH:i:sP', strtotime($date)); } protected function getModelUpdatedAt(Model $model): string { return !$model->updated_at || $model->updated_at === '0000-00-00 00:00:00' ? $this->currentDateTimeString : $model->updated_at; } }