Extensions de données et SiteConfig

Dans ce didacticiel, nous aborderons l’un des principaux éléments constitutifs du code modulaire et réutilisable de SilverStripe Framework: les extensions. Nous n'écrirons pas beaucoup de code dans cette leçon. Nous allons plutôt illustrer un concept clé qui doit être compris pour la suite des choses.

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

Que sont les extensions?

Par définition, une extension est une sous-classe de la SilverStripe\Core\Extensionclasse principale de SilverStripe. En pratique cependant, il s’agit d’un bit de code modulaire pouvant être injecté dans une ou plusieurs autres classes. Le mot “extend” peut vous faire penser à un sous-classement, mais les extensions sont en fait assez différentes des sous-classes. Les sous-classes héritent de toutes les méthodes et propriétés de leur classe parent unique. Les extensions, quant à elles, fournissent un ensemble de méthodes pouvant être ajoutées “par magie” à d’autres classes. J’utilise le mot “magiquement” car les extensions n’injectent aucun code matériel dans votre définition de classe. Les méthodes sont ajoutées au moment de l’exécution. Vous pouvez également aller plus loin en ajoutant des propriétés aux modèles dérivés de ceux-ci SilverStripe\ORM\DataObjecten utilisant la SilverStripe\ORM\DataExtensionclasse. Pour le reste de cette leçon, nousSilverStripe\ORM\DataExtension

Le cas le plus simple pour une extension est lorsque vous écrivez des fonctionnalités identiques ou presque identiques dans plusieurs classes. Imaginez que vous ayez un site Web pour une entreprise qui affiche tous ses magasins sur une carte Google. Il y a aussi des événements, qui ont lieu à des endroits spécifiques, et peuvent être affichés sur une carte. Un code similaire à celui-ci doit être associé à ces deux classes:

 

    private static $db = [
      'Address' => 'Varchar',
      'City' => 'Varchar',
      'Country' => 'Varchar(2)',
      'Postcode' => 'Varchar(5)',
      'Latitude' => 'Decimal(9,5)',
      'Longitude' => 'Decimal(9,5)',
    ];

    public function getFullAddress()
    {
        //...
    }

    public function geocodeAddress()
    {
        //....
    }

 

Vous pouvez placer tout cela dans une classe parente et vos objets Eventet Storedonnées en hériteront, mais ce n’est ni très pratique ni logique. Hormis la règle de gestion qui dit qu’ils doivent tous les deux aller sur une carte, il n’y a pas vraiment de bonne raison de placer ces deux classes dans le même ancêtre. De plus, si les deux classes ne partagent pas le même parent, le modèle entier s’effondre.

Donc que fais-tu? Placez tout le code partagé dans une extension et appliquez cette extension à toutes les classes qui en ont besoin. De cette façon, vous n’avez pas à vous répéter et il devient peu coûteux de rendre mappable tout objet DataObject.

Parmi d’autres exemples, citons l’ajout d’une fonctionnalité permettant d’envoyer un courrier électronique à un administrateur après la mise à jour d’un enregistrement donné ou l’ajout de fonctionnalités intégrant un enregistrement à des API de réseaux sociaux. Il y a beaucoup de bonnes raisons d’utiliser des extensions, et tout projet SilverStripe de taille décente doit en avoir quelques-uns en jeu.

Une métaphore utile pour distinguer les extensions des sous-classes de l’ architecture verticale, et des extensions de l’ architecture horizontale . Si vous avez fait beaucoup de CSS, vous connaissez probablement ce modèle. Pensez à la différence entre ce qui suit:

 

    <ul class="notifications">
        <li class="notification">Some text here</li>
    </ul>

    <div class="actions">
        <a class="action">Do this</a>
    </div>
    ul.notifications li.notification, .actions a.action {
        background: red;
        color:white;
    }

 

Versus utilisant un motif plus horizontal:

 

    <ul class="notifications">
        <li class="notification alert">Some text here</li>
    </ul>

    <div class="actions">
        <a class="action alert">Do this</a>
    </div>
    .alert {
        background: red;
        color: white;
    }

 

En injectant du style dans une classe séparée, nous pouvons effectivement “étiqueter” notre élément en lui attribuant un ensemble spécifique de traits, plutôt que de compter sur la chaîne d’héritage pour cibler l’élément dans un cas spécifique. Vous pouvez imaginer que les extensions de données dans SilverStripe vous offrent la possibilité de mélanger plusieurs classes PHP.

 

Extensions vs autres approches

Si vous avez déjà utilisé Ruby on Rails, ou peut-être plus communément, LESS, vous avez probablement déjà identifié ce concept familier comme un “mixin”, et c’est une évaluation précise. Les extensions SilverStripe sont très similaires aux mixins. Ce sont des ensembles de fonctionnalités à usage unique qui complètent le code existant.

De plus, si vous connaissez assez bien PHP, vous vous demandez peut-être pourquoi SilverStripe a réinventé le concept de traits , proposé nativement en PHP depuis sa version 5.4. Vous n’êtes certainement pas loin, mais il y a quelques bonnes raisons pour lesquelles SilverStripe utilise son propre modèle d’extensions plutôt que des traits PHP.

La première raison est simple histoire. La version open-source de SilverStripe est antérieure de sept ans à PHP 5.4, aussi, dans une certaine mesure, des extensions ont été intégrées à la base de code SilverStripe comme solution de contournement de longue date à une faille dans PHP.

De plus, certaines particularités SilverStripe ne sont pas facilement remplacées par des traits, telles que la manière dont les tableaux sont fusionnés plutôt que surchargés par des sous-classes, et l’utilisation de points d’extension, que nous verrons plus loin dans ce didacticiel.

Mais surtout, les extensions ont un avantage majeur sur les traits PHP: elles peuvent être appliquées à des classes en dehors de l’espace utilisateur. Cela signifie que vous pouvez modifier les classes principales sans modifier réellement le code source. Pour faire référence à notre dernier exemple, il est facile d’imaginer ajouter une fonctionnalité de mappage aux classes Eventet Storequi vivent dans notre code de projet, mais qu’en est-il si nous voulions ajouter des fonctionnalités à la Fileclasse principale ou modifier le comportement d’un contrôleur CMS spécifique? Vous ne pourrez pas affecter le trait sans modifier la définition de la classe principale, et bien sûr, nous ne voulons pas le faire, car cela casserait lors de la mise à niveau.

Vous pourriez vous demander pourquoi nous ne pourrions pas simplement créer notre propre sous-classe SilverStripe\Assets\Fileou y ajouter de nouvelles fonctionnalités. Nous pourrions le faire, et cela fonctionnerait parfaitement dans notre propre projet, mais le problème est que tout le monde – le CMS et tous vos modules – ne sera pas au courant de votre classe spéciale. Ils utilisent tous encore SilverStripe\Assets\File. Donc, si vous voulez un changement global, une sous-classe n’est pas une très bonne option. (Vous pouvez utiliser l’ injection de dépendance pour forcer votre sous-classe, mais c’est un sujet plus avancé que nous aborderons plus tard.)

 

Prises d’extension

Nous avons établi que les extensions constituent une solution de contournement des fonctionnalités qui ne sont pas offertes nativement par PHP. Il y a donc forcément quelques compromis à prendre en compte lorsque vous travaillez avec des extensions.

 

La “surcharge” gotcha

L’idée fausse la plus courante sur l’utilisation des extensions est qu’elles peuvent surcharger des méthodes telles que des sous-classes. Ce n’est pas le cas Supposons que vous souhaitiez mettre à jour la getMemberFormFields()méthode de la SilverStripe\Security\Memberclasse de manière à ce qu’elle envoie une commande ping à un service tiers, de sorte que vous écriviez quelque chose comme ceci:

 

use SilverStripe\ORM\DataExtension;

class MyMemberExtension extends DataExtension
{

    protected function apiCall()
    {
        //.. call API here...
    }

    public function getMemberFormFields()
    {
       $someData = $this->apiCall();
       //... get normal fields, and add $someData
    }
}

 

Cela ne marchera pas. Lorsqu’une méthode d’extension rencontre la classe lors de son extension, la méthode native gagne toujours. Vous pouvez uniquement injecter de nouvelles fonctionnalités dans une classe. Vous ne pouvez pas le surcharger comme vous le faites avec une sous-classe.

Heureusement, pour résoudre ce problème, SilverStripe propose des points d’extension . Les points d’extension sont créés lorsque la classe en cours d’extension appelle la $this->extend()méthode et transfère l’exécution à l’une quelconque des extensions de la classe, en fournissant toutes les références que l’extension peut souhaiter utiliser.

Regardons à nouveau notre méthode de connexion. Dans framework/src/Security/Member.php, nous pouvons voir que la getMemberFormFields()méthode que nous essayons de mettre à jour offre un point d’extension:

 

      public function getMemberFormFields()
      {
        //... build form fields
        $this->extend('updateMemberFormFields', $fields);        
      }

Étant donné ces connaissances, nous pourrions écrire notre extension pour utiliser l’un ou l’autre de ces deux crochets.

use SilverStripe\ORM\DataExtension;

class MyMemberExtension extends DataExtension {

    protected function apiCall()
    {
        //.. call API here...
    }

    public function updateMemberFormFields($fields)
    {
       $someData = $this->apiCall();
       $fields->push(HiddenField::create('SomeData', null, $someData));
    }

}

 

Pensez à en $this->extend()tant qu’émetteur d’événement et les classes d’extension en tant qu’écouteurs d’événement. Les points d’extension ne sont pas proposés partout, mais ils apparaissent dans la plupart des zones de la base de code que vous souhaitez améliorer ou modifier. En tant que développeur de modules, il est très important d’offrir des points d’extension afin que d’autres puissent effectuer les personnalisations qu’ils jugent utiles.

Le “propriétaire” a eu

Regardons à nouveau notre appel API. Supposons que l’API nécessite un paramètre pour définir la demande, tel que le nom complet de l’utilisateur.

 

use SilverStripe\ORM\DataExtension;

class MyMemberExtension extends DataExtension {

    protected function apiCall()
    {
        $myAPIClient->getUser($this->getName());
    }

    public function updateMemberFormFields($fields)
    {
       $someData = $this->apiCall();
       $fields->push(HiddenField::create('SomeData', null, $someData));
    }

}

 

C’est du code imaginaire, nous allons donc nous épargner la peine de l’exécuter. Le résultat serait quelque chose comme ceci:Fatal error: The method getName() does not exist on MyMemberExtension

Comment cela pourrait-il être? Le membre a la méthode getName(), non? Eh bien, rappelez-vous, nous ne traitons pas avec une sous-classe. Nous n’avons pas hérité de cette méthode dans notre extension. Cette classe est parallèle à la SilverStripe\Security\Memberclasse, pas en dessous.

Nous voudrions sûrement avoir accès à toutes ces méthodes dans notre extension, et pour cela SilverStripe nous fournit une propriété appelée owner, qui fait référence à l’instance de la classe que nous étendons. Pour que cela fonctionne, appelez simplement $this->owner->getName().

 

    protected function apiCall()
    {
        $myAPIClient->getUser($this->owner->getName());
    }

 

Voici ma promesse: vous oublierez cette idiosyncrasie à plusieurs reprises dans vos projets SilverStripe avec une certitude à 100%. Tout le monde le fait. C’est un anti-modèle, c’est bizarre, c’est facile à oublier, et c’est juste un de ces pièges dont vous devez être conscient lorsque vous travaillez avec des extensions. Alors, prenez une profonde respiration. Embrasse le. Vous allez apprendre à aimer cet écran d’erreur.

 

Construire et appliquer une extension

Croyez-le ou non, nous allons écrire du code maintenant. L’une des extensions les plus courantes que vous voudrez écrire est celle de la SilverStripe\SiteConfig\SiteConfigclasse. SiteConfig est un peu une anomalie. Il s’agit d’une table de base de données à enregistrement unique qui stocke tous les paramètres de votre site, comme indiqué dans l’ onglet Paramètres du CMS. Par défaut, SiteConfig vous donne des champs pour la TitleTaglineainsi que certains paramètres des autorisations globales simples. Invariablement, vous souhaiterez étendre cet inventaire de champs pour stocker les paramètres liés à votre projet.

Nous recherchons principalement des données qui apparaissent sur chaque page. Par conséquent, l’en-tête et le pied de page de votre site sont d’excellents endroits pour rechercher du contenu pouvant être stocké dans SiteConfig. En bas de page, vous trouverez des liens vers les médias sociaux et une brève description du site, à gauche. Jetons tout cela dans SiteConfig.

 

Définir une classe d’extension

Si votre extension doit être utilisée pour augmenter une classe principale, telle que SiteConfig, la convention consiste à utiliser le nom de la classe que vous étendez, suivi de “Extension”.

app / src / SiteConfigExtension.php

 

namespace SilverStripe\Lessons;

use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;

class SiteConfigExtension extends DataExtension
{

    private static $db = [
        'FacebookLink' => 'Varchar',
        'TwitterLink' => 'Varchar',
        'GoogleLink' => 'Varchar',
        'YouTubeLink' => 'Varchar',
        'FooterContent' => 'Text'
    ];

    public function updateCMSFields(FieldList $fields)
    {
        $fields->addFieldsToTab('Root.Social', array (
            TextField::create('FacebookLink','Facebook'),
            TextField::create('TwitterLink','Twitter'),
            TextField::create('GoogleLink','Google'),
            TextField::create('YouTubeLink','YouTube')
        ));
        $fields->addFieldsToTab('Root.Main', TextareaField::create('FooterContent', 'Content for footer'));
    }
}

 

Nous définissons une méthode pour l’un des points d’extension les plus utilisés du framework, updateCMSFieldsproposée par toutes les classes DataObject pour mettre à jour leur interface CMS avant le rendu. Notez que nous ne devons rien retourner. La classe SiteConfig le fera pour nous. Pour le moment, nous ne faisons que mettre à jour l’objet qu’il nous a transmis $this->extend('updateCMSFields', $fields). Comme les objets sont passés par référence en PHP, nous pouvons nous sentir libres de les muter si $fieldsnécessaire.

 

Enregistrer votre extension dans la configuration

Pour activer notre extension, nous devons l’appliquer à la SilverStripe\SiteConfig\SiteConfigclasse. Cela se fait à travers la couche Config.

_app / config / app.yml

 

    SilverStripe\SiteConfig\SiteConfig:
      extensions:
        - SilverStripe\Lessons\SiteConfigExtension

 

Comme nous avons changé la configuration, nous devons vider le cache. Construisez la base de données en utilisant dev/build?flush. Vous devriez voir de nouveaux champs.

Accédez maintenant à l’onglet Paramètres du CMS et remplissez les champs avec certaines valeurs.

Enfin, nous mettrons à jour notre modèle pour utiliser les nouveaux champs. Tous les Pagemodèles reçoivent une variable appelée $SiteConfigqui accède à l’enregistrement unique SiteConfig. Étant donné que nous obtiendrons plusieurs propriétés à partir de cet objet, il s’agit d’une excellente occasion d’utiliser le <% with %>bloc.

app / templates / Includes / Footer.ss (ligne 78)

 

<ul class="social-networks">
  <% with $SiteConfig %>
    <% if $FacebookLink %>
      <li><a href="$FacebookLink"><i class="fa fa-facebook"></i></a></li>
    <% end_if %>
    <% if $TwitterLink %>
      <li><a href="$TwitterLink"><i class="fa fa-twitter"></i></a></li>
    <% end_if %>
    <% if $GoogleLink %>
      <li><a href="$GoogleLink"><i class="fa fa-google"></i></a></li>
    <% end_if %>
    <% if $YouTubeLink %>
      <li><a href="#"><i class="fa fa-youtube"></i></a></li>    
    <% end_if %>
  <% end_with %>                                
</ul>

 

Nous avons ignoré Pintrest, car cela ne s’appliquerait probablement pas à cette entreprise. Nous allons couvrir les flux RSS dans un autre tutoriel, mais dans tous les cas, il ne s’agira pas d’un flux RSS couvrant l’ensemble du site, nous pouvons donc également supprimer ce bouton.

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