web-dev-qa-db-fra.com

Méthode de création d'une sous-requête à l'aide de JDatabase

À http://docs.joomla.org/Selecting_data_using_JDatabase , il n'existe pas de méthode documentée pour écrire une sous-requête à l'aide de JDatabase.

https://Gist.github.com/gunjanpatel/86633 illustre une manière d'accomplir cela avec (quelques bits omis):

$subQuery = $db->getQuery(true);
$query    = $db->getQuery(true);

// Create the base subQuery select statement.
$subQuery->select('*')
    ->from($db->quoteName('#__sub_table'))
    ->where($db->quoteName('subTest') . ' = ' . $db->quote('1'));

// Create the base select statement.
$query->select('*')
    ->from($db->quoteName('#__table'))
    ->where($db->quoteName('state') . ' = ' . $db->quote('1'))
    ->where($db->quoteName('subCheckIn') . ' IN (' . $subQuery->__toString() . ')')
    ->order($db->quoteName('ordering') . ' ASC');

// Set the query and load the result.
$db->setQuery($query);

Cela semble être une bonne approche plausible, mais existe-t-il une meilleure?

31
betweenbrain

Oui, pour ma part, la façon dont vous avez construit la sous-requête est celle adoptée par la majorité des développeurs d'extensions de joomla.

J'utilise cette même méthode sur certaines de mes extensions et sur des extensions personnalisées destinées aux clients.

Il n'y a pas de manière "officielle" de le faire, mais le faire comme vous l'avez montré vous permet d'utiliser le générateur de requêtes tout en conservant une bonne lisibilité.

16
Skullbock

Autant que je sache, il n’existe pas de méthode intégrée permettant de réaliser des sous-requêtes simples, ce qui est probablement une lacune du système et doit être corrigé via PR.

Cependant, je ne vois aucun problème avec votre exemple - semble assez raisonnable.

~~~

Voici un exemple en réponse au commentaire de @ DavidFritsch ci-dessous. Plus j'y pense, plus j'apprécie l'approche plus simple présentée dans le PO. C'est plus clair ce qui se passe.

$query = $this->db->getQuery(true)
  ->select('a.*')
  ->subQuery()
    ->select('b.*')
    ->from('#__table_b AS b')
    ->as('subQueryResult')
  ->endSubQuery()
  ->from('#__table_a AS a');
10
Don Gilbert

Il existe également un moyen d'exécuter des requêtes contenant des sous-requêtes à l'aide de l'API de la plate-forme Joomla. L'idée de base sur l'utilisation des sous-requêtes est basée sur gunjanpatel .

Voici un exemple d’exécution de requêtes sur Modèles de jeux imbriqués :

Requête SQL:

-- Find the Immediate Subordinates of a Node
SELECT node.title, (COUNT(parent.id) - (sub_tree.depth + 1)) AS depth
FROM lubd3_usergroups AS node,
        lubd3_usergroups AS parent,
        lubd3_usergroups AS sub_parent,
        (
                SELECT node.id, (COUNT(parent.id) - 1) AS depth
                FROM lubd3_usergroups AS node,
                        lubd3_usergroups AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.id = 1
                GROUP BY node.id
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.id = sub_tree.id
GROUP BY node.id
-- not showing the parent node
HAVING depth = 1
-- showing the parent node
-- HAVING depth <= 1
ORDER BY node.lft;

et la requête transformée à exécuter par Joomla:

// Create the subQuery select statement.
// Nested Set Queries: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
// CROSS JOIN: http://www.informit.com/articles/article.aspx?p=30875&seqNum=5
$subQuery->select(array('node.id', '(COUNT(parent.id) - 1) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt') . ' AND ' . $db->quoteName('node.id') . ' = ' . $db->quote('1'))
    ->group($db->quoteName('node.id'))
    ->order($db->quoteName('node.lft'));

// Create the base select statement.
$query->select(array('node.title', '(COUNT(parent.id) - (sub_tree.depth + 1)) AS depth'))
    ->from($db->quoteName('#__usergroups') . 'node')
    ->join('CROSS', $db->quoteName('#__usergroups', 'parent'))
    ->join('CROSS', $db->quoteName('#__usergroups', 'sub_parent'))
    ->join('CROSS', '(' . $subQuery .') AS sub_tree')
    ->where($db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('parent.lft') . ' AND ' . $db->quoteName('parent.rgt')
    . ' AND ' . $db->quoteName('node.lft') . ' BETWEEN  ' . $db->quoteName('sub_parent.lft') . ' AND ' . $db->quoteName('sub_parent.rgt')
    . ' AND ' . $db->quoteName('sub_parent.id') . ' = ' . $db->quoteName('sub_tree.id'))
    ->group($db->quoteName('node.id'))
    ->having($db->quoteName('depth') . ' = ' . $db->quote('1'))
    ->order($db->quoteName('node.lft'));

// Set the query and load the result.
$db->setQuery($query);
$rowList = $db->loadAssocList();

echo "<pre>";
print_r($rowList);
echo "</pre>";
3
Mario Neubauer

Je vais proposer ma version de l'extrait de code, puis expliquer ma justification et inclure des citations tirées du Manuel des normes de codage Joomla (qui sera au format quoteblock).

$subquery = $db->getQuery(true)
    ->select("checkin")
    ->from("#__sub_table")
    ->where("subTest = 1");

$query = $db->getQuery(true)
    ->select("*")
    ->from("#__table")
    ->where([
        "state = 1",
        "subCheckIn IN ({$subQuery})"
    ])
    ->order("ordering");

$db->setQuery($query);

Utilisez le chaînage de requêtes pour connecter plusieurs méthodes de requête, l'une après l'autre, chaque méthode renvoyant un objet pouvant prendre en charge la méthode suivante. Cela améliore la lisibilité et simplifie le code résultant.

  • J'écris d'abord les requêtes les plus profondes et passe à la requête la plus externe. Cela me permet de chaîner toutes les méthodes de construction de requêtes directement à la méthode getQuery(). En effet, le nom de la variable n’est écrit qu’une fois lors de la construction de la requête individuelle.
    Voici n exemple formidable d'imbrication de requête lourde (quand je pensais qu'il était mignon d'aligner les flèches d'enchaînement).

  • J'essaie d'éviter de faire plusieurs appels select() et/ou where() dans la même requête parce que je l'ai vue qui entraîne la confusion de développeurs moins expérimentés . Parce que ces méthodes acceptent les tableaux, je trouve qu'il est plus lisible et plus pratique de les coder de les utiliser.

  • et enfin le sujet le plus controversé ...

    Les noms de table et les noms de colonne de table doivent toujours être inclus dans la méthode quoteName afin d'échapper au nom de la table et aux colonnes de la table. Les valeurs de champ cochées dans une requête doivent toujours être incluses dans la méthode quote () afin d'échapper à la valeur avant de la transmettre à la base de données. Les valeurs de champ entier vérifiées dans une requête doivent également être transtypées en (int).

    Je suis très en conflit sur cette position. Quand je suis arrivé chez Joomla l’année dernière, je me suis dit que je ne ferais pas d’appels inutiles (aucun avantage pour la stabilité, la sécurité, la lisibilité de la requête) sur des valeurs statiques! Cependant, mon employeur aime l'idée de suivre la ligne Joomla et je dois admettre que je suis généralement très sensible aux règles. J'ai donc traité mes requêtes avec quote(), (int), et quoteName() qui signifie également des tas de concaténation de chaînes (tous correctement espacés). Le résultat final de mon travail est que nous sommes affolés par des blocs de requêtes que même moi avons du mal à regarder. Les lignes les plus mauvaises/les plus longues qui ne se prêtent pas à l’empilement vertical sont les appels join() en raison du nom de la table, l’alias, ON, puis une ou plusieurs conditions qui peuvent ou non nécessiter citant. Je peux comprendre que cette stratégie est mise en œuvre avec une sécurité à l'esprit pour les développeurs novices, mais je l'aimerais certainement si cette politique était quelque peu tempérée par la sensibilité du fait que tous les codeurs Joomla ne sont pas des copistes ignorants. . Je veux dire, regardez à quel point le code est clair et concis sans les appels inutiles.

  • En ce qui concerne le nettoyage:

    • Je n'utilise presque jamais * Dans mes clauses SELECT
    • Je n'appelle jamais __toString()
    • Je ne cite pas les nombres entiers, je les jette comme des nombres entiers
    • Je n'écris pas ASC car c'est le sens de tri par défaut
    • Je fais tout ce qui est en mon pouvoir pour ne pas utiliser les mots clés mysql lors de la création de nouveaux noms de tables et de colonnes
    • Par souci de préférence personnelle, j'ai tendance à utiliser des guillemets doubles sur les arguments de chaîne de ma méthode pour maintenir l'uniformité, à distinguer des guillemets simples de mysql et à pouvoir profiter d'une interpolation variable que j'écris habituellement avec " syntaxe complexe ".
    • J'utilise des noms de variable informatifs et des commentaires pour améliorer la lisibilité de mes requêtes imbriquées, et mon code en général
    • Je teste mon code avant qu'il ne quitte ma garde
1
mickmackusa