Travailler avec des relations de données – $many_many

Dans ce didacticiel, nous allons ajouter des catégories à nos articles en utilisant un autre type de relation de données plurielle appelée $ many_many.

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

Ce que nous allons couvrir

  • Créer un DataObject pour notre $ many_many
  • Ajout d’une interface pour $ many_many
  • Tirer les données dans le modèle

 

Création d’un objet de données pour $ many_many

Revenons au ArticlePageprésent et voyons que chaque article est associé à de nombreuses catégories. Nous pouvons imaginer que dans le CMS, nous voulons une liste de catégories sélectionnables, peut-être des cases à cocher, qui sont proposées pour chaque article. La première chose à faire est de créer un emplacement pour gérer les catégories. Vous pouvez le faire de différentes manières. Cela dépend vraiment du type d’expérience utilisateur que vous voulez créer, mais pour l’instant, collez-les sur l’objet ArticleHolder, de sorte qu’une autre ArticleHolderpage puisse éventuellement proposer son propre ensemble de catégories distinctes.

 

Gestion des objets ArticleCategory

app / src / ArticleHolder.php

 

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

class ArticleHolder extends Page {

    //...
    private static $has_many = [
        'Categories' => ArticleCategory::class,
    ];

    public function getCMSFields()
    {
        $fields = parent::getCMSFields();
        $fields->addFieldToTab('Root.Categories', GridField::create(
            'Categories',
            'Article categories',
            $this->Categories(),
            GridFieldConfig_RecordEditor::create()
        ));

        return $fields;
    }
}

 

Ensuite, créons cet ArticleCategoryobjet. Ça va être vraiment simple.

app / src / ArticleCategory.php

 

namespace SilverStripe\Lessons;

use SilverStripe\ORM\DataObject;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;

class ArticleCategory extends DataObject {

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

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

    public function getCMSFields()
    {
        return FieldList::create(
            TextField::create('Title')
        );
    }
}

 

Notez encore une fois que nous avons la réciprocité $has_onevers le ArticleHolder.

Notez également que nous n’utiliserons pas de versioning pour cet objet de données. Ceci est une décision délibérée basée sur la connaissance qu’il n’y a pas d’opinions où toutes les catégories seront listées. Nous savons que la seule façon d’afficher une catégorie sur le front-end est lorsqu’elle est associée à un article. Donc, sur cette base, nous n’avons pas à nous soucier de l’état publié des catégories.

Exécutez à dev/buildnouveau et voyez que nous obtenons une nouvelle table. Éditez la page “Guides de voyage” dans le CMS et ajoutez quelques exemples de catégories.

 

Relier les articles aux catégories

Maintenant que nous avons des catégories avec lesquelles travailler, relions-les aux articles. Les articles ont plusieurs catégories, comme nous pouvons le voir sur le modèle, il est donc raisonnable de supposer que nous en utiliserons une autre $has_many, n’est-ce pas?

Dans ce cas, un $has_manyn’est pas ce que nous voulons. Rappelez-vous que réciproque $has_onenous avons utilisé avec $has_many? Cela déclare que chaque objet associé ne peut appartenir qu’à un seul parent. Une fois que cette relation est créée, elle ne peut être utilisée nulle part ailleurs. Nous ne voulons pas ce comportement avec les catégories. Une fois qu’une catégorie est revendiquée par un article, elle devrait toujours être disponible pour d’autres articles. Par conséquent, les articles ont plusieurs catégories et les catégories ont plusieurs articles. Ceci est une $many_manyrelation.

app / src / ArticlePage.php

 

//...
class ArticlePage extends Page {
    //...
    private static $many_many = [
        'Categories' => ArticleCategory::class,
    ];
    //...
}

 

Exécuter dev/buildet voir que nous obtenons une nouvelle table, SilverStripe_Lessons_ArticlePage_Categories.

Réciproquant le $ many_many

Facultatif, mais fortement recommandé est un échange de cette relation sur l’ ArticleCategoryobjet, en utilisant $belongs_many_many. Cette variable ne crée aucune mutation de base de données, mais fournira une méthode magique à l’objet pour obtenir ses enregistrements parents. Dans ce cas, nous savons que nous aurons besoin de n’importe quel ArticleCategoryobjet pour obtenir ses articles, car notre conception inclut un filtre par catégorie dans la barre latérale, ce qui est donc très important.

app / src / ArticleCategory.php

 

//...
class ArticleCategory extends DataObject {
    //...
    private static $belongs_many_many = [
        'Articles' => ArticlePage::class,
    ];
    //...
}

 

Nous avons changé une variable statique, alors exécutez ?flush.

 

$ many_many vs $ appartient_many_many

Donc , si les deux côtés de la relation ont de nombreux documents associés, comment savez-vous que l’ on obtient la $many_manyet que l’ on est $belongs_many_many? En règle générale, l’objet qui contient l’interface obtient le fichier $many_many. Dans ce cas, nous allons ajouter des catégories aux articles en utilisant des cases à cocher, c’est donc notre $many_manyobjectif. Encore une fois, le $belongs_many_manyjust fournit la commodité d’une méthode d’accès pour obtenir les articles à partir d’une catégorie.

Ajout d’une interface pour $ many_many

En parlant d’interface, nous devons en ajouter à l’ ArticlePageobjet. Présentons CheckboxSetField.

app / src / ArticlePage.php

 

//...
use SilverStripe\Forms\CheckboxSetField;

class ArticlePage extends Page {
    //...
    public function getCMSFields()
    {
        $fields = parent::getCMSFields();
        //...
        $fields->addFieldToTab('Root.Categories', CheckboxSetField::create(
            'Categories',
            'Selected categories',
            $this->Parent()->Categories()->map('ID','Title')
        ));
        return $fields;
    }
}

 

Jetons un coup d’oeil à la signature d’argument de CheckboxSetField:

  • ‘Catégories’ : Le nom de la $many_manyrelation que nous gérons.
  • ‘Catégories sélectionnées’ : une étiquette pour les cases à cocher.
  • $ this-> Parent () -> Categories () : Les catégories sont stockées sur la ArticleHolderpage parente , nous devons donc d’ Parent()abord les appeler .
  • -> map (‘ID’, ‘Title’) : en utilisant la liste de catégories résultante, créez un tableau qui mappe l’ID de chaque catégorie sur son titre. Cela indique aux cases à cocher de sauvegarder l’ID dans la relation, mais présente le Titlechamp comme une étiquette. Notez qu’il Titlepeut s’agir de n’importe quelle méthode publique exécutable sur l’objet, ce qui est utile si vous souhaitez une valeur calculée ou une concaténation de plusieurs champs. Dans 99% des cas, vous voudrez utiliser IDcomme premier argument ici, car les données relationnelles sont toutes maintenues ensemble par des identificateurs uniques.

Allez dans le CMS et éditez un article sous “Guides de voyage”. Cochez certaines catégories et assurez-vous qu’elles sont sauvegardées.

CheckboxSetFieldC’est une bonne interface utilisateur pour la plupart des $many_manyrelations, mais elle ne s’adapte pas très bien. Si nous avions 100 catégories, cela ne serait pas une expérience agréable pour l’utilisateur. Pour les grands ensembles de données, il existe également ListboxFieldune interface utilisateur typeahead permettant d’associer des enregistrements sans les afficher tous en même temps.

 

Tirer les données dans le modèle

Voyons maintenant comment ajouter aux articles la liste des catégories séparées par des virgules.

app / templates / SilverStripe / Lessons / Layout / ArticlePage.ss, ligne 23

 

<li><i class="fa fa-tags"></i> 
     <% loop $Categories %>$Title<% if not $Last %>, <% end_if %><% end_loop %>
</li>

 

Nous pouvons utiliser la variable de modèle global $Lastpour nous dire si nous en sommes à la dernière itération de la boucle, ce qui déterminera si nous affichons ou non la virgule. Sont également disponibles $First$Even$Oddet bien d’ autres.

 

Utiliser un getter personnalisé

Si nous rechargeons la page, tout cela est beau, mais nous n’avons pas encore terminé. Les catégories sont également affichées sur ArticleHolder.sset HomePage.ss. Ceci est beaucoup de syntaxe de modèle pour continuer à reproduire. Nous pourrions mettre cela dans une inclusion, mais il serait préférable que les ArticlePageobjets puissent générer eux-mêmes une liste de catégories séparées par des virgules. Créons une nouvelle méthode pour cela.

app / src / ArticlePage.php

 

//...
class ArticlePage extends Page {
    //...
    public function CategoriesList()
    {
        if($this->Categories()->exists()) {
            return implode(', ', $this->Categories()->column('Title'));
        }

        return null;
    }
}

 

Nous vérifions l’existence de catégories avec la exists()méthode. Vérifier simplement le résultat de Categories()ne fonctionnera pas, car dans le pire des cas, il retournera un DataListobjet vide . Il ne retournera jamais faux. Nous utilisons exists()pour vérifier la véracité.

L’appel column()sur la liste d’ ArticleCategoryobjets obtiendra un tableau de toutes les valeurs de la colonne donnée, ce qui nous évite d’avoir à parcourir la liste en boucle pour obtenir un seul champ.

Maintenant, mettez à jour HomePage.ssArticleHolder.sset ArticlePage.ssutilisez la $CategoriesListméthode.

app/templates/Silverstripe/Lessons/Layout/ArticlePage.ss, ligne 23

<li><i class="fa fa-tags"></i> $CategoriesList</li>

app/templates/Silverstripe/Lessons/Layout / ArticleHolder.ss, ligne 23

<li><i class="fa fa-tags"></i> $CategoriesList</li>

app/templates/SilverStripe/Lessons/Layout/HomePage.ss, ligne 284

<li><i class="fa fa-tags"></i> $CategoriesList</li>
TOUT VOIR Ajouter une remarque
VOUS
Ajoutez votre commentaire
 
© Academy EdTech. 2019 All rights reserved.
X