Charger par lot les dépendances des enregistrements d'une vuepour limiter le cout des chargements individuels

Le module Images permet d'associer une image aux enregistrements des modules héritant du module Contenus. Cette fonctionnalité est le plus souvent utilisée pour associer une image aux actualités ou aux articles d'un site. On peut ainsi facilement associer une image, et les différentes version de miniatures qui l'accompagne, à un enregistrement, même si le module ne le prévoyait pas à la base.

La capture d'écran ci-dessous montre la section « Image associée » injectée par le module Images dans le formulaire de configuration du module Actualités.

463

Malheureusement, comme nous allons le voir, cela peut être assez couteux en terme d'accès à la base de données. Comme chaque problème à sa solution, nous verrons comment il est possible de diminuer le nombre de requête SQL, mais voyons d'abord comment se fait cette fameuse injection de dépendance.

Injection de dépendance

La propriété magique image permet d'accéder à l'image associée à un enregistrement.

<?php

$record->image;

Cette propriété est disponible grâce au getter get_image ajouté au prototype nœuds par le module Images. Voici un extrait de la configuration hooks du module :

<?php

return array
(
    'prototypes' => array
    (
        'ICanBoogie\ActiveRecord\Node::get_image' => 'ICanBoogie\Modules\Images\Hooks::get_image'
    )
);

Le code de la fonction de rappel get_image est présenté ci-dessous. On peut y voir que l'identifiant de l'image associée est obtenu depuis les métas-données de l'enregistrement et que l'image est chargée depuis le modèle de données images :

<?php

static public function get_image(Node $ar)
{
    global $core;

    $imageid = $ar->metas['resources_images.imageid'];

    return $imageid ? $core->models['images'][$imageid] : null;
}

Donc, si les métas-données de l'enregistrement n'ont pas encore été chargées et si l'image associée n'a pas encore été chargée non plus, cela implique deux appels SQL pour obtenir l'image. Cela peut être couteux si on doit le faire pour une quarantaine d'enregistrements. Si l'obtention de leur dépendance requiert 80 requêtes SQL, c'est inacceptable ! Heureusement il existe un moyen finalement assez simple pour remédier à cette situation.

Injection de dépendances par lot

Les fournisseurs de données des vues lancent des évènements qui permettent d'intervenir à chaque étape de la récupération des données, notamment pour altérer leur résultat avant de le renvoyer à la vue. Le module Images va profiter de cet évènement pour charger d'un coup les images associées aux enregistrements récupérés pour les injecter directement.

Voici un nouvel extrait de la configuration hooks du module Images. Cette fois on y découvre l'écouteur posé sur l'évènement alter_result du fournisseur de données :

<?php

return array
(
    'events' => array
    (
        'ICanBoogie\Modules\Contents\Provider::alter_result' => 'ICanBoogie\Modules\Images\Hooks::on_contents_provider_alter_result'
    )
);

La fonction de rappel on_contents_provider_alter_result va donc modifier les enregistrements, avant que le fournisseur de données ne les renvoient à la vue, en leur injectant la propriété image :

<?php

public static function on_contents_provider_alter_result(AlterResultEvent $event, \ICanBoogie\Modules\Contents\Provider $provider)
{
    global $core;

    $result = $event->result;

    if (!is_array($result) || count($result) < 4 || !(current($result) instanceof \ICanBoogie\ActiveRecord\Content)
    || !$core->registry['resources_images.inject.' . $event->module->flat_id])
    {
        return;
    }

    $record_keys = array();

    foreach ($result as $record)
    {
        $record_keys[] = $record->nid;
    }

    $image_keys = $core->models['system.registry/node']
    ->select('targetid, value')
    ->where(array('targetid' => $record_keys'name' => 'resources_images.imageid'))
    ->pairs;

    $images = $core->models['images']->find($image_keys);

    foreach ($result as $record)
    {
        $nid = $record->nid;

        if (empty($image_keys[$nid]))
        {
            continue;
        }

        $imageid = $image_keys[$nid];
        $record->image = $images[$imageid];
    }
}

Pour résumer

Le cout des injections de dépendance peut être considérablement réduit grâce à un simple écouteur sur le fournisseur de données des vues. Ainsi pour le rendu de 41 références client avec leur logo, le chargement par lot de dépendances réduit le nombre de requêtes SQL de 78 à 3, et ça c'est chouette.

Laisser un commentaire

Pas de commentaire