Parlons de votre projet
Partitech Sonata Extra Bundle

Sonata Extra Bundle : module de blog

Sonata Extra Bundle fournit un module de blog intégré à Sonata Page, avec des routes personnalisables et un rendu adapté aux sites multilingues. Intégration des routes principales Mise à jour des routes principales du blog Intégrez les pages de votre site ...

Contrôles de compatibilité Symfony et Sonata

Sonata Extra Bundle fournit un module de blog intégré à Sonata Page, avec des routes personnalisables et un rendu adapté aux sites multilingues.

Intégration des routes principales

Mise à jour des routes principales du blog

Intégrez les pages de votre site avec le contrôleur de blog en exécutant le update-core-route commande :

php bin/console sonata:page:update-core-routes --site={id}

L'exécution de cette commande synchronise la page Sonata avec les itinéraires hybrides suivants :

  • Catégorie de blog : /blog-category/{category}
  • Étiquette de blog : /blog-tag/{tag}
  • Article de blog : /blog-article/{article}

Ces routes correspondent à des pages générées dynamiquement qui sont liées à leurs contrôleurs respectifs.

Personnalisation des routes

Personnalisez les modèles de route par défaut pour localiser ou modifier la structure de votre URL tout en préservant le lien avec le contrôleur en modifiant les détails de la page :

  • Un écouteur d'événement sur routing.loader est ajouté automatiquement.
  • Une nouvelle route est créée avec votre modèle personnalisé à partir du champ custom_url.

Blog_UrlPersonnalisée.png

Reportez-vous au modèle suivant pour générer des chemins d'URL dans Twig :

{{  path('sonata_extra_blog_category', {'category': category.getSlug()}) }}
{{  path('sonata_extra_blog_tag', {'tag': tag.getSlug()}) }}
{{  path('sonata_extra_blog_article', {'article': article.getSlug()}) }}

Templates et rendu

Reliez la production de contenu de votre blog à la configuration de votre modèle en utilisant le Blog page type. Ce type de service de page est expliqué en détail dans le Documentation de SonataPageBundle.

Variables Twig disponibles

Le Blog page le service fournit les variables suivantes à votre modèle pour une intégration transparente :

Blog_ServiceType.png

  • content : Le contenu HTML de votre blog.
  • page : Le SonataPagePage instance d'entité.
  • site : Le SonataPageSite instance d'entité.

Affichez le contenu de votre blog dans votre modèle Twig comme suit :

{{ content|raw }}

Pour l'article de page, la catégorie et les tags, le service mettra également à disposition des paramètres SEO :

  • ogTitle
  • ogDescription
  • ogImage : Le chemin de l'image sera déjà calculé. Assurez-vous d'avoir un format d'image nommé og_image sinon reference sera utilisé. Comme les réseaux sociaux ont une largeur/hauteur maximale, il n'est pas recommandé d'utiliser le reference format.
  • seoTitle
  • seoKeyword
  • seoDescription

Dans votre modèle, vous pouvez l'utiliser ainsi

{% block sonata_seo_title %}
    {% apply spaceless %}
        <title>{{ seoTitle|default('Titre par défaut') }}</title>
    {% endapply %}
{% endblock %}

{% block sonata_seo_metadatas %}
    {% apply spaceless %}
        {% if seoDescription is defined %}
            <meta name="description" content="{{ seoDescription }}">
        {% endif %}
        {% if seoKeyword is defined %}
            <meta name="keywords" content="{{ seoKeyword }}">
        {% endif %}
    {% endapply %}
{% endblock %}

{% block app_og_metadatas %}
    {% apply spaceless %}
        {% if ogTitle is defined %}
            <meta property="og:title" content="{{ ogTitle }}">
        {% endif %}
        {% if ogDescription is defined %}
            <meta property="og:description" content="{{ ogDescription }}">
        {% endif %}
        {% if ogImage is  defined %}
            <meta property="og:image" content="{{ absolute_url(ogImage) }}">
        {% endif %}
        <meta property="og:type" content="article">
    {% endapply %}
{% endblock %}

et dans votre modèle de base

    {% block sonata_seo_title %}
        {{ sonata_seo_title() }}
    {% endblock %}
    {% block sonata_seo_metadatas %}
        {{ sonata_seo_metadatas() }}
    {% endblock %}
    {% block app_og_metadatas %}
        {{ app_og_metadatas() }}
    {% endblock %}

L'entité Catégorie et Étiquette devrait avoir les champs de référence

  • App\Entyty\SonataClassificationCategory.php :
<?php
namespace App\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Sonata\ClassificationBundle\Entity\BaseCategory;
use Partitech\SonataExtra\Traits\EntityTranslationTrait;
use Partitech\SonataExtra\Contract\MediaInterface;

#[ORM\Entity]
#[ORM\Table(name: 'classification__category')]
class SonataClassificationCategory extends BaseCategory
{
    use EntityTranslationTrait;
    #[ORM\Id]
    #[ORM\Column(type: Types::INTEGER)]
    #[ORM\GeneratedValue]
    protected ?int $id = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    #[ORM\ManyToOne(targetEntity: MediaInterface::class, cascade: ["persist"])]
    #[ORM\JoinColumn(name: "featured_image__media_id", referencedColumnName: "id", nullable: true, onDelete: "SET NULL")]
    private $featured_image = null;


    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $seo_title = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $seo_keywords = null;

    #[ORM\Column(type: 'text', nullable: true)]
    private ?string $seo_description = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $seo_og_title = null;

    #[ORM\Column(type: 'text', nullable: true)]
    private ?string $seo_og_description = null;

    #[ORM\ManyToOne(targetEntity: MediaInterface::class, cascade: ["persist"])]
    #[ORM\JoinColumn(name: "seo_og_image__media_id", referencedColumnName: "id", nullable: true, onDelete: "SET NULL")]
    private $seo_og_image = null;

    public function getFeaturedImage()
    {
        return $this->featured_image;
    }

    public function setFeaturedImage($featured_image): self
    {
        $this->featured_image = $featured_image;

        return $this;
    }

    public function getSeoTitle(): ?string {
        return $this->seo_title;
    }

    public function setSeoTitle(?string $seo_title): self {
        $this->seo_title = $seo_title;
        return $this;
    }

    public function getSeoKeywords(): ?string {
        return $this->seo_keywords;
    }

    public function setSeoKeywords(?string $seo_keywords): self {
        $this->seo_keywords = $seo_keywords;
        return $this;
    }

    public function getSeoDescription(): ?string {
        return $this->seo_description;
    }

    public function setSeoDescription(?string $seo_description): self {
        $this->seo_description = $seo_description;
        return $this;
    }

    public function getSeoOgTitle(): ?string {
        return $this->seo_og_title;
    }

    public function setSeoOgTitle(?string $seo_og_title): self {
        $this->seo_og_title = $seo_og_title;
        return $this;
    }

    public function getSeoOgDescription(): ?string {
        return $this->seo_og_description;
    }

    public function setSeoOgDescription(?string $seo_og_description): self {
        $this->seo_og_description = $seo_og_description;
        return $this;
    }

    public function getSeoOgImage() {
        return $this->seo_og_image;
    }

    public function setSeoOgImage($seo_og_image): self {
        $this->seo_og_image = $seo_og_image;
        return $this;
    }
}
  • App\Entyty\SonataClassificationTag :
<?php

declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\Types;
use App\Repository\SonataClassificationTagRepository;
use Sonata\ClassificationBundle\Entity\BaseTag;
use Partitech\SonataExtra\Traits\EntityTranslationTrait;
use Partitech\SonataExtra\Contract\MediaInterface;

#[ORM\Entity(repositoryClass: SonataClassificationTagRepository::class)]
#[ORM\Table(name: 'classification__tag')]
#[ORM\HasLifecycleCallbacks]

class SonataClassificationTag extends BaseTag
{
    use EntityTranslationTrait;

    #[ORM\Id]
    #[ORM\Column(type: Types::INTEGER)]
    #[ORM\GeneratedValue]
    protected ?int $id = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    #[ORM\ManyToOne(targetEntity: MediaInterface::class, cascade: ["persist"])]
    #[ORM\JoinColumn(name: "featured_image__media_id", referencedColumnName: "id", nullable: true, onDelete: "SET NULL")]
    private $featured_image = null;


    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $seo_title = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $seo_keywords = null;

    #[ORM\Column(type: 'text', nullable: true)]
    private ?string $seo_description = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private ?string $seo_og_title = null;

    #[ORM\Column(type: 'text', nullable: true)]
    private ?string $seo_og_description = null;

    #[ORM\ManyToOne(targetEntity: MediaInterface::class, cascade: ["persist"])]
    #[ORM\JoinColumn(name: "seo_og_image__media_id", referencedColumnName: "id", nullable: true, onDelete: "SET NULL")]
    private $seo_og_image = null;

    public function getFeaturedImage()
    {
        return $this->featured_image;
    }

    public function setFeaturedImage($featured_image): self
    {
        $this->featured_image = $featured_image;

        return $this;
    }

    public function getSeoTitle(): ?string {
        return $this->seo_title;
    }

    public function setSeoTitle(?string $seo_title): self {
        $this->seo_title = $seo_title;
        return $this;
    }

    public function getSeoKeywords(): ?string {
        return $this->seo_keywords;
    }

    public function setSeoKeywords(?string $seo_keywords): self {
        $this->seo_keywords = $seo_keywords;
        return $this;
    }

    public function getSeoDescription(): ?string {
        return $this->seo_description;
    }

    public function setSeoDescription(?string $seo_description): self {
        $this->seo_description = $seo_description;
        return $this;
    }

    public function getSeoOgTitle(): ?string {
        return $this->seo_og_title;
    }

    public function setSeoOgTitle(?string $seo_og_title): self {
        $this->seo_og_title = $seo_og_title;
        return $this;
    }

    public function getSeoOgDescription(): ?string {
        return $this->seo_og_description;
    }

    public function setSeoOgDescription(?string $seo_og_description): self {
        $this->seo_og_description = $seo_og_description;
        return $this;
    }

    public function getSeoOgImage() {
        return $this->seo_og_image;
    }

    public function setSeoOgImage($seo_og_image): self {
        $this->seo_og_image = $seo_og_image;
        return $this;
    }
}

Adapter l’exemple à la version installée

Les exemples Symfony et Sonata restent attachés aux versions indiquées dans l’article. Symfony 4.x et Symfony 6.2 ne sont plus maintenus : pour un projet actuel, utilisez une branche maintenue et la documentation correspondant exactement à vos dépendances.

php bin/console about
composer show symfony/framework-bundle sonata-project/admin-bundle

Avant de reprendre le code, contrôlez les signatures, les services, les routes et les templates concernés. Ajoutez ensuite un test fonctionnel couvrant les autorisations, le code HTTP et le résultat visible.

Référence : versions Symfony maintenues.

Partager cet article