<?php
namespace App\Controller;
use App\Entity\Page;
use App\Repository\PageRepository;
use App\Repository\BlockRepository;
use App\Repository\ServiceRepository;
use App\Repository\SettingRepository;
use App\Service\SiteOwnerResolver;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class PageController extends AbstractController
{
public function __construct(
private SettingRepository $settingRepository,
private ServiceRepository $serviceRepository,
private PageRepository $pageRepository,
private BlockRepository $blockRepository,
private SiteOwnerResolver $siteOwnerResolver,
) {}
#[Route('/', name: 'public_home')]
public function home(): Response
{
return $this->redirectToRoute('react_app', ['reactRouting' => 'accueil'], Response::HTTP_MOVED_PERMANENTLY);
}
#[Route(
'/{reactRouting}',
name: 'react_app',
requirements: [
'reactRouting' => '^(?!admin|login|logout|reset-password).*'
],
defaults: ['reactRouting' => null]
)]
public function index($reactRouting): Response
{
$page = $this->pageRepository->findPublishedBySlug($reactRouting, $this->siteOwnerResolver->getSiteOwner());
$initialData = $this->getInitialData();
if ($page) {
$initialData['page'] = [
'id' => $page->getId(),
'title' => $page->getTitle(),
'slug' => $page->getSlug(),
'seoTitle' => $page->getSeoTitle(),
'seoDescription' => $page->getSeoDescription(),
'blocks' => $this->buildBlocksData($page),
];
}
return $this->render('public/react_page.html.twig', [
'initialData' => $initialData,
]);
}
#[Route(
'/{slug}',
name: 'page_show',
requirements: [
'slug' => '^(?!admin|login|logout|reset-password|api).*'
]
)]
public function page(string $slug): Response
{
$page = $this->pageRepository->findPublishedBySlug($slug, $this->siteOwnerResolver->getSiteOwner());
if (!$page) {
throw $this->createNotFoundException('Page introuvable');
}
return $this->render('public/react_page.html.twig', [
'page' => $page,
'initialData' => array_merge($this->getInitialData()),
]);
}
/**
* Build initial data to inject into the HTML so React can render instantly
*/
private function getInitialData(): array
{
return [
'compagnie' => $this->BuildCompanyData()['company'],
'menu' => $this->buildMenuData(),
'footer' => $this->buildFooterData(),
'services' => $this->buildServicesData(),
'contact' => $this->buildContactData(),
];
}
private function buildMenuData(): array
{
$defaultItems = [
['id' => 1, 'name' => 'Accueil', 'href' => '/accueil', 'visible' => true, 'order' => 1],
['id' => 2, 'name' => 'Services', 'href' => '/services', 'visible' => true, 'order' => 2],
['id' => 3, 'name' => 'Secteurs', 'href' => '/secteurs', 'visible' => true, 'order' => 3],
['id' => 4, 'name' => 'Références', 'href' => '/references', 'visible' => true, 'order' => 4],
['id' => 5, 'name' => 'À propos', 'href' => '/a-propos', 'visible' => true, 'order' => 5],
['id' => 6, 'name' => 'FAQ', 'href' => '/faq', 'visible' => true, 'order' => 6],
['id' => 7, 'name' => 'Contact', 'href' => '/contact', 'visible' => true, 'order' => 7],
];
$defaultStyle = [
'position' => 'fixed',
'backgroundColor' => '#ffffff',
'textColor' => '#1f2937',
'hoverColor' => '#2563eb',
'logoText' => 'NovaCraft Cleaning',
'imgLogo' => '°',
'showPhone' => true,
'phoneNumber' => '01 84 25 36 78',
'showDevisButton' => true,
];
$menuItems = $this->settingRepository->get('menu_items', $defaultItems, $this->siteOwnerResolver->getSiteOwner());
$menuStyle = $this->settingRepository->get('menu_style', $defaultStyle, $this->siteOwnerResolver->getSiteOwner());
$companyName = $this->settingRepository->get('companyName', null, $this->siteOwnerResolver->getSiteOwner());
if ($companyName && empty($menuStyle['logoText'])) {
$menuStyle['logoText'] = $companyName;
}
$companyPhone = $this->settingRepository->get('companyPhone', null, $this->siteOwnerResolver->getSiteOwner());
if ($companyPhone && empty($menuStyle['phoneNumber'])) {
$menuStyle['phoneNumber'] = $companyPhone;
}
$companyLogo = $this->settingRepository->get('companyLogo', null, $this->siteOwnerResolver->getSiteOwner());
if ($companyPhone && empty($menuStyle['imgLogo'])) {
$menuStyle['imgLogo'] = $companyLogo;
}
$visibleItems = array_filter($menuItems, fn($item) => $item['visible'] ?? true);
usort($visibleItems, fn($a, $b) => ($a['order'] ?? 0) <=> ($b['order'] ?? 0));
return [
'items' => array_values($visibleItems),
'style' => $menuStyle,
];
}
private Function BuildCompanyData(): array
{
$companyKeys = [
'companyName', 'companySlogan', 'companyDescription', 'companyLogo',
'companyEmail', 'companyPhone', 'companyPhone2',
'companyAddress', 'companyCity', 'companyPostalCode', 'companyCountry',
'companyRegion', 'companyLatitude', 'companyLongitude',
'companyHours', 'companyOpeningHours',
'companySiret', 'companyVatNumber',
'companySeoDescription', 'companyKeywords', 'companyOgImage', 'serviceArea',
'facebook', 'instagram', 'linkedin', 'twitter', 'youtube'
];
$allSettings = $this->settingRepository->getAllAsArray($this->siteOwnerResolver->getSiteOwner());
$company = array_intersect_key($allSettings, array_flip($companyKeys));
return [
'company' => $company
];
}
private Function buildContactData(): array
{
$companyKeys = [
'companyName', 'companyLogo',
'companyEmail', 'companyPhone', 'companyPhone2',
'companyAddress', 'companyCity', 'companyPostalCode', 'companyCountry',
'companyRegion', 'companyLatitude', 'companyLongitude',
'companyHours', 'companyOpeningHours',
'companySiret', 'companyVatNumber'
];
$allSettings = $this->settingRepository->getAllAsArray($this->siteOwnerResolver->getSiteOwner());
$contact = array_intersect_key($allSettings, array_flip($companyKeys));
return [
'contact' => $contact
];
}
private function buildFooterData(): array
{
$companyKeys = [
'companyName', 'companySlogan', 'companyDescription', 'companyLogo',
'companyEmail', 'companyPhone', 'companyPhone2',
'companyAddress', 'companyCity', 'companyPostalCode', 'companyCountry',
'companyRegion', 'companyLatitude', 'companyLongitude',
'companyHours', 'companyOpeningHours',
'companySiret', 'companyVatNumber',
'companySeoDescription', 'companyKeywords', 'companyOgImage', 'serviceArea',
'facebook', 'instagram', 'linkedin', 'twitter', 'youtube'
];
$allSettings = $this->settingRepository->getAllAsArray($this->siteOwnerResolver->getSiteOwner());
$company = array_intersect_key($allSettings, array_flip($companyKeys));
$defaultFooterConfig = [
'showCta' => true,
'ctaTitle' => 'Besoin d\'un devis personnalisé ?',
'ctaDescription' => 'Contactez-nous dès aujourd\'hui pour obtenir une estimation gratuite et sans engagement.',
'ctaButtonText' => 'Demander un devis gratuit',
'ctaButtonLink' => '/devis',
'columns' => [
['id' => 1, 'title' => 'Nos services', 'type' => 'services', 'links' => []],
['id' => 2, 'title' => 'Entreprise', 'type' => 'custom', 'links' => [
['name' => 'À propos', 'href' => '/a-propos'],
['name' => 'FAQ', 'href' => '/faq'],
['name' => 'Contact', 'href' => '/contact']
]]
],
'showContact' => true,
'showHours' => true,
'legalLinks' => [
['name' => 'Mentions légales', 'href' => '/mentions-legales'],
['name' => 'Politique de confidentialité', 'href' => '/politique-confidentialite']
],
'copyrightText' => ''
];
$footerConfig = $this->settingRepository->get('footer', $defaultFooterConfig, $this->siteOwnerResolver->getSiteOwner());
$services = $this->serviceRepository->findAllPublished($this->siteOwnerResolver->getSiteOwner());
$pages = $this->pageRepository->findAllPublished($this->siteOwnerResolver->getSiteOwner());
$columns = [];
foreach ($footerConfig['columns'] ?? [] as $column) {
$processedColumn = [
'id' => $column['id'],
'title' => $column['title'],
'type' => $column['type'],
'links' => []
];
if ($column['type'] === 'services') {
$processedColumn['links'] = array_map(fn($s) => [
'name' => $s->getTitle(),
'href' => '/services/' . $s->getSlug()
], array_slice($services, 0, 6));
} elseif ($column['type'] === 'pages') {
$processedColumn['links'] = array_map(fn($p) => [
'name' => $p->getTitle(),
'href' => '/page/' . $p->getSlug()
], array_slice($pages, 0, 6));
} else {
$processedColumn['links'] = $column['links'] ?? [];
}
$columns[] = $processedColumn;
}
return [
'company' => $company,
'config' => [
'showCta' => $footerConfig['showCta'] ?? true,
'ctaTitle' => $footerConfig['ctaTitle'] ?? 'Besoin d\'un devis personnalisé ?',
'ctaDescription' => $footerConfig['ctaDescription'] ?? 'Contactez-nous dès aujourd\'hui pour obtenir une estimation gratuite.',
'ctaButtonText' => $footerConfig['ctaButtonText'] ?? 'Demander un devis gratuit',
'ctaButtonLink' => $footerConfig['ctaButtonLink'] ?? '/devis',
'showContact' => $footerConfig['showContact'] ?? true,
'showHours' => $footerConfig['showHours'] ?? true,
'legalLinks' => $footerConfig['legalLinks'] ?? [
['name' => 'Mentions légales', 'href' => '/mentions-legales'],
['name' => 'Politique de confidentialité', 'href' => '/politique-confidentialite']
],
'copyrightText' => $footerConfig['copyrightText'] ?? '',
'template' => $footerConfig['template'] ?? 'standard',
],
'columns' => $columns,
];
}
private function buildServicesData(): array
{
$services = $this->serviceRepository->findAllPublished($this->siteOwnerResolver->getSiteOwner());
return array_map(fn($s) => [
'id' => $s->getId(),
'title' => $s->getTitle(),
'slug' => $s->getSlug(),
'excerpt' => $s->getExcerpt(),
'image' => $s->getImage(),
], $services);
}
private function buildBlocksData(?Page $page = null): array
{
if (!$page) {
return [];
}
$blocks = $this->blockRepository->findVisibleByPage($page);
return array_map(function($block) {
$data = $block->getData() ?? [];
// Si le bloc a un BlockType, enrichir avec les métadonnées
$blockTypeData = [];
if ($block->getBlockType()) {
$blockType = $block->getBlockType();
$blockTypeData = [
'blockTypeName' => $blockType->getName(),
'blockTypeSlug' => $blockType->getSlug(),
'blockTypeCategory' => $blockType->getCategory(),
];
}
return [
'id' => $block->getId(),
'type' => $block->getType(),
'data' => $data,
'layoutWidth' => $block->getLayoutWidth() ?? 12,
'name' => $block->getName(),
'position' => $block->getPosition(),
...$blockTypeData
];
}, $blocks);
}
}