web-dev-qa-db-fra.com

Existe-t-il un moyen d'extraire un tableau associatif regroupé par les valeurs d'une colonne spécifiée avec PDO?

Par exemple, utilisons un ensemble de données simple

+---------+------+------+------------+
| name    | age  | sex  | position   |
+---------+------+------+------------+
| Antony  |   34 | M    | programmer |
| Sally   |   30 | F    | manager    |
| Matthew |   28 | M    | designer   |
+---------+------+------+------------+

Ce que nous essayons d’obtenir est un tableau organisé de cette façon

Array
(
  [Antony] => Array
    (
      [age] => 34
      [sex] => M
      [position] => programmer
    )

  [Sally] => Array
    (
      [age] => 30
      [sex] => F
      [position] => manager
    )

  [Matthew] => Array
    (
      [age] => 28
      [sex] => M
      [position] => designer
    )
)

À titre approximatif, nous pouvons utiliser

$pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);

Mais comme résultat, nous avons un niveau de nidification inutile

Array
(
    [Antony] => Array
        (
            [0] => Array
                (
                    [age] => 34
                    [sex] => M
                    [position] => programmer
                )

        )

    [Sally] => Array
        (
            [0] => Array
                (
                    [age] => 30
                    [sex] => F
                    [position] => manager
                )

        )

    [Matthew] => Array
        (
            [0] => Array
                (
                    [age] => 28
                    [sex] => M
                    [position] => designer
                )

        )

)

J'ai essayé de supprimer ce niveau d'imbrication inutile en utilisant la fonction de rappel

$stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC|PDO::FETCH_FUNC, 'current');

Mais pour certaines raisons, cela ne passe pas

Array
  (
   [0] => Array
    (
        [age] => 34
        [sex] => M
        [position] => programmer
    )
  ) 

mais juste un tas de scalaires 34, 'M', 'programmer' à la fonction de rappel :(

Vous pouvez le voir en utilisant une fonction telle que callback

function what_do_you_pass_me() {

  $numargs = func_num_args();
  $arg_list = func_get_args();
  for ($i = 0; $i < $numargs; $i++) {
    echo "Argument $i is: " . $arg_list[$i] . "\n";
  };
  echo "\n\n";
};

Existe-t-il un moyen d'obtenir les résultats souhaités en utilisant les modes PDO::FETCH_* sans utiliser array_map('current', $result) après l'extraction des résultats?

23
HongKilDong

C'est un sujet assez ancien, mais j'ai trouvé une solution très simple:

->fetchAll(\PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE)

La première colonne sera définie comme clé, le reste sera défini comme valeur.

Pas besoin de marcher sur le tableau ou d'utiliser array_map.

49
imclickingmaniac

pour réduire un niveau de tableau d'imbrication inutile:

$res = $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
$res = array_map('reset', $res);
13
Stefan

Tableau d'associations de clés

PDO::FETCH_GROUP|PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC
10
Xakki

La réponse acceptée est essentiellement un code culte du fret, qui ne fait son travail que par accident, mais qui n'a pas de sens en soi.

PDO::FETCH_GROUP et PDO::FETCH_UNIQUE sont des modes de récupération exclusifs mutuels , qui ne peuvent pas être utilisés ensemble. Un seul d'entre eux fonctionnerait. Lorsque vous les combinez, ce dernier prend le relais et \PDO::FETCH_GROUP|\PDO::FETCH_UNIQUE est en réalité juste PDO::FETCH_UNIQUE.

À côté de cela, la question est ambiguë en soi, le PO veut que son tableau soit indexé par le champ unique, alors qu'il l'a appelé groupement, ce qui a également soulevé une controverse.

Donc, pour faire simple:

  • to index un tableau avec des valeurs uniques (si vous voulez que le tableau résultant soit indexé par le nom de l'employé, étant donné qu'elles sont uniques), le mode de récupération doit être PDO :: FETCH_UNIQUE :

    $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_UNIQUE);
    
  • to group les résultats (lorsque vous souhaitez regrouper des employés par département, par exemple), le mode de récupération doit être PDO :: FETCH_GROUP :

    $pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_GROUP);
    

dans les deux cas, le champ à utiliser comme index de tableau de premier niveau doit être répertorié en premier dans la liste des champs SELECT.

Une note sur le PDO::FETCH_ASSOC. Étant donné que le mode d’extraction pour le format de résultat préféré peut être défini une fois pour toutes dans le constructeur, il n’a aucun sens de le lister aussi explicitement.

9
Your Common Sense

Cette réponse est obsolète, veuillez voir cette autre réponse à la place.


Il semble qu'il n'y ait aucun moyen de faire cela dans le cadre de fetchAll.

Votre meilleur choix sera de créer une classe qui étend PDO, en y ajoutant une méthode utilitaire.

public function queryKeyedAssoc($query, $params, $key) {
    $sth = $this->prepare($query);
    $sth->execute($params);
    $res = array();
    while($row = $sth->fetch(PDO::FETCH_ASSOC))
        $res[ $row[$key] ] = $row;
    return $res;
}
3
Charles

Nous pouvons rendre la solution de Charles un peu plus agréable en étendant la classe d'instructions à la place:

class MyPdo extends PDO {
    function __construct($Host, $database_name, $username, $password, $options=array()) {
        $options = self::merge(array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_STATEMENT_CLASS => array('PdoPlusStatement', array()),
            PDO::ATTR_EMULATE_PREPARES => true,
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
        ), $options);
        $dsn = "mysql:Host=$Host;dbname=$database_name;charset=utf8";
        parent::__construct($dsn, $username, $password, $options);
    }
}

class PdoPlusStatement extends PDOStatement {
    protected function __construct() {}

    /**
     * @param array|mixed $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed, or one or more non-array arguments to be matched with sequential parameter markers.
     * @throws PDOException
     * @return PdoPlusStatement
     */
    public function execute($input_parameters=null) {
        $args = func_get_args();
        $argc = func_num_args();
        if($argc===0) {
            parent::execute();
        } else {
            if($argc===1 && is_array($args[0])) {
                $args = $args[0];
            }
            parent::execute($args);
        }
        return $this;
    }

    /**
     * Returns an array containing all of the remaining rows in the result set
     * @return array An associative array using the first column as the key, and the remainder as associative values
     */
    public function fetchKeyAssoc() {
        return array_map('reset', $this->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC));
    }
}

Usage:

$users = $pcs->query("SELECT name, user_id, discipline_id FROM wx_user")->fetchKeyAssoc();
2
mpen

Vous ne savez pas pourquoi personne n'a mis en ligne la solution suivante, mais cela fonctionne parfaitement pour moi:

PDO::FETCH_UNIQUE | PDO::FETCH_ASSOC

Donc, changer votre déclaration en:

$pdo->query('SELECT * FROM employee')->fetchAll(PDO::FETCH_UNIQUE|PDO::FETCH_ASSOC);

devrait être exactement ce que vous voulez.

0
kojow7