Introduction à ModelAdmin

Dans cette leçon, nous allons créer l'objet Property qui gérera la plus grande partie du contenu de notre application et ajouter une interface de gestion correspondante dans le CMS à l'aide de ModelAdmin.

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

Ce que nous allons couvrir:

  • Qu’est-ce que ModelAdmin et quand dois-je l’utiliser?
  • Un aperçu des taxonomies de la CMS
  • Construire un DataObject autonome
  • Création d’une interface ModelAdmin
  • Personnalisations de base pour ModelAdmin
  • Ajout de nos données au modèle

 

Qu’est-ce que ModelAdmin et quand dois-je l’utiliser?

Au cours des dernières leçons, nous avons beaucoup parlé du contenu DataObject par rapport au contenu de la page. Pour récapituler, lorsque le contenu représente une page entière, c’est-à-dire la $Layoutsection de notre modèle, nous sous Page– classons et gérons ce contenu dans l’arborescence du site. Lorsque le contenu fait simplement partie d’une page, mais qu’il mérite sa propre interface d’édition, nous utilisons DataObjects. Nous avons examiné le contenu basé sur DataObject dans notre RegionsPage. Bien que chaque Regionobjet fasse partie d’une page, il nécessite son propre outil d’édition. Cela n’aurait aucun sens de gérer tout ce contenu structuré et répété sur une seule page.

Tout cela fonctionne bien lorsque le contenu DataObject est hébergé sur une page, mais qu’en est-il du contenu générique utilisé sur tout le site et n’appartenant pas à un parent spécifique? Vous pouvez avoir un magasin qui gère les produits ou un site de microcrédit avec de nombreux projets. Ce type de contenu mérite vraiment une console de gestion dédiée dans le CMS. Le relier à une page serait à la fois source de confusion pour l’auteur du contenu et ajouterait une complexité inutile à votre modèle de données. Un autre exemple est le contenu que vous souhaitez gérer dans le CMS et qui n’est jamais affiché au public. Pensez à une entreprise qui gère une vaste liste de clients et de commandes. Ce type de contenu n’est visible que par les administrateurs, et le relier à une page n’a aucun sens.

Nous parlons ici de l’idée de DataObjects autonome . Ils sont flottants dans notre système – gérés, mais non liés à une hiérarchie spécifique. Pour ce type de contenu, nous utilisons SilverStripe\Admin\ModelAdmin.

Lorsque nous créons une interface ModelAdmin, nous obtenons une nouvelle section de niveau supérieur du CMS, hébergée entre Pages, Fichiers, Sécurité, etc., dédiée à la gestion de contenu selon nos spécifications. L’avantage de ModelAdmin est que vous pouvez être opérationnel très rapidement et que les personnalisations sont relativement peu coûteuses.

 

Un aperçu des taxonomies de la CMS

Avant de commencer à écrire du code, revenons un peu en arrière et examinons l’ensemble de la structure du système de gestion de contenu et de sa relation avec ModelAdmin.

LeftAndMain

Chaque “page” de niveau supérieur que vous utilisez dans le CMS, c’est-à-dire Pages , Fichiers , Sécurité , Rapports et Paramètres , constitue une sous-classe de SilverStripe\Admin\LeftAndMain. LeftAndMain est un peu la matriarche de tout le système de gestion de contenu. Il supervise et gère tout, des autorisations, à la génération de formulaires de modification, à l’amorçage de CSS et de JavaScript, à la demande de négociation, à la sauvegarde et à la suppression d’enregistrements. Cela dit, la tâche principale de ce géant est de fournir une interface utilisateur sécurisée contenant une Leftsection, telle que l’arborescence du site, ou un formulaire de recherche, et une Mainsection, qui est souvent un formulaire de modification. Je sais que vous vous demandez probablement, à partir de cela, comment ont-ils trouvé le nom LeftAndMain?? Si vous y pensez, faites-le-moi savoir dès que vous l’aurez compris. Cela me laisse perplexe depuis des années.

Toute sous-classe de LeftAndMainsera automatiquement ajoutée au menu principal du CMS. Tout ce que vous avez à faire est de fournir des modèles qui définissent sa section principale et un autre qui définit sa section outils . ModelAdmin est un exemple de classe qui fait cela, et elle suppose beaucoup de choses sur ce que nous voulons dans les deux sections. Il est donc extrêmement facile de commencer.

 

Construire un DataObject autonome

Comme je l’ai déjà dit, dans ce didacticiel, nous allons aborder le type de contenu principal de notre application: l’ objet immobilier , qui représente toute location de vacances donnée qu’un utilisateur final peut louer ou que tout propriétaire peut louer. L’objet de propriété continuera sans aucun doute à grandir avec notre application, mais pour l’instant, nous nous concentrons uniquement sur leur donner une place dans le CMS, alors restons simples pour l’instant. Donnons simplement à cet objet tous les champs nécessaires à sa représentation sur la page d’accueil.

En regardant la page d’accueil, nous pouvons voir que nous avons besoin des champs suivants:

  • Prix ​​par nuit
  • Photo
  • Titre
  • Région
  • Nombre de chambres
  • Nombre de salles de bain

De plus, nous savons que ces propriétés qui s’affichent sur la page d’accueil doivent avoir un attribut spécial qui leur est assigné, ajoutons donc également un FeaturedOnHomepagechamp.

app / src / Property.php

 

namespace SilverStripe\Lessons;

use SilverStripe\ORM\DataObject;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\CurrencyField;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\Assets\Image;
use SilverStripe\Forms\TabSet;

class Property extends DataObject
{

    private static $db = [
        'Title' => 'Varchar',
        'PricePerNight' => 'Currency',
        'Bedrooms' => 'Int',
        'Bathrooms' => 'Int',
        'FeaturedOnHomepage' => 'Boolean'
    ];

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

    public function getCMSfields()
    {
        $fields = FieldList::create(TabSet::create('Root'));
        $fields->addFieldsToTab('Root.Main', [
            TextField::create('Title'),
            CurrencyField::create('PricePerNight','Price (per night)'),
            DropdownField::create('Bedrooms')
                ->setSource(ArrayLib::valuekey(range(1,10))),
            DropdownField::create('Bathrooms')
                ->setSource(ArrayLib::valuekey(range(1,10))),
            DropdownField::create('RegionID','Region')
                ->setSource(Region::get()->map('ID','Title')),
            CheckboxField::create('FeaturedOnHomepage','Feature on homepage')
        ]);
        $fields->addFieldToTab('Root.Photos', $upload = UploadField::create(
            'PrimaryPhoto',
            'Primary photo'
        ));

        $upload->getValidator()->setAllowedExtensions(array(
            'png','jpeg','jpg','gif'
        ));
        $upload->setFolderName('property-photos');

        return $fields;
    }
}

 

La plupart de ces choses sont simples, mais examinons quelques particularités qui pourraient vous échapper.

  • Currencytype de champ : Nous avons un Currencytype de champ dans notre $dbtableau et un CurrencyFielddans notre liste de champs. Ils travaillent bien les uns avec les autres pour fournir la mise en forme correcte pour la devise et s’assurer que la valeur est précédée d’un symbole monétaire.
  • RegionIDen tant que nom pour DropdownField : Pourquoi devons-nous explicitement annexer ID dans ce cas? D’autres champs, tels que UploadFieldsimplement accepter le nom du has_onechamp, tel que Photo , sans qu’il soit nécessaire de nommer le champ exact de la base de données. C’est un peu déroutant certes, mais gardez à l’esprit que DropdownFieldcela ne sauve pas toujours has_one. Il pourrait tout aussi bien être enregistrer dans un champ de texte. D’autres champs de formulaire qui fonctionnent uniquement avec des relations de données savent comment résoudre le nom d’une relation en colonne de base de données, mais ils DropdownFieldsont polyvalents et agnostiques à l’égard des modèles de données. C’est tout ce qui se passe ici.
  • ->setSource()sur le DropdownField : Rien de trop fou ici. Cette méthode indique au champ déroulant quelles options sont disponibles dans sa liste. Vous pouvez fournir la liste comme troisième argument DropdownField, mais j’aperçois que cela rend le code plus lisible pour l’affecter à une méthode chaînée.
  • ArrayLib::valuekey(): Like CheckboxSetFieldDropdownFieldprend un tableau où les clés sont les données qui seront sauvegardées lorsque l’option est sélectionnée, et les valeurs du tableau sont des étiquettes qui seront affichées pour chaque option. Souvent, ils sont identiques. La ArrayLib::valuekey()fonction ne fait que refléter les clés et les valeurs d’un tableau.
  • range(1,10): Ceci est une simple fonction PHP qui crée un tableau contenant une série d’éléments. Il n’est pas nécessaire que ce soit numérique. range('A', 'C')vous donnera un tableau contenant ['A','B','C'], par exemple.
  • ->setEmptyString(): Ceci est l’option par défaut et sans date de notre liste. Nous ne voulons pas que la liste déroulante utilise par défaut la première région répertoriée, car ce serait arbitraire. Nous voulons plutôt que l’utilisateur déclare explicitement la région dans laquelle se trouve la propriété. Pour les chambres et les salles de bain, il n’y a pas de problème 1.

Bon, maintenant que tout est réglé, partons dev/build.

 

Création d’une interface ModelAdmin

Nous allons maintenant créer l’ ModelAdmininterface qui nous donnera un endroit pour accrocher tous ces Propertyenregistrements. Une interface ModelAdmin de base est extrêmement simple à créer.

app / src / PropertyAdmin.php

 

namespace SilverStripe\Lessons;

use SilverStripe\Admin\ModelAdmin;

class PropertyAdmin extends ModelAdmin
{

    private static $menu_title = 'Properties';

    private static $url_segment = 'properties';

    private static $managed_models = [
        Property::class,
    ];
}

 

C’est tout! Passons en revue:

  • $menu_title: Le titre qui apparaîtra dans le menu de gauche du CMS.
  • $url_segment: La partie URL qui suivra admin/pour accéder à cette section. Dans ce cas, le chemin d’accès à notre interface ModelAdmin sera admin/properties.
  • $managed_models: Un tableau de noms de classes qui seront gérés. Chaque ModelAdmin peut gérer plusieurs modèles. Chacun est placé sur son propre onglet en haut de l’écran. Dans ce cas, nous n’en avons qu’un, mais nous en ajouterons plus tard.

Nous avons créé une nouvelle classe, nous devons donc exécuter un ?flush. Faisons cela et allons dans le CMS pour voir ce que nous avons. Vous devriez voir un nouvel onglet Propriétésà gauche. Essayez-le et voyez si vous pouvez ajouter quelques nouveaux Propertyenregistrements.

Faire des personnalisations

Maintenant que nous avons notre interface utilisateur d’édition simple, nous pouvons commencer à la personnaliser un peu pour la rendre plus puissante et plus conviviale pour nos éditeurs de contenu.

Ajout de $ summary_fields

Nous allons commencer par ce que nous avons vu auparavant. $summary_fieldsnous permet de contrôler quels champs sont affichés dans la vue liste.

app / src / Property.php

 

  //...
    private static $summary_fields = [
        'Title' => 'Title',
        'Region.Title' => 'Region',
        'PricePerNight.Nice' => 'Price',
        'FeaturedOnHomepage.Nice' => 'Featured?'
    ];
    //...

 

Notez que nous pouvons utiliser une syntaxe séparée par des points pour appeler des méthodes sur chaque champ. Nous savons que Regionc’est un has_one, donc obtenir le RegionIDest inutile. Nous aurons plutôt le titre de la région, qui est beaucoup plus convivial. Region.Titlese traduit par $this->Region()->Title.

Nous voulons également tirer parti du Currencytype de champ que nous avons utilisé. Rappelez-vous qu’il retourne également un objet. La plupart du temps, il se présente simplement sous forme de chaîne, mais nous pouvons y invoquer des méthodes. Dans ce cas, la Niceméthode proposée par la Currencyclasse nous donnera un prix bien formaté avec un symbole monétaire, des virgules et des valeurs décimales.

BooleanLes types de champs sont également très généreux. Nous pouvons appeler la Nice()méthode pour renvoyer une valeur de Oui ou Non , traduite selon les paramètres régionaux de l’utilisateur.

Fournir une icône personnalisée

Pour le moment, l’onglet de notre section Propriétés du CMS utilise une jolie icône générique, et si nous avons plusieurs de ces administrateurs personnalisés, ils ne seront pas facilement distingués. Donnons-lui notre propre icône.

Recherchez le property.pngfichier inclus dans le __assets/répertoire de cette leçon et déplacez-le dans public/icons.

 

namespace SilverStripe\Lessons;

use SilverStripe\Admin\ModelAdmin;

class PropertyAdmin extends ModelAdmin
{

    private static $menu_title = 'Properties';

    private static $url_segment = 'properties';

    private static $managed_models = [
        'Property'
    ];

    private static $menu_icon = 'icons/property.png';   
}

 

Nous avons modifié une propriété statique, nous allons donc exécuter ?flushet voir que nous avons une nouvelle icône.

Personnaliser le formulaire de recherche

Tout comme les champs affichés dans la vue liste, les champs qui apparaissent dans le formulaire de recherche sont également personnalisables dans la définition de classe du DataObject. Tout ce que nous avons à faire est de définir une nouvelle variable statique privée appelée $searchable_fields. Par défaut, DataObject fournira les mêmes champs que ceux spécifiés dans $summary_fields, mais ce n’est peut-être pas ce que vous recherchez. Dans ce cas, nous avons PricePerNightdans notre $summary_fields, mais ce n’est pas nécessairement un champ sur lequel nous voulons effectuer une recherche dans l’admin, alors déclarons explicitement un $searchable_fieldstableau pour lister ce que nous voulons.

app / src / Property.php

 

  //...
    private static $searchable_fields = [
        'Title',
        'Region.Title',
        'FeaturedOnHomepage'
    ];  
  //...

 

Exécutez a ?flushet voyez que nous avons un nouveau formulaire de recherche qui nous permet de rechercher par le titre de la propriété et par le titre de sa région associée.

Effectuer une recherche par titre de région est une bonne chose, mais cela n’a aucun sens de faire de cela un champ de texte libre, car nos régions sont une liste connue. Ce devrait vraiment être une liste déroulante qui nous permet de choisir parmi toutes les régions qui ont été ajoutées à la base de données. De cette façon, l’utilisateur n’a pas à s’inquiéter de faire une faute d’orthographe et a une meilleure idée de ce qu’il y a dans le système.

Pour ce faire, nous devrons écrire du code exécutable, qui ne peut pas être placé dans une affectation de variable statique, changeons private static $searchable_fieldsdonc public function searchableFields()et nous retournerons un tableau.

 

namespace SilverStripe\Lessons;

use SilverStripe\ORM\DataObject;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\CurrencyField;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\Assets\Image;
use SilverStripe\Forms\TabSet;

class Property extends DataObject
{
  //...

    public function searchableFields()
    {
        return [
            'Title' => [
                'filter' => 'PartialMatchFilter',
                'title' => 'Title',
                'field' => TextField::class,
            ],
            'RegionID' => [
                'filter' => 'ExactMatchFilter',
                'title' => 'Region',
                'field' => DropdownField::create('RegionID')
                    ->setSource(
                        Region::get()->map('ID','Title')
                    )
                    ->setEmptyString('-- Any region --')                
            ],
            'FeaturedOnHomepage' => [
                'filter' => 'ExactMatchFilter',
                'title' => 'Only featured'              
            ]
        ];
    }

  //...     
}

 

Lorsque nous définissons searchableFields(), nous devons être beaucoup plus explicites sur la manière dont nous souhaitons configurer notre formulaire de recherche. Chaque champ que nous incluons doit être mappé sur un tableau contenant trois clés:

  • filter: Le type de filtre à utiliser dans la recherche. Pour une liste complète des filtres disponibles, voir framework/src/ORM/Filters. Pour title, nous voulons une correspondance floue, nous utilisons donc PartialMatchFilter, et puisque les régions sont filtrées par ID, nous voulons que cela soit un ExactMatchFilter.
  • title: L’étiquette qui identifiera le champ de recherche
  • field: Vous avez trois options ici.
    • Vous pouvez fournir une chaîne, représentant la FormFieldclasse que vous voulez, comme nous l’avons fait avec Title.
    • Si vous voulez quelque chose de plus complexe, vous pouvez utiliser un FormFieldobjet. Dans ce cas, j’ai instancié un DropdownFieldpeu comme celui que nous avons utilisé dans notre getCMSFieldsfonction.
    • Une autre option consiste à laisser cet indéfini, et le DataObject demandera au type de champ son champ de recherche par défaut, comme nous l’avons fait avec notre FeaturedOnHomepagechamp. Chaque type de champ sait comment afficher son propre champ de recherche. Dans ce cas, Booleannous donne un bon menu déroulant de trois options: Oui , Non ou N’importe lequel, ce qui est parfait. A CheckboxFieldserait soit sur ou hors tension. Cela ne nous permettrait pas de nous retirer de ce filtre.

Essayez le formulaire de recherche maintenant. C’est un peu mieux, non?

 

Ajout de versioning

Les propriétés sont peut-être les éléments les plus importants de tout ce site Web. Nous allons donc nous assurer qu’elles ont un état brouillon. Nous allons également ajouter une $ownspropriété à la photo principale, de sorte qu’elle soit également publiée.

 

```php
//...
use SilverStripe\Versioned\Versioned;

class Property extends DataObject
{
  //...
  private static $owns = [
      'PrimaryPhoto',
  ];

  private static $extensions = [
      Versioned::class,
  ];

  private static $versioned_gridfield_extensions = true;

    public function searchableFields()
    {
    //...

 

Exécutez a dev/buildpour obtenir les nouvelles tables.

 

Importer des données

Si vous ne le faites pas depuis toujours, le moment est probablement propice pour importer une base de données à partir du __assets/database.sqlfichier de la version terminée de cette leçon. Ce fichier ajoutera de nombreux exemples de propriétés à la base de données, ce qui facilitera grandement le test de fonctionnalités telles que la recherche et le tri.

N’oubliez pas de copier également le assets/dossier. Les photos de la propriété sont là.

 

Ajout de propriétés au modèle

La dernière étape est simple. Écrivons simplement une méthode dans notre HomePagecontrôleur qui récupère les propriétés présentées.

app / src / HomePageController.php

 

namespace SilverStripe\Lessons;

use PageController;

class HomePageController extends PageController
{

  //...
    public function FeaturedProperties()
    {
        return Property::get()
                ->filter(array(
                    'FeaturedOnHomepage' => true
                ))
                ->limit(6);
    }   
}

 

Maintenant, rendons la sortie au modèle.

app / templates / SilverStripe / Lessons / Layout / HomePage.ss (ligne 118)

 

<% loop $FeaturedProperties %>
<div class="item col-md-4">
    <div class="image">
        <a href="$Link">
            <h3>$Title</h3>
            <span class="location">$Region.Title</span>
        </a>
        $PrimaryPhoto.Fill(220,194)
    </div>
    <div class="price">
        <span>$PricePerNight.Nice</span><p>per night<p>
    </div>
    <ul class="amenities">
        <li><i class="icon-bedrooms"></i> $Bedrooms</li>
        <li><i class="icon-bathrooms"></i> $Bathrooms</li>
    </ul>
</div>
<% end_loop %>

 

Vous avez peut-être remarqué que nous avons délibérément ajouté une méthode inexistante $Linkà la propriété. C’est bon. Elle sera simplement ignorée pour l’instant, mais à l’avenir, nous ajouterons cette méthode et nous n’aurons pas à revenir ici pour effectuer la mise à jour.

Rechargez la page et voyez vos propriétés en vedette!

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