Développement

Quelques notes pour répondre à des problématiques rencontrées au travail.

eZ Publish : récupérer récursivement les relations d’objets inverses

Le développement sur le CMF eZ Publish exige parfois de pouvoir récupérer les objets par relation inverse. C’est possible nativement, grâce à la fonction eZContentFunctionCollection::fetchReverseRelatedObjects (équivalente à reverse_related_objects en langage de template) mais pas de manière récursive.

Oulà, c’est compliqué ! Prenons un exemple :

Exemple relations d'objets

  • Un objet de type pays a une relation d’objets regions vers des objets de type region.
  • Un objet de type region a une relation d’objets departements vers un objet de type departement.
  • Un objet de type departement a une relation d’objets villes vers un objet de type ville.

Je connais ma ville. Je recherche les pays auxquels elle est liée par transitivité, selon ce schéma :

Ville → Département → Région → Pays

La fonction que je vous propose permet de récupérer les pays liés à la ville, assez simplement :

$villeContentObjectID = 8923; // L'objet de départ
$languageCode = 'fre-FR';

// Paramètres de parcours des relations d'objet inverses
$relatedObjectsFetchParams = array(
    array(
        // Ville => Département
        'class_identifier' => 'departement', // Recherche dans les objets liés d'un objet de type 'departement'
        'attribute_identifier' => 'villes', // Attribut de la relation d'objets inverse (dans le datamap d'un 'departement' dans notre cas)
        'include_in_result' => true // Inclure dans le résultat les objets constituant la relation ?
    ),
    array(
        // Département => Région
        'class_identifier' => 'region',
        'attribute_identifier' => 'departements',
        'include_in_result' => true
    ),
    array(
        // Région => Pays
        'class_identifier' => 'pays',
        'attribute_identifier' => 'regions',
        'include_in_result' => true
    )
);
$classIdentifier = 'pays';
$countries = Tools::getReverseRelatedObjects($villeContentObjectID, $classIdentifier, $relatedObjectsFetchParams, $languageCode);

Deux fonctions PHP sont nécessaires. Dans l’exemple ci-dessus, elles sont à placer dans une classe Tools. La première est la fonction récursive. Bas niveau, elle ne doit pas être appelée directement.

/**
 * Récupère les objets liés par relation inverse d'un objet.
 * Permet de faire du chaînage de relations inverses,
 * en utilisant plusieurs arrays dans $params.
 *
 * Cette fonction est uniquement appelée par son wrapper self::getReverseRelatedObjects();
 *
 * @param int $objectID Le contentobject_id de l'objet à partir duquel récupérer les autres objets
 * @param string $requestedClassIdentifier Le class_identifier du type d'objet à récupérer
 * @param array $params Liste des paramètres
 * @param int $key Nombre entier indiquant l'avancée dans le tableau $params
 * @param string $languageCode
 * @return array Tableau de eZContentObject
 */
private static function getReverseRelatedObjectsRecursive($objectID, $requestedClassIdentifier, $params, $key, $languageCode)
{
    $objectsList = array();

    // On recherche dans les objets liés de ce type de contenu
    $classIdentifier = $params[$key]['class_identifier'];

    // Attribut de la relation d'objets inverse (attribut présent dans le datamap des $classIdentifiers ci-dessus)
    $attributeIdentifier = $classIdentifier . '/' . $params[$key]['attribute_identifier'];

    // Inclure les objets constituant la relation ?
    $includeInResult = isset($params[$key]['include_in_result']) ? $params[$key]['include_in_result'] : true;

    // Récupération des objets par relation inverse directe
    $relatedObjects = eZContentFunctionCollection::fetchReverseRelatedObjects($objectID, $attributeIdentifier, false, false, array(), null);

    if (isset($relatedObjects['result']))
    {
        // L'objet vers lequel on cherche les relations
        $object = eZContentObject::fetch($objectID);

        foreach ($relatedObjects['result'] as $relatedObject)
        {
            if ($relatedObject->ClassIdentifier == $classIdentifier)
            {
                // La clé de $params contenant les paramètres de la relation inverse suivante
                $nextKey = $key+1;

                // Si la clé suivante existe
                if (isset($params[$nextKey]))
                {
                    // On recommence (récursivité)
                    $nextRelatedObjects = self::getReverseRelatedObjectsRecursive($relatedObject->ID, $requestedClassIdentifier, $params, $nextKey, $languageCode);

                    foreach ($nextRelatedObjects as $nextRelatedObject)
                    {
                        // Si on a trouvé ce qu'on cherchait
                        if (isset($nextRelatedObject[$requestedClassIdentifier]))
                        {
                            // On l'ajoute
                            $objectsList[$nextRelatedObject[$requestedClassIdentifier]->ID][$requestedClassIdentifier] = $nextRelatedObject[$requestedClassIdentifier];

                            // Si on veut ajouter également l'objet lié
                            if ($includeInResult)
                            {
                                $objectsList[$nextRelatedObject[$requestedClassIdentifier]->ID][$relatedObject->ClassIdentifier] = $relatedObject;
                                $objectsList[$nextRelatedObject[$requestedClassIdentifier]->ID][$object->ClassIdentifier] = $object;
                            }
                        }
                    }
                }
                else // On est sur le dernier des paramètres, ce sont les objets de cette relation qu'on renvoie
                {
                    // Si on a trouvé ce qu'on cherchait
                    if ($relatedObject->ClassIdentifier == $requestedClassIdentifier)
                    {
                        $objectsList[$relatedObject->ID][$relatedObject->ClassIdentifier] = $relatedObject;

                        // Si on veut ajouter également l'objet lié
                        if ($includeInResult)
                        {
                            $objectsList[$relatedObject->ID][$object->ClassIdentifier] = $object;
                        }
                    }
                }
            }
        }
    }

    return $objectsList;
}

La seconde fonction est destinée à être utilisée par le développeur :

/**
 * Fonction de génie permettant de récupérer les objets liés par relation
 * inverse d'un object (par transitivité).
 * Permet de faire du chaînage de relations inverses,
 * en utilisant plusieurs arrays dans $params.
 * 
 * Wrapper de la fonction récursive self::getReverseRelatedObjectsRecursive();
 * 
 * @param int $startObjectID Le contentobject_id de l'objet à partir duquel récupérer les autres objets
 * @param string $requestedClassIdentifier Le class_identifier du type d'objet à récupérer
 * @param array $params Liste des paramètres. Exemple pour aller de l'objet $startObjectID vers ses pays liés par transitivité :
 * array(
 *     array(
 *         // Ville => Département
 *         'class_identifier' => array('departement'), // On recherche dans les objets liés d'un objet de type 'departement'
 *         'attribute_identifier' => 'villes', // Attribut de la relation d'objets inverse (dans le datamap d'un 'departement' dans notre cas)
 *         'include_in_result' => true // Inclure dans le résultat les objets constituant la relation ?
 *     ),
 *     array(
 *         // Département => Région
 *         'class_identifier' => array('region'),
 *         'attribute_identifier' => 'departements',
 *         'include_in_result' => true
 *     ),
 *     array(
 *         // Région => Pays
 *         'class_identifier' => array('pays'),
 *         'attribute_identifier' => 'regions',
 *         'include_in_result' => true
 *     )
 * );
 * @param string $languageCode
 * @return array Tableau de eZContentObject
 */
public static function getReverseRelatedObjects($startObjectID, $requestedClassIdentifier, $params, $languageCode)
{
    $key = 0;

    // Récupération des objets liés par relation d'objet
    $relatedObjects = self::getReverseRelatedObjectsRecursive($startObjectID, $requestedClassIdentifier, $params, $key, $languageCode);

    return $relatedObjects;
}

Publié le 24/03/2014
dans la catégorie Développement.
Dernière mise à jour le 17/11/2014.

Tags : ezpublish php

2 commentaires

Lulu 25/03/2014

Pas mal, je m’en souviendrais si le besoin se présente !
Merci El Robico pour cet article.
Ça serait top que tes bouts de code aient la coloration syntaxique ;)

Vincent 25/03/2014

Bien vu, c’est fait ! :-)

Poster un commentaire

Votre adresse e-mail ne sera pas affichée sur le site.