PDO : obtenir les noms des colonnes d'un jeux de résultat

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

09 nov. 2011, 16:47

salut,

la question parait conne mais la je sèche :/

J'utilise des requêtes à la volée, et je ne sais pas combien de champs je vais récupérer ni même comment ils s'appellent.
en gros
<?php
$requete = array( 'requete 1','requete2');//etc etc
$pdo = new pdo();
foreach ($requete as $r ) {
$result = $pdo->query($r);
echo '<table>';
                    echo '<thead><tr>';
                    for ($i=0;$i<$result->columnCount();$i++){
                        echo '<td>&nbsp;</td>';
                    }
                    echo '</tr></thead><tfoot><tr>';
                    for ($i=0;$i<$result->columnCount();$i++){
                        echo '<td>&nbsp;</td>';
                    }
                    echo '</tr></tfoot>';
                    while ($data = $result->fetch(PDO::FETCH_ASSOC)) {
                        echo '<tr>';
                        $i =0;
                        foreach ($data as $c) {
                            $i++;
                            echo '<td class="centertext';
                            if ($i == $result->columnCount()) echo ' last';
                            echo '">'.$c.'</td>'; 
                        }
                        echo '</tr>';
                    }
                    echo '</table>';
}
?>
ça marche au poil mais c'est moche, avec le nom des colonnes ça serait beaucoup plus sympa.

j'ai donc pensé à
$d = array_keys($result->fetch(PDO::FETCH_ASSOC));
for ($i=0;$i<$result->columnCount();$i++){
echo '<td class="centertext">'.$d[$i].'</td>' ;
}
mais en fait non, car même si pdostatement implémente l'interface traversable elle fait partie des exceptions qui n'ont pas besoin d'implementer itérator.
et la c'est le drame, pas de rewind() et je ne trouve pas de moyen de remettre le jeux de résultat à "zéro".

la question est donc : est ce qu'il y a moyen remettre le jeux de résultat au début à la manière d'un mysql_data_seek ou d'un rewind de l'interface iterator ?

oui je sais question à la con mais bon :mrgreen:

mici

@+
Il en faut peu pour être heureux ......

ViPHP
ViPHP | 5462 Messages

09 nov. 2011, 17:04

fait un do...while, sinon met le dans un IteratorIterator

EDIT :
$stmt = $pdo->query('SELECT * FROM test');
$stmt->setFetchMode(PDO::FETCH_ASSOC);

echo '<table>';

$row = $stmt->fetch();

echo '<tr><th>', implode('</th><th>',  array_keys($row)), '</th></tr>';

do {
    echo '<tr><td>', implode('</td><td>',  $row), '</td></tr>';
} while($row = $stmt->fetch());

echo '</table>';
OU
$stmt = $pdo->query('SELECT * FROM test');
$stmt->setFetchMode(PDO::FETCH_ASSOC);

echo '<table>';

$iterator = new IteratorIterator($stmt);
$iterator->rewind();

echo '<tr><th>', implode('</th><th>',  array_keys($iterator->current())), '</th></tr>';

foreach ($iterator as $row) {
    echo '<tr><td>', implode('</td><td>',  $row), '</td></tr>';
}

echo '</table>';

ViPHP
xTG
ViPHP | 7331 Messages

09 nov. 2011, 17:26

Il y a getColumnMeta() sinon, mais à prendre avec des pincettes car c'est un joujou expérimental. :)

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

09 nov. 2011, 21:15

Effectivement stealth35 j'ai finis sur un do while, mais je cherchais un truc plus simple en fait.
J'avoue que je ne connais pas trop les iterateurs et que cette solution est pas mal si je veux garder mon where :)

Merci pour les infos

xTG : j'ai testé mais existe pas pour oracle :D

Globalement j'ai l'impression que php à plus de facilité avec les trucs "libre" que les grosses machine de "l'industrie" ^^


@+
Il en faut peu pour être heureux ......

ViPHP
xTG
ViPHP | 7331 Messages

09 nov. 2011, 22:07

Globalement j'ai l'impression que php à plus de facilité avec les trucs "libre" que les grosses machine de "l'industrie" ^^
C'est pas plus étonnant que cela. ^^

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

09 nov. 2011, 22:29

C'est pas faux.

C'est juste que la pour le coup j'ai pas le choix du sgbd :)
Il en faut peu pour être heureux ......

Mammouth du PHP | 19672 Messages

11 nov. 2011, 13:30

En gros, tu voudrais faire un tableau HTML dynamique à la manière de PHPMyAdmin, avec en en-tête les noms des colonnes, quelque soit le nombre de ces colonnes et même si la requête est du type « SELECT * FROM ... » ?
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

11 nov. 2011, 17:31

Vi c'est peux ça. Sauf que la j'ai exo sur les DML avec 20 questions et vu que j'avais du temps à perdre j'ai fait une belle page qui affiche les résultats.

Vu que je suis assez feignant j'ai mis questions / réponses dans des tableaux et j'affiche la chose à la volée. (pas besoin d'afficher 25 fois le même tableau de prévoir entêté / pieds de page etc).
Et Vu qu'avec l'extension mysql j'utilisais la remise à zéro je voulais faire pareil avec pdo :)

C'est stupid de la par de ne pas avoir pensé au do while et j'ai vu un cas d'utilisation des itérateurs ce qui est pas mal (même si je suis pas sur de me le rappeler ;) )

@+
Il en faut peu pour être heureux ......

Mammouth du PHP | 19672 Messages

11 nov. 2011, 19:05

Ok, je voulais être sûr.

En fait, je m'étais fait un truc à des fins de débogage précisément pour ça. Je n'utilise pas de do/while du tout, je me suis construit la petite classe suivante :
<?php
/**
 * Construction d'un tableau HTML dynamique pour un jeu de
 * résultats issus d'une requête SQL, quel que soit le nombre de colonnes et/ou de
 * lignes retournées.
 *
 * Le tableau comportera en en-tête le nom des colonnes, même si la requête est
 * du type « SELECT * FROM ... » et en pied de tableau le nombre total de lignes
 * affichées.
 *
 * Utilisation :
 * Il faut dans un premier temps récupérer le jeu de résultats dans un tableau (voir les
 * commentaires du constructeur.)
 * L'exemple suivant est simplifié et part de l'utilisation d'une classe qui va exécuter la
 * requête SQL et retourner un tableau indexé :
 * <code>
 * $oDb = new mydb();
 * $sql = "SELECT * FROM matable WHERE condition = 1";
 * $oDb->execute($sql);
 * $aDatas = $oDb->fetchAssoc();
 *
 * </code>
 * On crée ensuite une instance en lui passant ce tableau en paramètre.
 * <code>
 * $oTable = new db_html_table($aDateFct);
 * </code>
 * Si l'instance existait déjà, on met à jour les informations avec le nouveau je de résultats à
 * afficher avec la méthode setNewTable().
 * <code>
 * $oTable->setNewTable($aDatas);
 * </code>
 * Enfin, on récupère le tableau HTML avec la méthode getTableau.
 * <code>
 * echo($oTable->getTableau());
 * </code>
 */
class db_html_table
{
    /**
     * Tableau de données à 3 dimensions de résultat de requête SQL.
     *
     * @var Array
     */
    private $_aInfos = array();
    /**
     * Table HTML d'affichage du contenu du tableau.
     *
     * @var String
     */
    private $_sTableau = '';

    /**
     * Définition de l'instance.
     *
     * Le tableau de données attendues sera de la forme suivante :
     * tableau => array(
     *   0 => array(
     *     'nom_colonne_1'   => 'valeur_1',
     *     'nom_colonne_2'   => 'valeur_2',
     *     'nom_colonne_...' => 'valeur_...',
     *     'nom_colonne_n'   => 'valeur_n',
     *   ),
     *   1 => array(
     *     'nom_colonne_1'   => 'valeur_1',
     *     'nom_colonne_2'   => 'valeur_2',
     *     'nom_colonne_...' => 'valeur_...',
     *     'nom_colonne_n'   => 'valeur_n',
     *   ),
     *   etc...
     * )
     *
     * @param Array $aInfos
     */
    public function __construct($aInfos = null)
    {
        $this->_aInfos = (!is_null($aInfos)) ? $aInfos : array();
    }

    /**
     * Remplacement des données par un nouveau tableau à 3 dimensions.
     *
     * Permet l'utilisation de l'instance en cours pour un autre jeu de résultat à afficher
     *
     * @param Array $aInfos
     */
    public function setNewTable($aInfos)
    {
        $this->_aInfos = $aInfos;
        $this->_sTableau = '';
    }

    /**
     * Construction dynamique d'une table HTML pour afficher
     * le contenu du tableau.
     *
     * @param  Array    $aInfos Optionnel, si absent, le jeu de résultat passé au constructeur sera utilisé.
     * @param  String   $sTable Optionnel, nom de la table ou de la vue qui servira de titre du tableau.
     * @return String
     */
    public function getTableau($aInfos = null, $sTable = null)
    {
        if(is_null($aInfos))
        {
            $aInfos = $this->_aInfos;
        }
        $nl = count($aInfos);
        $label = (isset($sTable)) ? 'Contenu de la table '. $sTable .' : ' : null;
        if($nl == 0):
            /* Tableau de données vide, donc on affiche simplement un message */
            $this->_sTableau = <<<CODE
    <p class="erreur">{$label}Pas de données disponibles pour ces critères.</p>
    <hr />

CODE;
        else:
            /* Il y a des données, on va identifier les noms des colonnes et leur nombre */
            $l1 = $aInfos[0];
            $aIndexes = array();
            /* On construit le header avec les noms des colonnes et le footer avec le nombre total de lignes */
            $this->_sTableau  = <<<CODE
    <table summary="{$label}">
      <caption>{$label}</caption>
      <thead>
        <tr>

CODE;
            foreach ($l1 as $index => $val):
                $aIndexes[] = $index;
                $this->_sTableau .= <<<CODE
          <th>{$index}</th>

CODE;
            endforeach;
            $nc = count($aIndexes);
            $s = ($nl > 1) ? 's' : null;
            $this->_sTableau .= <<<CODE
        </tr>
      </thead>
      <tfoot>
        <tr>
          <td colspan="{$nc}">{$nl} ligne{$s}</td>
        </tr>
      </tfoot>
      <tbody>

CODE;
            /* On construit maintenant les lignes de données */
            foreach($aInfos as $i => $ligne):
                $this->_sTableau .= <<<CODE
        <tr>

CODE;
                foreach ($aIndexes as $idcol):
                    $align = (is_numeric($ligne[$idcol])) ? ' style="text-align: right;"' : null;
                    $this->_sTableau .= <<<CODE
          <td{$align}>{$ligne[$idcol]}</td>

CODE;
                endforeach;
                $this->_sTableau .= <<<CODE
        </tr>

CODE;
            endforeach;
            $this->_sTableau .= <<<CODE
      </tbody>

CODE;
                $this->_sTableau .= <<<CODE
    </table>
    <hr />

CODE;
        endif;
        return($this->_sTableau);
    }
}
Ensuite, ben c'est pas très dur à utiliser, dans ton cas, si je reprends un tout petit bout de ton premier code, ça devrait ressembler à quelque chose dans ce style là :
<?php
$stmt = $pdo->query('SELECT * FROM test');
$stmt->setFetchMode(PDO::FETCH_ASSOC);
/* Ici est le bout important : on alimente le tableau des données à afficher */
$aDatas = array();
while(false != ($row = $stmt->fetch()))
{
    $aDatas[] = $row;
}
/* Inclusion de la classe et création de l'instance : */
include_once($classetableau);
$oTable = new db_html_table();
/* Affichage : */
echo($oTable->getTableau($aDatas, 'Test'));
C'est tout, je te laisse observer ce que ça donne, que ton tableau ait 1 ou 25 colonnes, et de 1 à 5 000 000 de lignes, ça fera l'affaire et s'il n'y a pas de résultat du tout, ben ce sera traité aussi.

J'ai largement commenté le code de la classe, je te laisse l'adapter si nécessaire ;)
Modifié en dernier par Cyrano le 11 nov. 2011, 20:31, modifié 1 fois.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

ViPHP
ViPHP | 5462 Messages

11 nov. 2011, 20:25

$aDatas = array();
while(false != ($row = $stmt->fetch()))
{
    $aDatas[] = $row;
}
fecthAll ?

Mammouth du PHP | 19672 Messages

11 nov. 2011, 20:30

$aDatas = array();
while(false != ($row = $stmt->fetch()))
{
    $aDatas[] = $row;
}
fecthAll ?
J'ai pas vérifié dans le détail mais c'est possible, l'idée générale, c'est qu'on va passer le tableau de données à la classe et la classe va construire le tableau HTML.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Mammouth du PHP | 19672 Messages

12 nov. 2011, 14:02

@moogli, ce truc ne t'inspire pas ? :-k
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

13 nov. 2011, 01:23

pour être j'ai pas testé, je passe beaucoup (trop) de temps dans les transports la semaine alors le week end c'est limité :)


mais sur le principe c'est exactement ce que je voulais, j'ai fait ça en moins bien et moins réutilisable :)

je regarderais lundi je pense que je vais avoir un peu de temps.

en tout cas merci pour l'info (et le taf qui va avec)

@+
Il en faut peu pour être heureux ......

Mammouth du PHP | 19672 Messages

13 nov. 2011, 02:41

Note bien que ça me rassure quand même un peu, au moins tu as vu la réponse ;)
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 8758 Messages

15 nov. 2011, 15:09

Note bien que ça me rassure quand même un peu, au moins tu as vu la réponse ;)
Au final c'est sur que c'est plus clair que ma sauce :)

J'ai juste modifié un peu l'affichage car ça me gène d'avoir un <caption></caption> quand je ne passe pas de second paramètre à "getTableau" (idem pour le "summary")
public function getTableau($aInfos = null, $sTable = null) {
        if (is_null($aInfos)) {
            $aInfos = $this->_aInfos;
        }
        $nl = count($aInfos);
        $label = (isset($sTable)) ? 'Contenu de la table ' . $sTable . ' :' : null;
        if ($nl == 0):
            /* Tableau de données vide, donc on affiche simplement un message */
            $this->_sTableau = <<<CODE
 <p class="erreur">{$label}Pas de données disponibles pour ces critères.</p>
 <hr />

CODE;
        else:
            // la ^^
           if ( !is_null($label)){
                $label = '<caption>'.$label.'</caption>';
                $summary = 'summary="'.$label.'"';
            }
            /* Il y a des données, on va identifier les noms des colonnes et leur nombre */
            $l1 = $aInfos[0];
            $aIndexes = array();
            /* On construit le header avec les noms des colonnes et le footer avec le nombre total de lignes */
            $this->_sTableau = <<<CODE
 <table {$summary}>
   {$label}
   <thead>
     <tr>

CODE;
            foreach ($l1 as $index => $val):
                $aIndexes[] = $index;
                $this->_sTableau .= <<<CODE
       <th>{$index}</th>

CODE;
            endforeach;
            $nc = count($aIndexes);
            $s = ($nl > 1) ? 's' : null;
            $this->_sTableau .= <<<CODE
     </tr>
   </thead>
   <tfoot>
     <tr>
       <td colspan="{$nc}">{$nl} ligne{$s}</td>
     </tr>
   </tfoot>
   <tbody>

CODE;
            /* On construit maintenant les lignes de données */
            foreach ($aInfos as $i => $ligne):
                $this->_sTableau .= <<<CODE
     <tr>

CODE;
                foreach ($aIndexes as $idcol):
                    $align = (is_numeric($ligne[$idcol])) ? ' style="text-align: right;"' : null;
                    $this->_sTableau .= <<<CODE
       <td{$align}>{$ligne[$idcol]}</td>

CODE;
                endforeach;
                $this->_sTableau .= <<<CODE
     </tr>

CODE;
            endforeach;
            $this->_sTableau .= <<<CODE
   </tbody>

CODE;
            $this->_sTableau .= <<<CODE
 </table>
 <hr />

CODE;
        endif;
        return($this->_sTableau);
    }
Par contre je ne comprend pas l'utilité de setNewTable vu que l'on peux renouveler les données avec getTableau, le plus simple pourrait être de supprimer le passage de paramètre a getTableau pour "séparer" les deux, sinon supprimer setNewTable ^^

après c'est du cosmétique, une amélioration serais de proposer d'indiquer les classes css des éléments si besoin (par exemple j'affiche les chiffres centré mais pas le texte etc etc) mais bon après c'est poussé le vice un peux loin mais pour une classe de rendu c'est sympa :)

@+
Il en faut peu pour être heureux ......