Bei SQL-Abfragen, bei denen man mehrere Werte, z.B. via ids, aus einer Tabelle gleichzeitig abfragen will, kann man Konstrukte wie WHERE `id` IN (18,12,33,4) verwenden. Joomla 3 und 4 haben dafür eine Datenbank-Methode where() dabei. Mit J!4 kam eine weitere Methode whereIn() dazu, die sowohl das Erstellen der Queries erleichtert als auch die Sicherheit erhöht durch automatische prepared Statements.

Aufgabenstellung

Aus der Joomla-Tabelle #__com_content sollen die Titel der Beiträge abgefragt werden, die die ID 18, 12, 33 oder 4 haben.

Dafür sollen ausschließlich die Joomla-PHP-Klassen verwendet werden. Also nicht irgendwelche selbst zusammengepopelten SQL-Strings der Art

SELECT `title` FROM `#__content` WHERE `id` IN (18,12,33,4)

Vergleich der Abfragen

Bei allen folgenden Beispielen gehe ich davon aus, dass keine use-Zeilen in der Datei verwendet werden (anderes Thema), mit denen man sich die Backslash-Würmer sparen könnte.

Auch verzichte ich auf J-Aliase, da diese in Joomla 5 nurmehr mit aktiviertem Plugin "Verhalten – Abwärtskompatibilität" (plg_behaviour_compat) funktionieren und in Joomla 6 vermutlich gar nicht mehr. Meint beispielsweise: Nicht JFactory, sondern \Joomla\CMS\Factory.

Variante 1. Old-fashioned Methode where() mit Jomla 3, die auch unter J!4 nutzbar ist

// Die abzufragenden IDs:
$idWerte = array(18, '12', 33, '4');

/* Werte säubern; auch der Sicherheit wegen.
Obwohl gar nicht immer nötig. */
$idWerte = \Joomla\Utilities\ArrayHelper::toInteger($idWerte);

$db = \Joomla\CMS\Factory::getDbo();
$query = $db->getQuery(true);

$query->select($db->quoteName('title'))
	->from($db->quoteName('#__content'))
	->where($db->quoteName('id') . ' IN (' . implode(',', $idWerte) . ')');

// Debug-Ausgabe:
echo ' Query mit where() <pre>' . print_r((string) $query, true) . '</pre>';exit;

Variante 2. Old-fashioned mit Methode where() nur unter J!4 mit etwas modernisiertem Code

// Die abzufragenden IDs:
$idWerte = array(18, '12', 33, '4');

/* Werte säubern; auch der Sicherheit wegen.
Obwohl gar nicht immer nötig. */
$idWerte = \Joomla\Utilities\ArrayHelper::toInteger($idWerte);

$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);

$query->select($db->quoteName('title'))
	->from($db->quoteName('#__content'))
	->where($db->quoteName('id') . ' IN (' . implode(',', $idWerte) . ')');

// Debug-Ausgabe:
echo ' Query mit where() etwas moderner: <pre>' . print_r((string) $query, true) . '</pre>';exit;

Varianten 1 und 2 unterscheiden sich nur durch Zeilen 8. Siehe dazu auch JFactory::getDbo()-Ersatz ab Joomla 4

Beide Varianten geben via der Debugzeile 16 aus:

SELECT `title`
FROM `#__content`
WHERE `id` IN (18,12,33,4)

Schaut man sich die PHP-Codes in Zeilen 13 an, die etwas blöd kompliziert anmuten mit ihrem implode() und Klammern, die man nicht vergessen darf, freut man sich vielleicht über Variante 3.

Variante 3. Modern (bis es wieder veraltet ist). Joomla 4 und Methode whereIn().

// Die abzufragenden IDs:
$idWerte = array(18, '12', 33, '4');

/* Werte säubern; auch der Sicherheit wegen.
Obwohl gar nicht immer nötig. */
$idWerte = \Joomla\Utilities\ArrayHelper::toInteger($idWerte);

$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);

$query->select($db->quoteName('title'))
	->from($db->quoteName('#__content'))
	->whereIn($db->quoteName('id'), $idWerte));

// Debug-Ausgabe:
echo ' Query mit whereIn(). Zur Zeit modern: <pre>' . print_r((string) $query, true) . '</pre>';

In Zeile 13 wird der Methode das Array $idWerte ohne jegliches, weiteres Kokolores direkt übergeben. Joomla weiß dann schon, wie man das richtig auflöst. Nämlich zu:

SELECT `title`
FROM `#__content`
WHERE `id` IN (:preparedArray1,:preparedArray2,:preparedArray3,:preparedArray4)
Uuuups!? Wo sind die IDs in Variante 3 hin verschwunden?

Wie oben schon erwähnt und verlinkt, arbeitet man heute bevorzugt mit so genannten prepared Statements bei Datenbankabfragen. Eigentlich konnte man das auch schon früher. Es ist jedoch in Joomla 3 noch nicht der Standard und war etwas aufwändiger hinzubekommen.

Ich sage damit nicht, dass prepared Statements in Joomla 4 immer so selbstverständlich und automatisch vom System zusammengebastelt werden. Man kann auch hier ziemlich stolpern.

Wir haben Glück, dass wir im obigen whereIn()-Beispiel mit Integer-Zahlen arbeiten. Da muss man dann nicht noch weitere Parameter übergeben, sondern Joomla nimmt die "Voreinstellungen".

Die eigentlichen ID-Werte werden in einem ersten Schritt durch eindeutige, unverwechselbare Platzhalter im Query ersetzt, beispielweise :preparedArray1 für die Klartext-ID 18. Der Platzhalter, der auch :firlefanz1 heißen könnte, wird an die ID gebunden (siehe auch Methode bind() und zahlreiche Beispiele im Joomla-4-Core-Code für eigene "Binds").

Im Hintergrund wird die Datenbankabfrage so vorbereitet, dass sie dann final weiß, welcher Platzhalter durch welche ID-Zahl ausgetauscht werden muss.

Empfehlen kann ich das Folgende nicht(!!!!!), da 90% aller Standard-Webserver wegen Überlastung "explodieren" und Browser bis Rechner nahezu einfrieren können, trotzdem habe ich mich fluchend für diesen Beitrag mal getraut, aus meiner Debugzeile 16 das (string) zu entfernen und habe die Ausgabe in eine Datei umgeleitet, die riesig ist. Siehe aber unten, weil ich mittlerweile schlauer bin ;-)

Dort finde ich zu Beginn die "Binds" in der Objekteigenschaft bounded die Joomla dann später auswerten wird. Darin die Platzhalter und ID-Zahlen (siehe jeweils das value).

Joomla\Database\Mysqli\MysqliQuery Object
(
[bounded:protected] => Array
(
	[:preparedArray1] => stdClass Object
	(
		[value] => 18
		[dataType] => int
		[length] => 0
		[driverOptions] => Array
		(
		)
	)

	[:preparedArray2] => stdClass Object
	(
		[value] => 12
		[dataType] => int
		[length] => 0
		[driverOptions] => Array
		(
		)
	)

	[:preparedArray3] => stdClass Object
	(
		[value] => 33
		[dataType] => int
		[length] => 0
		[driverOptions] => Array
		(
		)
	)

	[:preparedArray4] => stdClass Object
	(
		[value] => 4
		[dataType] => int
		[length] => 0
		[driverOptions] => Array
		(
		)
	)
)
... und vieles, vieles mehr ...

OK, explodierende Webserver und riesige Dateien wollen wir natürlich nicht. Deshalb hat Joomla die Methode $query->getBounded() dabei, die uns nur die Ausgabe liefert wie im letzten Code-Block zu sehen. Unten taucht die noch mal auf.

WhereIn() am Beispiel mit zu findenden String-Werten

Diesmal gehen wir davon aus, dass wir nicht ein Array mit Integerwerten wie im obigen Beispiel haben, sondern ein Array mit Strings.

Aufgabenstellung

  • Wir haben eine Datenbank-Tabelle #__belegschaft mit einer Spalte bereich. Darin diverse String-Werte, z.B. logistik, firma, systemloesungen, spritzgusstechnik, firlefanz und was weiß ich.
  • Wir sollen alle Datensätze/Zeilen abfragen, die in Spalte bereich den Wert systemloesungen oder den Wert logistik haben.
  • Wir sollen die Methode $query->whereIn() verwenden.

Umsetzung

$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);

// Die abzufragenden Bereiche:
$bereicheWerte = array('systemloesungen', 'logistik');

$query->select('*')
	->from($db->quoteName('#__belegschaft'))
	->whereIn($db->quoteName('bereich'), $bereicheWerte, \Joomla\Database\ParameterType::STRING);

// Debug-Ausgabe 1:
echo ' Query mit whereIn(): <pre>' . print_r((string) $query, true) . '</pre>';

// Debug-Ausgabe :
echo ' Array mit den Binds: <pre>' . print_r($query->getBounded(), true) . '</pre>';exit;

Man beachte Zeile 9, in der dem whereIn() durch den ParameterType klar gemacht wird, dass das übergebene Array diesmal Strings enthält und keine Integerwerte wie oben im ersten Beispiel.

Die beiden Debugging-Zeilen 12 und 15 spucken das hier aus:

Query mit whereIn():

SELECT *
FROM `#__belegschaft`
WHERE `bereich` IN (:preparedArray1,:preparedArray2)

Array mit den Binds:

Array
(
    [:preparedArray1] => stdClass Object
        (
            [value] => systemloesungen
            [dataType] => string
            [length] => 0
            [driverOptions] => Array
                (
                )
        )
    [:preparedArray2] => stdClass Object
        (
            [value] => logistik
            [dataType] => string
            [length] => 0
            [driverOptions] => Array
                (
                )
        )
)

Schaut man sich die Werte für die Keys dataType an, sieht man, dass dort jetzt string steht. So soll das sein.

ParameterTypes?

Diese Konstanten werden z.B. in Joomla 5.0.1 in der Datei /libraries/vendor/joomla/database/src/ParameterType.php definiert.

<?php

/**
 * Part of the Joomla Framework Database Package
 *
 * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

namespace Joomla\Database;

/**
 * Class defining the parameter types for prepared statements
 *
 * @since  2.0.0
 */
final class ParameterType
{
    /**
     * Defines a boolean parameter
     *
     * @var    string
     * @since  2.0.0
     */
    public const BOOLEAN = 'boolean';

    /**
     * Defines an integer parameter
     *
     * @var    string
     * @since  2.0.0
     */
    public const INTEGER = 'int';

    /**
     * Defines a large object parameter
     *
     * @var    string
     * @since  2.0.0
     */
    public const LARGE_OBJECT = 'lob';

    /**
     * Defines a null parameter
     *
     * @var    string
     * @since  2.0.0
     */
    public const NULL = 'null';

    /**
     * Defines a string parameter
     *
     * @var    string
     * @since  2.0.0
     */
    public const STRING = 'string';

    /**
     * Private constructor to prevent instantiation of this class
     *
     * @since   2.0.0
     */
    private function __construct()
    {
    }
}