Accéder au contenu principal

17min.

JoliMediaBundle, un nouveau bundle de médias pour vos projets Symfony

C’est une petite histoire qui commence au début de l’année lorsque, en relisant un article que nous sommes sur le point de publier sur le blog de JoliCode, je remarque une image dont la qualité graphique est plus que douteuse.

Comparaison d'une image source et de sa version compressée par l'ancien processeur de médias du site de JoliCode

« — Quel dommage d’illustrer cet article avec un schéma aux couleurs délavées et aux traits pas franchement nets », voilà ce que je me dis à ce moment-là. Direction l’interface d’administration du site, pour remplacer cette image par une version de meilleure qualité. Et là, surprise : l’image source, employée pour illustrer l’article, est parfaitement nette et éclatante !

C’est le début d’une longue quête qui, de fil en aiguille, a conduit à l’écriture d’un nouveau bundle Symfony de gestion de médias : JoliMediaBundle.

Section intitulée c-est-quoi-exactement-un-gestionnaire-de-mediasC’est quoi exactement, un gestionnaire de médias ?

Au fil des nombreux projets Symfony que nous avons menés au cours des 15 dernières années, nous avons fréquemment été confrontés à la nécessité de mettre en place une médiathèque, un explorateur de fichiers, des fonctionnalités d’upload, ou des problématiques de ce type.

Ce type de besoins apparaît régulièrement dans les projets que nous menons, qu’il s’agisse de sites vitrines, de plateformes eCommerce, ou d’applications métier. En effet, la gestion de contenus multimédias est une composante essentielle de nombreuses applications Web modernes : si mon site présente des articles, il y a de bonnes chances pour que j’aie envie de les illustrer avec des images. Si mon site est une boutique qui vend des produits, il faudra des visuels pour les présenter, etc.

Plus précisément, dans notre cas, la notion de gestion de médias recouvre différents aspects : l’upload, le stockage, l’organisation, la transformation (redimensionnement, recadrage), l’optimisation (compression, changement de format), la diffusion, la sélection et l’association à des entités métier, l’affichage (dans des pages Web), etc.

Passons ces différents aspects en revue :

  • l’upload de médias consiste à proposer aux utilisateurs une expérience agréable pour envoyer des fichiers depuis leur ordinateur ou leur appareil mobile vers le serveur. Cela peut inclure des fonctionnalités telles que le glisser-déposer, la sélection multiple de fichiers, le suivi de la progression de l’upload, et la validation des types et tailles de fichiers ;
  • le stockage des médias implique de décider où et comment les fichiers seront conservés. Cela peut être sur le système de fichiers du serveur ou via un service de stockage en ligne (comme AWS S3, Google Cloud Storage, etc.). Il est important que l’approche choisie soit scalable, sécurisée, et adaptée aux besoins du projet. Par exemple, si le projet doit gérer un très grand volume de médias, le SSD d’un VPS ne sera peut-être pas la solution adaptée. À l’inverse, pour un petit site vitrine, un stockage local peut être suffisant et il est sans doute inutile de complexifier le projet en intégrant un service de stockage en ligne ;
  • l’organisation des médias est cruciale pour permettre aux utilisateurs de retrouver facilement les fichiers dont ils ont besoin. Cela peut inclure la création de dossiers et sous-dossiers, la mise en place d’un système de recherche, voire l’ajout de métadonnées aux médias. L’objectif est de rendre la médiathèque intuitive et efficace, et d’éviter que, par dépit, les utilisateurs ne finissent par stocker tous leurs fichiers dans un seul et même dossier racine (ou qu’ils les perdent dans une arborescence trop complexe et finissent par uploader 10 fois le même fichier) ;
  • la transformation des médias est souvent nécessaire pour adapter les fichiers aux besoins spécifiques du projet. Par exemple, il est certainement nécessaire de redimensionner automatiquement les images pour qu’elles s’affichent correctement sur différents appareils (desktop, tablette, mobile). Si l’administrateur fournit des photos en très haute résolution pour les produits de son catalogue, il est probable qu’il faille les recadrer ou les redimensionner avant de les afficher sur le site ;
  • l’optimisation des médias est également importante pour garantir des temps de chargement rapides et une bonne expérience utilisateur. Cela peut inclure la compression des médias pour réduire leur taille (par exemple, en employant des réglages graphiques un peu plus agressifs), et leur conversion dans des formats plus performants (comme WebP pour les images) ;
  • la diffusion des médias consiste à rendre les fichiers accessibles aux utilisateurs finaux. Sans forcément aborder la notion de CDN, le rôle d’une brique logicielle de gestion de médias est de fournir des URLs lisibles (c’est-à-dire compréhensibles, comprenant le nom du fichier, etc.) et stables (ie., qui ne changent pas à chaque déploiement) pour accéder aux médias ;
  • la sélection des médias est une fonctionnalité clé pour permettre aux utilisateurs d’associer facilement des fichiers aux contenus qu’ils créent. Par exemple, lorsqu’un administrateur rédige un article de blog, il doit pouvoir sélectionner une image d’illustration depuis la médiathèque, plutôt que d’avoir à uploader un nouveau fichier à chaque fois ;
  • l’affichage des médias dans les pages Web est l’ultime étape du processus. Il s’agit de s’assurer que les fichiers sont correctement intégrés dans les pages HTML, en employant les bonnes balises, les attributs appropriés (comme les attributs srcset pour les images responsives), et en veillant à ce que les médias s’affichent correctement sur tous les appareils.

Cela fait beaucoup de choses, n’est-est-ce pas ? 😅 En réalité, la réalisation de projets Web « bien faits » passe systématiquement par la prise en compte de tous ces petits détails, et de 1000 autres choses ! Capitaliser la connaissance que nous avons accumulée, en lien avec la question de la gestion des médias, répond donc à un enjeu important de qualité et de productivité : si nous avions une solution standard de gestion des médias à disposition, nous pourrions en faire profiter l’ensemble de nos projets.

Section intitulée et-pourquoi-vous-n-utilisez-pas-directement-un-cmsEt pourquoi vous n’utilisez pas directement un CMS ?

Bonne question ! En effet, les CMS (Content Management Systems) tels que Drupal, WordPress, ou Joomla, intègrent généralement des fonctionnalités de gestion de contenus et de médias. Alors, pourquoi ne pas simplement utiliser un CMS pour gérer les médias dans nos projets Symfony ? Ou bien, autre approche, pourquoi ne pas écarter la problématique de la gestion de médias, en employant systématiquement un service tiers qui serait dédié à cette problématique (par exemple un SaaS spécialisé en gestion de médias) ?

Il y a plusieurs raisons à cela.

D’abord, un CMS est conçu pour gérer l’ensemble du cycle de vie des contenus, et pas seulement la gestion des médias. Cela signifie que l’utilisation d’un CMS pour gérer les médias dans un projet Symfony peut introduire une complexité inutile, et potentiellement des limitations en termes de personnalisation et d’intégration avec les fonctionnalités métier spécifiques du projet. Concrètement, intégrer un CMS du marché avec une application sur mesure, c’est faisable, mais cela demande souvent un effort considérable pour assurer une intégration fluide entre les deux systèmes – ce que l’on pourrait qualifier de « montage frankensteinien ».

Le choix entre une solution intégrée (un CMS) et une solution sur mesure (comme JoliMediaBundle) n’est pas un sujet tout neuf : il s’agit d’un débat qui existe depuis longtemps dans le monde du développement Web. Cependant, dans le contexte spécifique des projets Symfony que nous menons, nous avons constaté que la flexibilité et la personnalisation offertes par une solution sur mesure sont souvent plus adaptées aux besoins de nos clients.

Souvent, les besoins directement liés à la gestion des médias sont relativement simples, et peuvent être implémentés rapidement et efficacement au sein de l’application Symfony elle-même, sans avoir à recourir à un CMS complet. Symfony propose depuis longtemps des bundles très productifs pour la création rapide d’interfaces d’administration – tels que EasyAdmin ou SonataAdmin – qui sont de réels accélérateurs de productivité pour mettre en place rapidement des interfaces de contribution personnalisées et directement intégrées à l’application. Dans ces conditions, l’ajout d’un CMS peut s’avérer être une surcharge inutile, compliquant le développement et la maintenance de l’application, alors que quelques entités font parfaitement l’affaire.

A l’opposé, on pourrait se dire que la gestion des médias, c’est forcément un sujet compliqué, et qu’il vaut mieux employer un service en ligne plutôt que d’implémenter quoi que ce soit sur le sujet (comprendre : un SaaS spécialisé en gestion de médias). Il existe plusieurs solutions SaaS sur le marché pour gérer des médias et les aspects associés (widget d’upload / stockage / redimensionnement et optimisation / CDN / etc.). Là encore, c’est une approche qui peut être pertinente dans certains contextes, mais qui présente aussi des inconvénients non négligeables :

  • d’abord, des coûts récurrents (souvent loin d’être anodins, surtout si le volume de médias est important). Ces services facturent souvent la bande passante, donc si votre site réalise un trafic important, la facture peut monter très vite ;
  • une dépendance à un service tiers (ce qui peut poser des problèmes de confidentialité ou de conformité, selon la nature des médias gérés) ;
  • une flexibilité limitée (les fonctionnalités proposées par le service peuvent ne pas correspondre exactement aux besoins du projet, et il peut être difficile ou impossible de les adapter) ;
  • une complexité accrue de l’architecture (il faut gérer l’intégration entre l’application Symfony et le service tiers, ce qui peut introduire des points de défaillance supplémentaires) ;
  • une situation plus complexe pour les stacks de développement, pour l’intégration continue, et pour la maintenance à long terme du projet (le test en local devient plus compliqué, par exemple) ;
  • et dans certains cas, cela ne correspond juste pas à l’usage métier du projet. Si mon application doit permettre d’organiser des comptes rendus de réunion PDF, est-ce que cela a du sens d’employer un SaaS spécialisé en gestion de médias, qui sera en premier lieu optimisé pour des images et des vidéos ?

En fin de compte, choisir de développer sur mesure des fonctionnalités de gestion de contenu (c’est la catégorie dans laquelle s’inscrit la gestion de médias) est souvent une approche simple, économique et adaptée aux besoins spécifiques des projets. De bonnes raisons peuvent conduire à d’autres choix, mais il n’y a pas de solution universelle.

Section intitulée revenons-a-nos-moutons-pourquoi-un-nouveau-bundle-de-gestion-de-medias-pour-symfonyRevenons à nos moutons : pourquoi un nouveau bundle de gestion de médias pour Symfony ?

Retour à notre histoire initiale : après avoir constaté que l’image illustrant notre article de blog était de mauvaise qualité, malgré la présence d’une version source correcte dans l’interface d’administration, j’ai commencé à enquêter sur ce gravissime problème ! 😋

Depuis quelques années, nous embarquons les images dans les articles de notre blog sous la forme de balises <picture> avec généralement deux sources: une version dans le format d’origine (JPEG ou PNG), et une version WebP. L’objectif est de profiter des avantages du format WebP (meilleure compression, qualité visuelle préservée) tout en assurant une compatibilité avec les navigateurs qui ne supportent pas encore ce format (d’ailleurs, nous avons fréquemment un débat en interne : pour ou contre l’abandon pur et simple des formats d’images historiques au profit de formats plus modernes comme WebP ?).

Cette conversion était traitée par LiipImagineBundle, un bundle Symfony très populaire pour la gestion et la transformation d’images, que nous employons avec plaisir depuis des années, dans le cadre de nombreux projets. C’est un bundle développé par nos confrères de Liip, et qui wrappe la librairie Imagine, une bibliothèque PHP de manipulation d’images qui abstrait les traitements graphiques par-dessus plusieurs extensions PHP (GD, Gmagick, Imagick, etc.).

En creusant peu à peu, j’ai d’abord essayé d’améliorer notre configuration de LiipImagineBundle, en augmentant les niveaux de qualité, mais cela n’a pas vraiment eu les effets désirés : l’image était toujours aussi moche, et son poids avait augmenté de façon significative. En réalisant des tests supplémentaires, il est apparu qu’il n’y avait malheureusement pas de correctif facile à apporter : le choix de l’abstraction offerte par Imagine, bien que très pratique, impose des limitations importantes en termes de qualité des images produites.

C’est même le processus global qui est en cause dans le cas précis de notre utilisation de LiipImagineBundle : pour servir une image WebP optimisée, l’image source est convertie sous la forme d’une ressource Imagick, qui subit autant d’étapes successives de transformation que demandé dans la configuration du bundle (redimensionnements, recadrages, etc.), puis est finalement exportée au format WebP. Et ensuite, cette image WebP est optimisée par le post-processeur WebP, afin de tenter d’en réduire la taille.

Chaque étape de ce processus entraîne une perte de qualité, et le cumul de ces pertes aboutit à un résultat final décevant. En réalité, c’est même dès l’ouverture de l’image source, et sa conversion en objet Imagick, que la majeure partie de la qualité graphique est perdue… Et il n’y a pas vraiment de remède à ça, si ce n’est définir un niveau de qualité de 100% dans la configuration d’Imagine ce qui, évidemment, génère des fichiers énormes.

En analysant l’ensemble des traitements, j’ai même trouvé dommage que, pour générer une image WebP à partir d’un PNG source, le traitement passe par de multiples conversions intermédiaires (PNG -> Imagick Resource -> Ressource redimensionnée -> WebP -> WebP optimisé par cwebp) :

Redimensionnement et optimisation avec Imagine, en plusieurs étapes

Or, le binaire cwebp est capable de convertir directement un PNG en WebP, la redimensionner et la croper en une seule étape, avec un contrôle fin sur les paramètres d’optimisation. Ca a été le point de départ du développement de JoliMediaBundle, en partant de l’idée qu’il serait sans doute possible de faire mieux, en s’affranchissant des limitations imposées par Imagine :

Redimensionnement et optimisation en une étape avec cwebp

Section intitulée d-accord-et-quelles-sont-les-fonctionnalites-de-ce-jolimediabundleD’accord, et quelles sont les fonctionnalités de ce JoliMediaBundle ?

JoliMediaBundle est donc un bundle Symfony open source, développé par JoliCode, qui vise à fournir une solution complète et performante pour la gestion des médias dans les applications Symfony. Voici un aperçu des principales fonctionnalités offertes par JoliMediaBundle :

  • Intégration à EasyAdmin et Sonata Admin : JoliMediaBundle s’intègre parfaitement avec les bundles d’administration populaires, facilitant ainsi la gestion des médias directement depuis l’interface d’administration. La médiathèque est graphiquement bien intégrée dans l’univers visuel de ces bundles, et propose différents modes d’affichage (liste, grille, etc.), ainsi que les fonctionnalités d’upload multiples et de sélection intuitive ;
  • Stockage flexible : l’écosystème PHP dispose déjà d’excellentes solutions d’abstraction du stockage de fichiers, et nous n’allions pas réinventer la roue ! JoliMediaBundle s’appuie sur le composant Flysystem pour offrir une flexibilité maximale en matière de stockage ;
  • Transformation et optimisation : le bundle propose une notion de « variation », qui permet de définir des règles de transformation et d’optimisation des médias. Par exemple, il est possible de créer une variation « thumbnail » qui redimensionne les images à une taille spécifique. Différents types de transformations sont supportés (crop, resize, expand, heighten, etc.) ;
  • Diffusion efficace : JoliMediaBundle génère des URLs stables et lisibles pour l’accès aux médias, facilitant ainsi leur diffusion aux utilisateurs finaux ;
  • Affichage optimisé : le bundle propose des composants Twig pour intégrer facilement les médias dans les pages Web, en tenant compte des bonnes pratiques (attributs srcset, lazy loading, etc.).

Passons en revue quelques-unes de ces fonctionnalités !

Section intitulée transformation-et-optimisation-des-mediasTransformation et optimisation des médias

JoliMediaBundle permet de définir des « variations » de médias, qui sont des ensembles de règles de transformation et d’optimisation. Par exemple, on peut définir une variation nommée « miniature » pour redimensionner les images à une taille spécifique. Voici un exemple de configuration YAML pour définir une telle variation :

variations:
    miniature:
        transformers:
            thumbnail:
                width: 200
                height: 200

La documentation de référence des transformations fournit différents exemples de configurations possibles :

Exemples de transformations supportées par JoliMediaBundle

Section intitulée integration-a-easyadmin-et-sonata-adminIntégration à EasyAdmin et Sonata Admin

Le bundle propose, par le biais de deux bundles complémentaires (JoliMediaEasyAdminBundle et JoliMediaSonataAdminBundle), une intégration transparente avec les deux bundles d’administration les plus populaires dans l’écosystème Symfony. Ces deux bundles sont directement fournis avec JoliMediaBundle, et il suffit de les activer dans votre application Symfony pour en profiter.

L’ergonomie de la médiathèque a été soigneusement pensée pour offrir une expérience utilisateur agréable et efficace. Les deux bridges ont des niveaux de qualité similaire et proposent des fonctionnalités comparables. SImplement, l’apparence visuelle est adaptée à chaque bundle d’administration, pour une intégration harmonieuse dans l’interface existante. Voici quelques captures d’écran illustrant l’intégration de JoliMediaBundle avec EasyAdmin et Sonata Admin :

Intégration de JoliMediaBundle à EasyAdmin et Sonata Admin

Voici différentes fonctionnalités clés de cette intégration :

  • Explorateur de fichiers : un explorateur de fichiers intuitif permet de naviguer facilement dans l’arborescence de la médiathèque, sous la forme de vue en liste ou en grille ;
  • Upload multiple : la fonctionnalité d’upload multiple permet d’ajouter plusieurs fichiers en une seule opération, avec un suivi de la progression des uploads ;
  • Sélection intuitive : lors de la création ou de la modification d’entités métier, il est possible de sélectionner ou d’ajouter facilement des médias depuis la médiathèque en fenêtre modale ;
  • Gestion du cycle de vie : des fonctionnalités de suppression, de renommage, et de déplacement des médias sont disponibles directement depuis l’interface d’administration.

Section intitulée affichage-des-images-dans-les-pages-web-avec-les-balises-code-lt-img-gt-code-et-code-lt-picture-gt-codeAffichage des images dans les pages Web avec les balises <img /> et <picture />

L’insertion d’images dans les pages Web est une tâche courante, mais qui peut rapidement devenir complexe si l’on souhaite respecter les bonnes pratiques en matière de performance et d’accessibilité. Il arrive encore trop couramment que la négociation de contenu ne soit pas prise en compte, que les attributs srcset et sizes ne soient pas utilisés correctement, ou que le lazy loading ne soit pas implémenté.

JoliMediaBundle propose donc des composants Twig dédiés pour faciliter cette tâche. Par exemple, pour insérer une image avec une variation spécifique, il suffit d’utiliser le composant joli:Img :

<twig:joli:Img
    path="example-image.png"
    variation="variation_name"
    alt="Alternative text"
/>

…ce qui produira le balisage HTML suivant (dans l’exemple ci-dessous, les attributs width et height sont automatiquement ajoutés pour limiter le Cumulative Layout Shift (CLS)) :

<img
    src="/path/to/cache/variation-name/example-image.png"
    alt="Alternative text"
    width="200"
    height="200"
    loading="lazy"
    decoding="async"
/>

Les composants Twig joli:Img et joli:Picture prennent en charge automatiquement les bonnes pratiques, telles que le lazy loading, l’ajout des attributs width et height, et la génération des balises <source> pour le format WebP dans le cas du composant joli:Picture. Ces comportements par défaut peuvent évidemment être surchargés selon les besoins spécifiques de vos projets.

Section intitulée debogage-et-analyseDébogage et analyse

La compréhension des transformations appliquées aux médias peut parfois être complexe, surtout lorsque plusieurs variations entrent en jeu dans une même page. Pour aider les développeurs à mieux comprendre ce qui se passe « sous le capot », JoliMediaBundle propose un outil de débogage intégré, accessible via la Web Debug Toolbar de Symfony et le Profiler. Cet outil fournit des informations détaillées sur les transformations appliquées aux médias, les variations disponibles, etc.

A capture of the JoliMediaBundle profiler panel

Section intitulée utilisation-dans-des-entites-doctrineUtilisation dans des entités Doctrine

JoliMediaBundle fournit un type de champ Doctrine personnalisé, MediaType, qui facilite l’association de médias aux entités métier. Voici un exemple d’utilisation dans une entité Doctrine :

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use JoliCode\MediaBundle\Doctrine\Types as MediaTypes;
use JoliCode\MediaBundle\Model\Media;

#[ORM\Entity]
class Article
{
    // ...

    #[ORM\Column(type: MediaTypes::MEDIA, nullable: true)]
    public ?Media $image = null;
}

Lors de la récupération de l’entité, le champ image contiendra une instance de JoliCode\MediaBundle\Model\Media, que vous pourrez utiliser avec les composants Twig pour afficher l’image dans vos templates.

Il est également possible de définir des critères de validation pour les champs de type Media, afin de garantir que les médias associés respectent certaines contraintes (type MIME, taille maximale, etc.), et de définir le comportement à adopter lors de la tentative de suppression d’un média qui serait référencé par une entité Doctrine (interdire la suppression, passer le champ à null, etc.):

use JoliCode\MediaBundle\DeleteBehavior\Attribute\MediaDeleteBehavior;
use JoliCode\MediaBundle\DeleteBehavior\Strategy;
use JoliCode\MediaBundle\Validator\Media as MediaConstraint;

#[ORM\Entity]
class Article
{
    // ...

    #[ORM\Column(type: MediaTypes::MEDIA, nullable: false)]
    #[MediaConstraint(
        allowedPaths: ['illustration', 'avatar'],
        allowedMimeTypes: ['image/jpeg', 'image/png'],
    )]
    #[MediaDeleteBehavior(strategy: Strategy::RESTRICT)]
    public ?Media $requiredImage;
}

Section intitulée une-api-interne-pour-manipuler-les-medias-efficacementUne API interne pour manipuler les médias efficacement

L’API interne du bundle repose autour de quelques concepts clés :

  • retrouver un média ou une de ses variations se fait par le biais du service de résolution, le Resolver ;
  • l’exécution d’une conversion de média est réalisée par le Converter ;
  • on ne manipule habituellement pas directement un fichier ou un chemin, mais plutôt un modèle métier, le Media.
use JoliCode\MediaBundle\Conversion\Converter;
use JoliCode\MediaBundle\Library\LibraryContainer;
use JoliCode\MediaBundle\Model\Media;
use JoliCode\MediaBundle\Resolver\Resolver;

$imagePath = 'some/path/example-image.png';

// resolve the media. If this is successful, $media is an instance of JoliCode\MediaBundle\Model\Media
$media = $this->resolver->resolve($imagePath);

if (!$media instanceof Media || !\in_array($media->getMimeType(), ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) {
    throw new RuntimeException('The media could not be resolved in any of the configured storages, or it does not have a supported mime-type.', [
        'url' => $imageFilename,
    ]);
}

// generate the variation file
$this->resolver->convert($media, variationName: 'small_thumbnail');

Alternatively, you could want to retrieve the variation first, and then convert it:

use JoliCode\MediaBundle\Conversion\Converter;
use JoliCode\MediaBundle\Library\LibraryContainer;
use JoliCode\MediaBundle\Model\Media;
use JoliCode\MediaBundle\Resolver\Resolver;

$imagePath = 'some/path/example-image.png';

// resolve a specific variation of the media
$variation = $this->resolver->resolve($imagePath, variation: 'small_thumbnail');

// generate the variation file, if it does not exist yet
$this->converter->convertMediaVariation($variation, false);

// output the URL of the converted media
echo $variation->getUrl();

Le bundle propose un contrôleur Symfony pour générer les variations à la volée, si elles n’existent pas encore. Cela permet de ne pas avoir à pré-générer toutes les variations pour tous les médias, ce qui peut être assez contraignant et long si votre médiathèque contient beaucoup de fichiers.

Section intitulée ce-que-ce-bundle-ne-fournit-pasCe que ce bundle ne fournit pas

Le bundle n’a pas vocation à fournir une solution complète de gestion des ressources numériques (aka. un DAM), mais plutôt à être une bibliothèque de gestion de médias flexible et facile à utiliser, scalable, et qui peut aisément être intégrée dans vos applications Symfony et étendue selon vos besoins. Si vous avez besoin d’une solution DAM complète, il faudra plutôt envisager d’autres outils spécifiquement conçus à cet effet, ou développer votre propre solution sur la base du bundle.

De même, certaines fonctionnalités ne sont pas présentes dans ce bundle (et n’ont pas vocation à y figurer). Par exemple, la gestion avancée de métadonnées sous la forme d’entités Doctrine, ou des fonctionnalités d’IA (crop centré sur un visage, reconnaissance du contenu des images pour la génération automatique des attributes alt, etc.) ne sont pas incluses dans JoliMediaBundle, mais pourraient être ajoutées via des extensions ou des intégrations avec d’autres services.

Section intitulée je-suis-convaincu-comment-testerJe suis convaincu, comment tester ?

Voici une question très pertinente 💛 ! JoliMediaBundle est un projet open source, disponible sur GitHub, à l’adresse https://github.com/JoliCode/MediaBundle. Vous pouvez l’installer facilement dans votre projet Symfony en suivant les instructions fournies dans la documentation du projet, disponible à https://mediabundle.jolicode.com/.

Nous serions ravis d’avoir vos retours, vos suggestions d’amélioration, et vos contributions ! N’hésitez pas à ouvrir des issues ou des pull requests sur le dépôt GitHub du projet !

Ce bundle est encore jeune, mais il est déjà employé en production dans plusieurs projets, ce qui atteste de son efficacité. Nous avons par ailleurs de nombreuses idées d’amélioration et d’évolutions, alors restez à l’écoute pour les prochaines mises à jour !

Commentaires et discussions

Nos formations sur ce sujet

Notre expertise est aussi disponible sous forme de formations professionnelles !

Voir toutes nos formations

Ces clients ont profité de notre expertise