Introduction à l’ORM

Dans cette leçon, nous allons utiliser l’ossature de SilverStripe Framework, l’ORM (Object Relational Model), pour syndiquer du contenu sur la page d’accueil de notre site Web.

 

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

 

L’ORM fournit une couche d’abstraction entre votre code PHP et le contenu de la base de données. Chaque fois que vous lisez ou manipulez des enregistrements, l’ORM sera probablement impliqué.

Les bases de l’ORM

Voici un moyen facile de conceptualiser l’ORM:

  • Les classes se réfèrent aux tableaux
  • Les instances de classes font référence à des enregistrements
  • Les propriétés des objets font référence à des colonnes

Pour qu’une classe suive ce paradigme, elle doit être un descendant de SilverStripe\ORM\DataObject. Jusqu’à présent, nous n’avons traité que des sous-classes de Page, qui est lui-même un DataObject (SilverStripe \ ORM \ DataObject -> SilverStripe \ CMS \ Model \ SiteTree -> Page). Pour les types de données génériques ne nécessitant pas de fonctionnalité de page, vous pouvez aller plus loin dans la chaîne d’héritage et simplement sous-classe DataObject. Nous parlerons beaucoup plus du contenu de la base de données sans page dans le prochain tutoriel.

 

Traiter des dossiers individuels

Imaginons que nous ayons la classe simple DataObject suivante:

 

use SilverStripe\ORM\DataObject;

class Product extends DataObject 
{
    private static $db = [
      'Title' => 'Varchar(100)',
      'Price' => 'Currency',
    ];
 }

 

La classe DataObject nous donne quatre propriétés pour commencer:

  • ID – clé primaire
  • ClassName – indique à ORM quelle classe doit être créée pour l’enregistrement. Dans ce cas, sa valeur par défaut est «Produit».
  • Créé – horodatage de la première sauvegarde de l’enregistrement dans la base de données
  • LastEdited – horodatage mis à jour chaque fois que l’enregistrement est écrit dans la base de données.

Conformément au modèle ORM, créer un enregistrement est aussi simple que créer une instance de votre sous-classe DataObject.

 

    $product = Product::create();

 

N’oubliez pas que DataObjects n’écrit pas de manière optimiste dans la base de données.

 

    echo $product->ID; // 0

 

Afin de conserver l’enregistrement, nous devons invoquer sa write()méthode.

 

    $product->write();
    echo $product->ID; // 442

 

Pour plus de commodité, la write()méthode retourne l’ID de l’enregistrement.

 

    echo Product::create()->write(); // 443

 

En outre, la create()méthode peut remplir l’objet avec des données si un tableau est transmis.

 

    $product = Product::create(['Title' => 'My first product']);

 

Pour supprimer un enregistrement, appelez delete().

 

    $product->delete();echo $product->ID; // 0

 

Pour le mettre à jour, il suffit de modifier les propriétés de l’objet, puis d’appeler write().

 

    $product->Price = 2.49;
    $product->write();

 

Tout cela devrait sembler très naturel une fois que vous en prendrez connaissance.

 

Traitement des données agrégées

Les ORM sont essentiellement des API permettant d’écrire des requêtes SQL. Si vous avez déjà travaillé sur un projet où SQL a été écrit en ligne avec du code PHP, vous n’aurez probablement pas de difficulté à voir la valeur d’un tel outil. En général, les requêtes de base de données et la logique contrôleur / vue ne se mélangent pas. Nous essayons d’éviter cela autant que possible, car cela peut devenir vraiment redondant. Il suffit de regarder combien de répétition existe entre ces trois requêtes:

 

$sql = SELECT ID, Created, LastEdited, Title, Price FROM Product”;

$sql = SELECT ID, Created, LastEdited, Title, Price FROM Product WHERE Price > 100”;

$sql = SELECT ID, Created, LastEdited, Title, Price FROM Product WHERE Price > 100 LIMIT 5”;

 

En plus de cela, vous devez vous occuper de la notion de suppression de l’injection SQL et d’autres éléments fondamentaux de l’intégration de la base de données. En outre, si vous changez de plate-forme de base de données, le code ne sera pas bien envoyé.

Vous pouvez créer des fonctions d’assistance pour assembler des requêtes de ce type, mais au final, cette approche n’est pas évolutive. Les cadres préfèrent unanimement confier tous les travaux de base de données à leur propre couche.

Comme les requêtes de base de données concernent des listes d’enregistrements et non des enregistrements individuels, les méthodes que nous allons utiliser sont toutes définies dans la définition de la classe. Comme indiqué précédemment, une classe est essentiellement une référence à une table, cela devrait donc avoir du sens. Dans la programmation orientée objet, les méthodes appelées non pas contre une instance d’une classe, mais plutôt contre la classe elle-même, sont appelées méthodes statiques . Voyons comment cela fonctionne.

Tout d’abord, nous aurons tous nos produits.

 

$products = Product::get();

 

Maintenant, obtenons tous les produits qui coûtent 100 $.

 

$products = Product::get()->filter([
  'Price' => 100
)];

 

Maintenant, obtenons les cinq produits les plus chers de moins de 100 $:

 

$products = Product::get()
               ->filter([
                 'Price:LessThan' => 100
               ])
               ->sort('Price','DESC')
               ->limit(5);

 

Comme vous pouvez le constater, toutes ces méthodes sont chaînables, avec une mise en garde importante: elles sont immuables. Cela signifie que chacune de ces méthodes renvoie une nouvelle liste. Une fois que vous avez stocké la liste dans une variable, vous ne pouvez plus la mettre à jour. Vous ne pouvez écraser que la variable précédente.

 

$products = Product::get();
$products->limit(5);

echo $products->sql(); // does not contain a limit clause

 

Remarque: la sql()méthode est rarement nécessaire. Il est généralement utilisé uniquement pour le débogage.

C’est une erreur très commune que les gens font avec l’ORM. Si nous écrivions jQuery, l’approche ci-dessus aurait appliqué les deux méthodes à l’instance, mais l’ORM ne suivait pas ce modèle. Les structures de données immuables sont de plus en plus courantes dans les frameworks Web.

Mettons à jour ce code pour que les deux clauses s’appliquent.

 

$products = Product::get()->sort('Price', 'DESC');
$products = $products->limit(5);

echo $products->sql(); // contains a sort clause and a limit clause

 

Notez que nous pouvons soit chaîner les méthodes lors de la création, soit écraser la variable à chaque fois. Les deux obtiennent le même résultat.

À propos du chargement paresseux … peut-être plus tard.

L’une des caractéristiques les plus fortes du SilverStripe ORM est peut-être le chargement paresseux des données. Cela signifie qu’une requête n’est exécutée que si elle doit absolument l’être, ce qui entraîne non seulement moins de requêtes, mais également des requêtes plus efficaces.

Regardons l’exemple suivant:

 

    $products = Product::get();

 

La pensée commune nous dirait que la méthode get () que nous avons appelée exécuterait une requête similaire à celle-ci:

 

    SELECT ID, Created, LastEdited, Title, Price FROM Product

 

En réalité, aucune requête n’est exécutée lorsque cette méthode est appelée. SilverStripe fait plutôt remarquer qu’à un moment donné, vous voudrez peut-être obtenir tous les produits.

Allons un peu plus loin:

 

    $products = Product::get();
    foreach($products as $product) {
        echo $product->Title;
    }

 

Nous avons maintenant lancé une requête, car la boucle foreach a indiqué à l’ORM que nous avions besoin des enregistrements et qu’il était temps d’agir. Jusque-là, la liste des produits n’était qu’une idée.

Qu’est-ce qu’il y a de si génial? Plusieurs choses. La première est que nous avons beaucoup moins de requêtes «gaspillées». Par exemple:

 

    $articles = ArticlePage::get();
    $articles = $articles->filter(array('Author' => 'Aaron'));
    $articles = $articles->limit(5);

    foreach($articles as $article) {
      // ...
    }

 

Même si nous appelons plusieurs méthodes dans la requête, une seule est réellement exécutée.

De plus, le chargement différé offre à l’ORM l’occasion d’optimiser la requête juste avant son exécution.

 

    $products = Product::get();
    echo $products->count();

 

Plutôt que de renvoyer quelque chose comme un sizeof()tableau du tableau d’enregistrements résultant, l’ORM est suffisamment intelligent pour voir que tout ce que vous voulez vraiment, c’est un compte, et qu’il exécute quelque chose comme ce qui suit:

 

    SELECT COUNT(*) FROM Product

 

Généralement, dans SilverStripe, il s’agit d’un <% loop %>bloc sur votre modèle, sinon d’un foreachcontrôleur, qui indique en fait que la requête doit être exécutée.

 

Utiliser l’ORM dans un contrôleur

Prenons maintenant tout cela en pratique et ajoutons une requête de base de données personnalisée à un contrôleur. En regardant la page d’accueil, nous voyons qu’il y a une section où les derniers articles sont publiés. Pour les afficher, nous devons écrire une méthode dans notre contrôleur pour récupérer la liste. Appelons ça LatestArticles.

 

namespace SilverStripe\Lessons;

use PageController;

class HomePageController extends PageController 
{

  public function LatestArticles() 
  { 
    return ArticlePage::get()
               ->sort('Created', 'DESC')
               ->limit(3);
  } 
}

 

Il n’est pas nécessaire de passer cette méthode dans le modèle. Parce que c’est dans le contrôleur et défini comme public, nous pouvons y accéder en utilisant $LatestArticles. Mettons à jour HomePage.sspour parcourir les articles.

 

<h1 class="section-title">Recent Articles</h1>
<div class="grid-style1">
  <% loop $LatestArticles %>
    <div class="item col-md-4">
        <div class="image">
            <a href="$Link">
                <span class="btn btn-default"> Read More</span>
            </a>
            $Photo.Fit(220,148)
        </div>
        <div class="tag"><i class="fa fa-file-text"></i></div>
        <div class="info-blog">
            <ul class="top-info">
                <li><i class="fa fa-calendar"></i> $Date.Format('j F, Y')</li>
                <li><i class="fa fa-comments-o"></i> 2</li>
                <li><i class="fa fa-tags"></i> Properties, Prices, best deals</li>
            </ul>
            <h3>
                <a href="$Link">$Title</a>
            </h3>
            <p><% if $Teaser %>$Teaser<% else %>$Content.FirstSentence<% end_if %></p>
        </div>
    </div>
    <% end_loop %>
</div>

 

Il y a une amélioration mineure que nous pouvons apporter à cette fonction. À l’heure actuelle, la limite de trois enregistrements est codée en dur dans le contrôleur, ce qui n’est pas très configurable. La raison pour laquelle nous limitons le jeu de résultats est due aux contraintes imposées par la mise en page. Il est donc plus logique d’affecter cette valeur au modèle.

Mettez à jour la LatestArticlesfonction pour accepter un $countparamètre et définissez sa valeur par défaut sur 3. Passez ce paramètre dans la limit()méthode.

 

//...
class HomePageController extends PageController {

  public function LatestArticles($count = 3) 
  {
    return ArticlePage::get()
                 ->sort('Created', 'DESC')
                 ->limit($count);
  }
}

 

Maintenant, sur le modèle, utilisez simplement <% loop $LatestArticles(3) %>.

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