1. Go to this page and download the library: Download ecourty/sitemap-bundle library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
// src/Repository/PostRepository.php
use Doctrine\ORM\QueryBuilder;
class PostRepository extends ServiceEntityRepository
{
public function getSitemapQueryBuilder(): QueryBuilder
{
return $this->createQueryBuilder('p')
->where('p.published = true')
->andWhere('p.deletedAt IS NULL')
->orderBy('p.updatedAt', 'DESC');
}
}
// src/Service/PostSitemapService.php
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
class PostSitemapService
{
public function __construct(
private EntityManagerInterface $em,
) {
}
public function getPublishedPostsQueryBuilder(): QueryBuilder
{
return $this->em->createQueryBuilder()
->select('p')
->from(Post::class, 'p')
->where('p.status = :published')
->setParameter('published', 'published')
->orderBy('p.publishedAt', 'DESC');
}
}
// src/Service/ProductUrlProvider.php
namespace App\Service;
use Doctrine\ORM\EntityManagerInterface;
use Ecourty\SitemapBundle\Contract\UrlProviderInterface;
use Ecourty\SitemapBundle\Enum\ChangeFrequency;
use Ecourty\SitemapBundle\Model\SitemapUrl;
use App\Entity\Product;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class ProductUrlProvider implements UrlProviderInterface
{
// Inject dependencies
public function getUrls(): iterable
{
// Join with Category to get both slugs in one query
$qb = $this->em->createQueryBuilder()
->select('p', 'c') // Select both product and category
->from(Product::class, 'p')
->innerJoin('p.category', 'c')
->where('p.active = true')
->andWhere('c.active = true')
->orderBy('p.updatedAt', 'DESC');
foreach ($qb->getQuery()->toIterable() as $product) {
// Generate URL with both category and product parameters
$path = $this->urlGenerator->generate('product_show', [
'categorySlug' => $product->getCategory()->getSlug(),
'productSlug' => $product->getSlug(),
]);
yield new SitemapUrl(
loc: rtrim($this->baseUrl, '/') . $path,
priority: 0.7,
changefreq: ChangeFrequency::WEEKLY,
lastmod: $product->getUpdatedAt(),
);
}
}
public function count(): int
{
// Implement the count efficiently
// $qb = $this->em->createQueryBuilder()
// ->select('COUNT(p.id)')
// ...
}
public function getSourceName(): string
{
return 'products';
}
}
// ✅ Automatic streaming - no memory issues with 100k+ entities
$query->toIterable();
// ❌ Would load all entities in memory at once
$query->getResult();
// ✅ Correct - return QueryBuilder
public function getSitemapQueryBuilder(): QueryBuilder
{
return $this->createQueryBuilder('p')
->where('p.published = true');
}
// ❌ Wrong - don't call getQuery() or toIterable()
public function getSitemapData(): iterable
{
return $this->createQueryBuilder('p')
->getQuery()
->toIterable(); // Bundle handles this automatically
}
// Repository method with filtering
public function getActiveProductsQueryBuilder(): QueryBuilder
{
return $this->createQueryBuilder('p')
->where('p.active = true')
->andWhere('p.stock > 0')
->orderBy('p.updatedAt', 'DESC');
}