Travailler avec des fichiers et des images

Dans la leçon précédente, nous avons commencé à ajouter des champs personnalisés de base à nos pages Article. Nous allons maintenant continuer à améliorer l'intégration de ces pages avec le système de gestion de contenu en permettant à l'utilisateur d'y attacher des fichiers et des images.

 

Dépôt de code à télécharger

 

Examinons rapidement les lacunes que nous essayons de combler dans ce didacticiel. Premièrement, nous voyons que la liste des articles a une petite image à gauche qui représente apparemment une photo associée à l’article. Sur la vue de détail, nous avons une photo plus grande. La conception ne spécifie pas explicitement s’il s’agit d’images différentes ou de la même image dimensionnée différemment, mais pour les besoins de ce didacticiel, nous supposerons que l’utilisateur ne doit télécharger qu’une seule image.

Le client nous a également informés qu’il voudra parfois joindre une brochure de voyage au format PDF à chaque guide de voyage. Nous devrons donc également prévoir une disposition dans le système de gestion de contenu pour le téléchargement d’un fichier.

 

Approches courantes du stockage de fichiers sur des enregistrements de base de données

Si vous avez utilisé d’autres frameworks d’application Web ou CMS, vous pouvez avoir quelques idées sur la façon de conserver des fichiers dans des enregistrements de base de données. Couvrons quelques approches communes:

 

1. Enregistrez un chemin de fichier local dans un champ de texte de l’enregistrement.

Bien que cette solution marque beaucoup de points pour sa simplicité, elle présente de sérieux problèmes de longévité. Si le fichier est déplacé à un autre endroit, vous devez remplir les enregistrements qui font référence au fichier avec le nouveau chemin, ce qui en fait une méthode particulièrement fragile.

 

2. Téléchargez le fichier sur un CDN et enregistrez un URI absolu dans le fichier de l’enregistrement.

Il s’agit d’une solution extrêmement évolutive, car elle exploite les limites de stockage presque infinies de l’hébergement en nuage. Les fichiers hébergés sur le cloud ont tendance à être assez robustes et permanents. Il est donc peu probable que les mêmes problèmes de synchronisation se posent qu’un fichier local, mais extraire le fichier à distance nuit à notre capacité à le manipuler, car nous travaillons sur HTTP plutôt que sur le système de fichiers local.

 

3. Stockez le fichier dans un champ BLOB

Les blobs sont un champ de base de données de type spécial qui stocke des données binaires arbitraires, ce qui en fait un excellent candidat pour les fichiers persistants. Dans la mesure où cela vous permet de télécharger directement directement dans l’enregistrement, cette approche ne présente aucun des problèmes de synchronisation que vous avez avec un système de fichiers, ce qui le rend assez fiable. L’inconvénient est que vous pouvez très rapidement gonfler votre base de données, ce qui crée une mauvaise séparation des problèmes. Votre base de données peut rapidement être réutilisée dans un serveur de fichiers de facto si vous n’êtes pas avisé de son utilisation.

Bien sûr, toutes ces techniques ont du sens dans certains contextes, mais un framework tente de servir une large gamme d’implémentations et SilverStripe choisit donc son propre type de stockage de fichiers, alliant facilité d’utilisation, fiabilité et évolutivité.

 

Comment SilverStripe gère les fichiers

À partir de la version 4.0, toutes les approches ci-dessus sont possibles via la configuration. Dans SilverStripe, le stockage des fichiers et toutes ses pièces en mouvement ont été résumés et exposés à l’utilisateur en tant que services composables. Un Fileobjet n’a pas besoin de savoir où se trouvent les fichiers ni comment les diffuser dans le navigateur. Tant qu’un service est en place pour fournir les mécanismes nécessaires, les fichiers l’utilisent pour faire ce qu’ils doivent faire, sans tenir compte du fonctionnement des internes. Heureusement, SilverStripe est préconfiguré avec une implémentation de stockage de fichiers assez commune et robuste que vous ne devriez avoir à remplacer que si vous avez des besoins spécifiques, tels que le stockage dans un CDN comme Amazon S3.

Fondamentalement, les fichiers dans SilverStripe sont des objets, avec sa propre table dans la base de données, qui conserve essentiellement une liste de tous les fichiers du système de fichiers. La responsabilité de le maintenir synchronisé est entièrement laissée à ces enregistrements de fichiers. Toutes les pages ou autres types de contenu de base de données qui reposent sur des fichiers n’ont pas à s’inquiéter de ce problème. Au lieu de cela, tout ce dont ils ont besoin pour stocker est IDle fichier dont ils ont besoin. Un ID, comme vous le savez peut – être, est considéré comme immuable dans le monde des bases de données, et par conséquent, peu importe ce qui se passe au dossier – si elle se déplace, change son nom, ou sera remplacé – la page n’a pas besoin d’être informé. Il conserve le IDfichier et peut acquérir toutes ses métadonnées en cas de besoin.

 

Présentation du has_one

Jusqu’à présent, nous avons parlé de champs qui sont natifs au type de page. $Author$Dateet $Teasersont tous stockés dans la ArticlePagetable et dans le $dbtableau. Parfois, les champs sont stockés sur une table étrangère et tous les besoins de la table native sont une référence à la IDnotice étrangère. Le principal avantage de cette conception est que, si le contenu étranger change jamais, tous les enregistrements qui y font référence n’ont pas besoin de se soucier de rester à jour.

Pour associer un type de page à un objet étranger, vous pouvez penser que tout ce dont vous avez besoin se trouve dans le $dbtableau, converti en tant que Int, stockant l’ID de l’enregistrement étranger. C’est une option, mais il est beaucoup plus simple de configurer ce champ en tant que clé étrangère, afin que la base de données et le cadre SilverStripe sachent comment le gérer correctement.

Créons un nouveau tableau statique privé dans la ArticlePageclasse appelée $has_one. Cela fonctionne beaucoup comme le $dbtableau, mais au lieu de mapper les noms de champs sur les types de champs, nous les mapperons au nom de classe (ou nom de table) de l’objet associé. Appelons notre champ image “Photo” et notre champ fichier “Brochure”.

 

namespace SilverStripe\Lessons;

use SilverStripe\Assets\Image;
use SilverStripe\Assets\File;

class ArticlePage extends Page 
{

    // ...

    private static $has_one = [
        'Photo' => Image::class,
        'Brochure' => File::class
    ];

    // …
}

 

Avis qui Imagea sa propre classe. De manière appropriée, il s’agit d’une sous-classe de File, mais offre son propre ensemble de fonctionnalités spéciales, en particulier autour du redimensionnement et du rééchantillonnage.

Exécuter dev/buildet notez les nouveaux champs créés. Ils prennent les noms PhotoIDet BrochureID. SilverStripe s’ajoute automatiquement IDà n’importe quel $has_onechamp. Après tout, la seule chose qui sera stockée ici est celle IDd’un disque étranger.

 

Ajout de champs de téléchargement de fichiers

Ajoutons maintenant quelques fonctionnalités de téléchargement à notre getCMSFieldsfonction. Pour les relations de fichiers, UploadFieldc’est le meilleur choix. Pour la propreté, nous allons mettre les téléchargeurs sur leur propre onglet.

 

namespace SilverStripe\Lessons;

use SilverStripe\Assets\Image;
use SilverStripe\Assets\File;
use SilverStripe\AssetAdmin\Forms\UploadField;

class ArticlePage extends Page 
{
    // ...
    public function getCMSFields() {
         $fields = parent::getCMSFields();
         // ...

         $fields->addFieldToTab('Root.Attachments', UploadField::create('Photo'));
         $fields->addFieldToTab('Root.Attachments', UploadField::create('Brochure','Travel brochure, optional (PDF only)'));

           return $fields;
    }
}

 

Connectez-vous au CMS et essayez de télécharger quelques fichiers. Enregistrez et vérifiez que les champs conservent leur état.

Cela fonctionne bien, mais nous pouvons le resserrer un peu. Premièrement, donner une indication écrite du type de fichier que nous attendons (PDF) est une bonne chose, mais il serait préférable que nous puissions réellement appliquer cette contrainte. Après tout, nous devrions toujours nous attendre à ce que si cela peut être cassé, un utilisateur le cassera.

Pour cela, nous allons puiser dans le validateur de UploadField .

 

  public function getCMSFields()
  {
        $fields = parent::getCMSFields();

        // ..

        $fields->addFieldToTab('Root.Attachments', UploadField::create('Photo'));
        $fields->addFieldToTab('Root.Attachments', $brochure = UploadField::create(
          'Brochure',
          'Travel brochure, optional (PDF only)'
        ));

        $brochure->getValidator()->setAllowedExtensions(['pdf']);

        return $fields;
  }

 

Notez que nous pouvons utiliser le raccourci consistant à ajouter simultanément le champ à l’onglet et à l’attribuer à une variable. Cette technique est souvent utilisée lors de la mise à jour de champs de formulaire après l’instanciation.

Désormais, lorsque nous essayons de télécharger autre chose qu’un fichier PDF dans le champ de la brochure, celui-ci le refuse et génère une erreur.

Il serait également intéressant que le téléchargeur place tous les fichiers dans un dossier de notre choix. Par défaut, tout finira dans assets/Uploads, et cela peut directement devenir très pollué si vous ne restez pas sur la configuration de vos répertoires de téléchargement.

Nous pouvons utiliser setFolderName()le UploadFieldaffecter un dossier, par rapport à `actifs / *. Si le dossier n’existe pas, il sera créé avec tous les ancêtres inexistants que vous spécifiez, c’est-à-dire que “n’existe pas / n’existe pas” créerait trois nouveaux dossiers.

 

    public function getCMSFields() {
          $fields = parent::getCMSFields();

          //...

          $fields->addFieldToTab('Root.Attachments', $photo = UploadField::create('Photo'));
          $fields->addFieldToTab('Root.Attachments', $brochure = UploadField::create('Brochure','Travel brochure, optional (PDF only)'));

          $photo->setFolderName('travel-photos');
          $brochure
            ->setFolderName('travel-brochures')
            ->getValidator()->setAllowedExtensions(array('pdf'));

          return $fields;
    }

 

Essayez de télécharger un nouveau fichier et assurez-vous qu’il se trouve à l’emplacement approprié.

 

Travailler avec des fichiers sur le modèle

Comme nous avons déclaré la relation de fichier en tant que $has_one, nous pouvons accéder aux propriétés de l’enregistrement de fichier comme s’il s’agissait d’un champ natif. SilverStripe gérera automatiquement toutes les requêtes pour nous.

Faisons une mise à jour pour ArticlePage.ssafficher un bouton de téléchargement pour la brochure, le cas échéant. Ci <div class="share-wrapper" />– dessous , ajoutez ce qui suit:

 

    <% if $Brochure %>
      <div class="row">
        <div class="col-sm-12"><a class="btn btn-warning btn-block" href="$Brochure.URL"> Download brochure ($Brochure.Extension, $Brochure.Size)</a>
        </div>
      </div>
    <% end_if %>

 

L’appel de la propriété $Brochure, tel que défini dans notre, $has_onenous procure un Fileobjet avec son propre ensemble de propriétés. Nous afficherons certains d’entre eux, mais il y a beaucoup d’ autres mis à votre disposition , y compris $Brochure.Filename$Brochure.Titleet plus encore.

Rechargez la page et testez-la. Vous devriez pouvoir télécharger votre PDF.

 

Le bloc <% avec%>

Ce téléchargement de fichier fonctionne très bien, mais nous pouvons nettoyer un peu la syntaxe du modèle. Il existe plusieurs références aux propriétés obtenues en traversant l’ $Brochureobjet. Nous pouvons supprimer toute cette syntaxe séparée par des points en encapsulant le tout dans un bloc de portée, appelé <% with %>.

 

    <% if $Brochure %>
    <div class="row">
        <% with $Brochure %>
        <div class="col-sm-12">
            <a href="$URL" class="btn btn-warning btn-block"><i class="fa fa-download"></i> Download brochure [$Extension] ($Size)</a>                  
        </div>
        <% end_with %>
    </div>
    <% end_if %>

 

Bien que cette approche n’apporte que peu, voire pas du tout, de performances en termes de performances, il peut être plus facile à lire. Certains développeurs utilisent davantage les opérateurs d’étendue que d’autres. En règle générale, plus vous obtenez de propriétés de l’objet, plus vous aurez d’utilité pour vous sortir d’un <% with %>bloc.

 

Comment fonctionne le rééchantillonnage d’image

Vous avez peut-être remarqué que nous n’avons choisi d’utiliser qu’un seul champ de téléchargement pour ce qui semble être deux tailles de photo différentes: une petite taille dans la vue liste et une plus grande dans la vue détaillée. En effet, lorsque nous traitons des images, nous ne nous préoccupons que du contenu distinct. Le dimensionnement et le rééchantillonnage des photos sont effectués au chargement de la page par le biais d’appels de fonction sur le modèle, vous offrant ainsi un nombre illimité de tailles et de formats différents pour chaque image.

Si l’optimisation des pages vous préoccupe même à distance, l’idée même de rééchantillonner les images au chargement des pages vous fait probablement mal au cœur. Heureusement, comme nous le verrons dans un instant, ce n’est pas si simple.

Étant donné le champ d’image Photo, nous pouvons simplement invoquer $Photopour créer une étiquette d’image pour la photo, telle qu’elle a été téléchargée, sous sa forme brute. En règle générale, vous voulez éviter cela, car dans la plupart des cas d’utilisation, les images peuvent avoir un impact considérable sur la présentation, et nous ne voulons pas faire confiance aveuglément à ce qu’un utilisateur de CMS a chargé (c’est-à-dire un fichier JPEG de 5 Mo).

Si nous invoquons une fonction de rééchantillonnage d’image sur la photo, nous obtiendrons la même balise d’image, uniquement pour une nouvelle version de l’image, à la taille de notre choix.

La syntaxe suivante montrera la photo sur une largeur de 600 pixels, avec une hauteur non contrainte, réduite proportionnellement:

 

    $Photo.ScaleWidth(600)

En réalité, cela revient à réduire une photo en faisant glisser le coin tout en maintenant la touche Maj enfoncée dans le logiciel de traitement d’images.

Cela semble-t-il beaucoup de frais généraux à ajouter à vos modèles? La plupart du temps, ce n’est presque rien. Voici comment ça fonctionne:

SilverStripe génère un sku pour l’image rééchantillonnée en fonction du nom de fichier d’origine, de la méthode de rééchantillonnage et du ou des arguments qui lui sont transmis. Par exemple, étant donné le nom de fichier “photo.jpeg”, la fonction ci-dessus générera une image comme celle-ci:

 

ScaleWidth600-photo.jpeg

 

Lorsque la ScaleWidth()méthode est appelée, SilverStripe génère le nom de fichier et vérifie s’il existe. Si tel est le cas, il restitue l’image existante. Sinon, il le crée et renvoie le chemin d’accès au nouveau fichier. Quoi qu’il en soit, vous obtenez toujours votre balise d’image et le rééchantillonnage est transparent pour vous.

L’avantage de cette approche est qu’elle est incroyablement simple et déclarative, mais l’inconvénient de la programmation déclarative est qu’elle masque le développeur de ce qui se passe réellement sous le capot. Dans ce cas, le développeur doit savoir que le premier chargement de page après l’ajout ou la modification d’une fonction de redimensionnement sera toujours plus lent que les chargements de page suivants. Combien plus lent dépend du nombre d’images que vous avez. La plupart du temps, vous ne le remarquerez jamais, mais il est important de savoir que si vous prenez beaucoup de nouvelles photos (par exemple 10 ou plus), vous voudrez probablement cliquer une fois sur la page pour vous assurer que toutes ces photos se mettre en cache. Ce n’est jamais une bonne idée de mettre des centaines de nouvelles photos sur une page et d’essayer de les rééchantillonner toutes en un seul chargement de page, car vous risqueriez de faire expirer votre processus PHP.

De nombreuses fonctions de rééchantillonnage d’image sont fournies avec l’installation par défaut de SilverStripe. Il est également très facile de créer le vôtre, ce qui couvrira un autre tutoriel. Voici quelques méthodes courantes qui pourraient vous être utiles:

 

$ Image.ScaleWidth (largeur)Redimensionner l’image proportionnellement pour qu’elle tienne dans la largeur donnée
$ Image.ScaleHeight (height)Redimensionnez l’image proportionnellement pour l’ajuster à la hauteur donnée
$ Image.FixMax (largeur, hauteur)Forcer l’image à une certaine largeur et hauteur. Si une dimension est insuffisante, ajoutez un remplissage.
$ Image.Fit (largeur, hauteur)Redimensionnez à la largeur et à la hauteur données, en les recadrant si nécessaire pour conserver les proportions.

Ajout d’images au modèle

Maintenant que nous comprenons le fonctionnement des images, cette dernière étape devrait être assez simple. Sur ArticleHolder.ss, nous voyons que les photos en mode liste concernent les 242x156pixels. Utilisons CroppedImagepour cela, car il est plus important de conserver une taille uniforme que de montrer tout leur contenu.

Remplacez l’image de marque de réservation dans le <% loop $Children %>avec $Photo.Fit(242,156).

Sur ArticlePage.ss, la photo est plus grande et il est important que nous affichions tout son contenu, car il s’agit de la vue détaillée. Utilisons SetWidth(750)pour celui-ci.

Remplacez l’image de marque de réservation <div class="blog-main-image" />par $Photo.ScaleWidth(750).

Rechargez la page et vérifiez que vos images s’affichent correctement.

Personnaliser la balise image

Comme nous l’avons indiqué dans les leçons précédentes, les situations dans lesquelles SilverStripe ne vous permet pas de contrôler totalement votre code HTML sont rares. Les tags d’image ne font pas exception.

Imaginons que nous devions ajouter un nom de classe personnalisé à notre balise image. À l’heure actuelle, nos fonctions ScaleWidth()et Fit()affichent toute la chaîne de code HTML, de sorte que nous n’avons aucun contrôle sur cela.

La bonne nouvelle est que ces méthodes ne renvoient en réalité pas de chaînes de texte. Ils nous donnent un objet image qui contient toutes les propriétés que nous attendrions un fichier d’avoir, par exemple $URL$Extension$Sizeet tout ce que nous nous attendons à une image d’avoir, par exemple $Width, et $Height.

Réécrivons ces variables de modèle pour générer du code HTML personnalisé.

 

    <img class="my-custom-class" src="$Photo.ScaleWidth(750).URL" alt="" width="$Photo.ScaleWidth(750).Width" height="$Photo.ScaleWidth(750).Height" />

 

Cela devient un peu difficile à manier, alors revoyons le <% with %>bloc que nous avons utilisé plus tôt pour nettoyer un peu les choses.

 

    <% with $Photo.ScaleWidth(750) %>
    <img class="my-custom-class" src="$URL" alt="" width="$Width" height="$Height" />
    <% end_with %>

Ajout de propriété

Essayez de prévisualiser votre page d’article dans un autre navigateur, où vous n’êtes pas connecté en tant qu’administrateur. Notez que les images sont manquantes. C’est parce que les fichiers, comme les pages, ont un état publié et un brouillon. En tant que nouveaux téléchargements, ces fichiers sont encore en projet.

Alors, comment publiez-vous des fichiers? Le moyen le plus évident est dans la section Fichiersdu CMS. Mais dans ce cas, il serait bien que, lorsque nous avons publié l’article, tous les fichiers attachés soient également implicitement publiés. Pour cela, nous devons déclarer la propriété des fichiers afin de nous assurer qu’ils reçoivent la publication par association.

 

class ArticlePage extends Page
{
    //...
    private static $owns = [
        'Photo',
        'Brochure',
    ];
}

 

Maintenant, les fichiers joints seront favorables à l’état de publication de la page qui les contient.

TOUT VOIR Ajouter une remarque
VOUS
Ajoutez votre commentaire
 
© Academy EdTech. 2019 All rights reserved.
X