Travailler avec des relations de données – $has_many

Dans cette leçon, nous allons approfondir un peu les données relationnelles en introduisant des relations plurielles avec $ has_many.

 

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

 

Jusqu’à présent, nous ne traitions que du contenu directement associé à une page, mais souvent, les pages sont composées de contenu stocké en dehors de la page et simplement importées dans la page par le biais d’une relation de données. Nous avons vu un peu de cela dans la leçon n ° 7 lorsque nous avons créé des $has_onerelations avec des Fileobjets. Toutes les informations sur le fichier sont stockées dans sa propre table et la page en fait simplement référence. Nous allons approfondir un peu les données relationnelles dans cette leçon en introduisant des relations plurielles avec $has_many.

Ce que nous allons couvrir

  • Création d’un objet de données générique
  • Mise en place d’une $has_manyrelation
  • Introduction au champ de grille
  • Travailler avec des données relationnelles sur le modèle

Création d’un objet de données générique

Un lien de téléchargement vers un nouveau modèle statique avec lequel nous allons travailler est joint à cette leçon  regions-page.html. Notre concepteur a une fois de plus dépouillé tout le chrome et ne nous a laissé que la $Layoutsection. Nous allons donc, avant d’aller plus loin, télécharger ce fichier et migrer la page dans un modèle SilverStripe.

Copiez le contenu regions-page.htmldans un nouveau fichier, app/templates/SilverStripe/Lessons/Layout/RegionsPage.ss.

Ensuite, créez un nouveau type de page pour aller avec.

app / src / RegionsPage.php

 

namespace SilverStripe\Lessons;

use Page;

class RegionsPage extends Page
{

}

 

app / src / RegionsPageController.php

 

namespace SilverStripe\Lessons;

use PageController;

class RegionsPageController extends PageController
{

}

 

Exécutez a dev/build?flushet accédez au CMS. Modifiez la Regionspage à taper RegionsPage(sous l’onglet Paramètres). Retournez maintenant sur votre site Web et cliquez sur “Régions”. Vous devriez voir la nouvelle mise en page.

Bien entendu, tout cela est du contenu statique et nous devons commencer à le scinder en un élément modifiable dans le système de gestion de contenu. L’une des caractéristiques les plus apparentes de ce contenu est qu’il appartient clairement à une boucle. Nous voyons les régions Nord – Est , Sud – Est , etc., toutes avec la même structure et les mêmes données. Chacun contient un titre, une photo et une brève description.

Nous pourrions confier cela à l’éditeur de texte enrichi et créer tout ce contenu dans le $Contentbloc, mais ce serait plutôt moche. Notre éditeur de contenu devrait veiller à créer un balisage très spécifique, ce qui repousserait les limites de l’utilitaire WYSIWYG. Nous devons rendre chacune de ces régions modifiable en données structurées.

Commençons par créer le Regiontype de données. Comme nous l’avons vu dans la leçon précédente, les types de contenu génériques ne contenant pas de page devraient être des sous-classes SilverStripe\ORM\DataObject.

app / src / Region.php

 

namespace SilverStripe\Lessons;

use SilverStripe\ORM\DataObject;
use SilverStripe\Assets\Image;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\AssetAdmin\Forms\UploadField;

class Region extends DataObject
{

    private static $db = [
        'Title' => 'Varchar',
        'Description' => 'Text',
    ];

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

    private static $owns = [
        'Photo',
    ];

    public function getCMSFields()
    {
        $fields = FieldList::create(
            TextField::create('Title'),
            TextareaField::create('Description'),
            $uploader = UploadField::create('Photo')
        );

        $uploader->setFolderName('region-photos');
        $uploader->getValidator()->setAllowedExtensions(['png','gif','jpeg','jpg']);

        return $fields;
    }
}

 

Comme décrit dans un didacticiel précédent, le $ownstableau est utilisé pour garantir que, lorsque les régions sont enregistrées, leurs images associées sont également publiées. Les fichiers et les images sont, par défaut, brouillons jusqu’à ce qu’ils soient explicitement publiés. Cela garantit qu’ils seront implicitement publiés avec la page qui les utilise.

Vous avez peut-être remarqué que notre getCMSFields()fonction est un peu différente. C’est parce que nous n’allons pas utiliser l’interface d’édition de page typique pour cet objet, nous n’allons donc pas avoir les onglets fournis avec les objets Page. Nous pourrions très facilement en créer un, mais comme ce type de données est si simple, nous le laisserons sous la forme d’une simple liste de champs et ajouterons tous les champs de formulaire au constructeur.

 

Mise en place d’une relation $has_many

Maintenant que nous avons notre Regionobjet autonome , nous devons le relier à notre RegionsPage. Notre conception nous a appris que la page peut contenir un nombre quelconque de régions. Nous utiliserons donc cette $has_manyrelation pour cela.

app / src / RegionsPage.php

 

// ...
class RegionsPage extends Page
{
    private static $has_many = [
        'Regions' => Region::class,
    ];
}

 

Cela suit la même convention que celle $has_oneutilisée dans une leçon précédente. La clé “Régions” est le nom arbitraire que nous allons donner à la relation. C’est la méthode que nous allons utiliser pour obtenir une liste de toutes les régions liées. La valeur est le nom de la classe liée.

Exécutez a dev/buildet voyez si vous obtenez des modifications de base de données. Si vous n’en voyez pas, c’est le résultat attendu! Pourquoi? Eh bien, nous n’avons pas encore terminé.

 

Réciproquant le $ has_many

$has_manyles relations sont un cas particulier, car elles doivent être réciproquement partagées par la classe associée. Alors que tous les RegionsPagea beaucoup Regions, il est vrai aussi que tout Regionun RegionPage qui le contient. Les régions ne peuvent pas appartenir à plus d’une page de région.

Ceci est important, car la mutation de la base de données se produit au $has_oneniveau, pas le $has_many. Ce qui liera réellement ces deux objets, c’est un Regionobjet fournissant un, et un seul RegionsPageID. Faisons cette mise à jour maintenant.

app / src / Region.php

 

//...
class Region extends DataObject
{

    //...
    private static $has_one = [
        'Photo' => Image::class,
        'RegionsPage' => RegionsPage::class,
    ];

    //...

 

En règle générale, une réciprocité has_onecomme celle-ci peut simplement être nommée d’après la classe parente.

Maintenant exécutons a dev/buildet voyons que nous obtenons un nouveau RegionsPageIDchamp.

 

Introduction à GridField

Notre $has_manyrelation est définie, avec un $has_onecôté opposé, et nous sommes prêts à commencer à la peupler avec des données. Pour cela, nous aurons besoin de l’un des chevaux de travail de l’interface CMS – GridField.

GridField est un champ de formulaire hautement configurable qui vous permet de gérer une table de données arbitraire. Dans son sens le plus primitif, vous pouvez considérer cela comme une abstraction d’une table de base de données, mais vous pouvez faire beaucoup plus avec cela. Pour gérer nos Regionobjets, nous souhaitons un onglet sur le site RegionsPagenous permettant de créer, lire, éditer et supprimer des Regionenregistrements associés .

Faisons la mise à jour suivante pour notre RegionsPageobjet.

 

//...
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;

class RegionsPage extends Page {

    private static $has_many = [
        'Regions' => Region::class,
    ];

    public function getCMSFields()
    {
        $fields = parent::getCMSFields();
        $fields->addFieldToTab('Root.Regions', GridField::create(
            'Regions',
            'Regions on this page',
            $this->Regions(),
            GridFieldConfig_RecordEditor::create()
        ));

        return $fields;
    }
}

Le constructeur GridField

Jetons un coup d’oeil à la signature de l’argument pour GridField:

  • ‘Régions’ : Nom obligatoire et arbitraire du champ GridField. Vous en aurez besoin si vous souhaitez effectuer des mises à jour de votre GridField après l’avoir ajouté à FieldList.
  • ‘Régions sur cette page’ : Titre du champ GridField. Devrait être convivial.
  • $ this-> Regions () : il s’agit du composant le plus important de votre GridField. Il remplit la grille avec des données. Dans ce cas, nous utilisons la méthode magique créée par notre $has_manyrelation pour remplir la grille avec tous les enregistrements actuellement associés à la page.
  • GridFieldConfig_RecordEditor :: create () : C’est un peu plus complexe. Il crée un objet contenant un certain nombre d’ GridFieldComponentobjets, qui fournissent divers outils d’interface utilisateur à la grille, tels que la pagination, un bouton “ajouter”, des boutons de suppression / modification, etc. Ces GridFieldConfigobjets peuvent être configurés avec toute une variété de composants que vous aimez. , mais SilverStripe est livré avec quelques configurations courantes qui sont souvent utilisées. GridFieldConfig_RecordEditorest une excellente solution, car elle fournit toute l’interface utilisateur de base que vous attendez pour la gestion des données.

 

Ajout de versioning

À l’heure actuelle, toutes les modifications que nous apportons aux régions seront immédiatement consultées sur le site actif, car ils n’ont pas d’état publié / en projet. Commençons par ajouter un contrôle de version à ces enregistrements.

app / src /Region.php

 

//...
use SilverStripe\Versioned\Versioned;

class Region extends DataObject
{
    //...
    private static $extensions = [
        Versioned::class,
    ];    
    //...  

 

Tout d’abord, nous appliquons le DataExtension Versioned . Nous parlerons davantage des extensions dans un prochain tutoriel, mais pour l’instant, il suffit de comprendre que cette extension intègre des fonctionnalités dans la classe pour qu’elle prenne en charge le contrôle de version.

Exécutez a dev/build?flushet nous devrions voir de nouvelles tables créées.

Enfin, nous devons déclarer la propriété entre RegionsPageet Regionafin que les modifications apportées à la publication se répercutent d’un parent à l’autre et inversement.

app / src / RegionsPage.php

 

//...
class RegionsPage extends Page
{
    //...
    private static $owns = [
        'Regions'
    ];    
    //...  

 

Notez que nous n’avons pas besoin d’utiliser le nom de classe complet Region::class, dans la $ownsdéclaration. C’est parce que nous faisons référence à la relation, pas à la classe. La relation est déclarée comme Regionsdans le has_manytableau. Les entrées dans $ownspourraient tout aussi bien être des noms de méthodes, si vous vouliez déclarer la propriété dans un getter personnalisé.

Retournons dans le CMS et modifions notre page “Régions”. Voir que nous avons maintenant un onglet qui contient une grille. Essayez-le et ajoutez des enregistrements de test.

 

Configuration du GridField

Comme indiqué précédemment, GridFieldest hautement configurable. L’une des personnalisations les plus courantes que vous voudrez apporter à votre grille concerne les colonnes affichées. Dans ce cas, il y a seulement une poignée de champs, donc ne montrer que Title et ce Descriptionn’est pas si grave, mais vous pouvez imaginer que si le DataObject avait 10 champs distincts, nous voudrions probablement le resserrer un peu.

Dans notre cas, apportons une amélioration mineure et ajoutons simplement le Photochamp à notre liste. Par défaut, seuls les champs du $dbtableau sont inclus. Depuis Photoest dans le $has_one, nous devons l’inclure spécifiquement.

Définissons une $summary_fieldsvariable sur notre Regionobjet.

 

//...
class Region extends DataObject {
    //...    
    private static $summary_fields = [
        'Photo' => '',
        'Title' => 'Title of region',
        'Description' => 'Short description'
    ];
    //...
}

 

Ce tableau mappe le nom du champ sur le nom de colonne lisible par l’homme. Nous allons laisser la photo parler d’elle-même et laisser son en-tête de colonne vide.

Comme nous avons modifié une variable statique privée, nous devons exécuter? Flush pour voir la mise à jour de la grille. De quoi ça a l’air? Assez mal, non? La photo n’est pas redimensionnée pour s’adapter à la grille.

Utiliser un getter personnalisé

Heureusement, $summary_fieldsaccepte plus que des noms de champs. Nous pouvons fournir n’importe quelle méthode publique sur notre DataObject en tant que valeur de colonne. Créons une méthode qui renvoie une photo redimensionnée.

 

//...
class Region extends DataObject {
    //...
    private static $summary_fields = [
        'GridThumbnail' => '',
        'Title' => 'Title',
        'Description' => 'Description'
    ];

    public function getGridThumbnail()
    {
        if($this->Photo()->exists()) {
            return $this->Photo()->ScaleWidth(100);
        }

        return "(no image)";
    }
    // ...
}

 

Exécuter à ?flush nouveau et voir que notre grille a l’air beaucoup plus propre maintenant.

Nous aurions pu gagner beaucoup de temps en utilisant une méthode fournie Image par défaut par SilverStripe à tous les objets – CMSThumbnail()Photo.CMSThumbnail aurait abouti à un résultat très similaire, sans le texte de repli (pas d’image) .

Champs traversants

Une autre fonctionnalité intéressante $summary_fields est que vous pouvez traverser des relations pour obtenir des champs étrangers, en utilisant une syntaxe séparée par des points. Supposons que nous voulions afficher le Filename champ sur la photo:

 

//...
class Region extends DataObject
{
    //...
    private static $summary_fields = [
        'Photo.Filename' => 'Photo file name',
        'Title' => 'Title',
        'Description' => 'Description'
    ];
    //...
}

 

Ce type de syntaxe devient particulièrement utile lorsque vous formatez des dates ou que vous obtenez le titre d’un document associé $has_oneplutôt que de simplement afficher son ID numérique.

Ajout de versioning à GridField

Pour le moment, GridField ne nous offre rien en termes de publication. Tout ce que nous avons sont les boutons “Enregistrer” et “Supprimer”. Pour obtenir la gamme complète des actions de publication (“Enregistrer”, “Publier”, “Archiver”, etc.), nous devrons indiquer explicitement que l’ Region objet doit utiliser les extensions GridField versionnées. Pour l’activer, définissez versioned_gridfield_extensions sur true.

 

//...
class Region extends DataObject
{
    //...
    private static $versioned_gridfield_extensions = true;
    //...
}

Travailler avec des données relationnelles sur le modèle

Maintenant que toutes nos données relationnelles sont en place, il est temps de les afficher sur le modèle. Cela devrait être assez simple. Commençons avecRegionsPage.ss

app / templates / Layout / RegionsPage.ss, ligne 9

 

<div class="grid-style1 clearfix">
    <% loop $Regions %>
    <div class="item col-md-12"><!-- Set width to 4 columns for grid view mode only -->
        <div class="image image-large">
            <a href="#">
                <span class="btn btn-default"><i class="fa fa-file-o"></i> Read More</span>
            </a>
            $Photo.Fit(720,255)
        </div>
        <div class="info-blog">
            <h3>
                <a href="#">$Title</a>
            </h3>
            <p>$Description</p>
        </div>
    </div>
    <% end_loop %>
</div>

 

Notez que nous sautons les liens. Nous aborderons cela dans une prochaine leçon.

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