web-dev-qa-db-fra.com

Comment utiliser la clause IN dans Joomla Query

J'essaie de sélectionner des enregistrements où une colonne est dans une liste suggérée si les valeurs utilisent la clause IN. J'ai essayé la requête ci-dessous mais le système ne fonctionne pas.

    $db = JFactory::getDbo();
    $query = $db->getQuery(true); 
    $query
        ->select(array('type', 'name', 'id', 'created_by', 'amount', 'created', 'cat_id', 'status'))
        ->from($db->quoteName('#__Zoo_item'))
        ->where($db->quoteName('created_by') .' = '.$db->quote(10))
        ->where($db->quoteName('status') .' IN '.$db->quote(1,2,4))//THIS IS MY PROBLEM.
        ->group($db->quoteName('created_by'))
        ->order('id DESC')
        ->setLimit(1);
    $db->setQuery($query);
    $loan = $db->loadObject();

La clause problématique est la suivante

->where($db->quoteName('status') .' IN '.$db->quote(1,2,4))

Je serai heureux si quelqu'un peut aider, merci.

3
David Addoteye

D'abord, le problème avec

->where($db->quoteName('status') .' IN '.$db->quote(1,2,4))

n’est pas seulement qu’une erreur de syntaxe est générée sur la requête, mais aussi que $ db-> quote () traite 1 comme paramètre $text et 2,4 Comme valeur attendue de bool pour le paramètre $escape. (Je code sur PhpStorm et ce fait est précisé)

Malheureusement pour votre projet, alors que la solution que vous avez publiée supprime l'erreur de syntaxe dans votre requête, l'erreur de logique persiste. En effet, les valeurs 2 Et 4 Ont été ignorées lors de toutes vos requêtes.

Si vous appelez $query->dump(), vous verrez que la requête générée ressemble à ceci:

SELECT type,name,id,created_by,amount,created,cat_id,status
FROM `zyxwv_Zoo_item`
WHERE `created_by` = '10' AND `status` IN ('1')
GROUP BY `created_by`
ORDER BY id DESC LIMIT 1

Il s'agit de la même valeur endommagée pour IN que dans votre code d'origine, sauf qu'elle est désormais enveloppée de manière parenthétique.


Deuxièmement, une idée fausse commune est que vous [~ # ~] avez besoin de [~ # ~] pour citer tous les points de données possibles dans vos requêtes Joomla SQL. Cette pratique de codage est un geste de prudence - et ce n’est pas un conseil horrible, car certains développeurs de Joomla sont très nouveaux et ne connaissent pas une requête sécurisée/stable à partir d’une requête instable/non sécurisée.

En réalité, si vous codez en dur des entiers dans un tableau (ou plus généralement, les données ne sont pas fournies par une source non digne de confiance - entrée utilisateur ou une ressource hors site - et que vous n'incorporez pas de valeurs avec des caractères monkeywrenching tels que des guillemets), vous avez un contrôle suffisant sur la stabilité de la requête et il n'y a aucun impact sur la sécurité.

Pour la requête publiée ci-dessus, l'écriture se fait de la manière suivante:

$query = $db->getQuery(true)
    ->select(['type', 'name', 'id', 'created_by', 'amount', 'created', 'cat_id', 'status'])
    ->from("#__Zoo_item")
    ->where(["created_by = 10", "status IN (1,2,4)"])
    ->order("id DESC")
    ->setLimit(1);

Il n'y a pas de mots-clés réservés à MySQL , pas de variables, pas de guillemets dans les chaînes, et on suppose que le préfixe de la table #__ Est stable pour commencer, de sorte que la requête peut être écrite avec moins de bouffée de code, une efficacité améliorée et toujours aussi sûre et stable.

* remarque, il n'y avait pas de fonctions AGGREGATE dans la clause SELECT, j'ai donc supprimé la clause inutile group().


Pour les autres chercheurs ...

Vous pourrait quote-wrap les entiers comme ceci:

->where("status IN (" . implode(',', $db->q($array)) . ")")

Bien que cette technique protège votre requête d'une attaque par injection, elle ne nettoie pas les valeurs d'entrée en tant qu'entiers. C'est plus un raccourci qu'une solution appropriée, car cela permettrait à des types de données complètement erronés d'entrer dans la requête.

Si vous avez un tableau non fiable/dynamique de valeurs de chaîne (qui sont supposées être des entiers), vous pouvez alors écrire votre propre processus de nettoyage personnalisé ou vous pouvez transtyper chaque valeur en tant qu'entier.

->where("status IN (" . implode(',', array_map('intval', $array)) . ")")  

ou vous pouvez utiliser la méthode ArrayHelper dans le noyau (qui fait effectivement la même chose) ...

Ceci est exprimé dans la documentation Joomla @ section Secure_arrays_of_integers de https://docs.joomla.org/Secure_coding_guidelines **.

->where($db->quoteName('status') . ' IN (' . implode(',', ArrayHelper::toInteger($array)) . ')')

Vous pouvez constater que le noyau de Joomla sécurise de manière appropriée ses propres requêtes avec toInteger() dans des fichiers tels que: administrator/components/com_contact/models/contacts.php

* remarque, selon l'accessibilité de votre script, vous devrez peut-être déclarer:

use \Joomla\Utilities\ArrayHelper;

Pour ceux qui souhaitent générer une chaîne séparée par des virgules entre guillemets/échappés pour IN (car les valeurs ne sont pas des entiers), la syntaxe est un peu plus longue:

sous-PHP7.4

->where("fullname IN (" . implode(',', array_map(function($n)use($db){return $db->q($n);}, $array)) . ")")

PHP7.4 +

->where("fullname IN (" . implode(',', array_map(fn($n) => $db->q($n), $array)) . ")")

Cet extrait final cite correctement chaque chaîne et échappe à ces guillemets (p. Ex. 'Mark O\'Malley') Voici n message similaire pour un peu plus de lecture.

6
mickmackusa

Je l'ai résolu. Ce que j'ai fait est d'entourer la méthode quote () d'un crochet

->where($db->quoteName('status') .' IN ('.$db->quote(1,2,4).')')

Cela a résolu le problème. J'espère que ça aide quelqu'un.

0
David Addoteye