web-dev-qa-db-fra.com

Doctrine - Comment imprimer le vrai SQL, pas seulement la déclaration préparée?

Nous utilisons Doctrine, un PHP ORM. Je crée une requête comme celle-ci:

$q = Doctrine_Query::create()->select('id')->from('MyTable');

puis dans la fonction que j'ajoute dans diverses clauses où et des choses appropriées, comme ceci

$q->where('normalisedname = ? OR name = ?', array($string, $originalString));

Plus tard, avant execute()-, cet objet de requête, je veux imprimer le SQL brut afin de l'examiner, et ceci:

$q->getSQLQuery();

Cependant, cela n'imprime que l'instruction préparée, pas la requête complète. Je veux voir ce qu’il envoie à MySQL, mais au lieu de cela, il affiche une instruction préparée, y compris ?. Y a-t-il un moyen de voir la requête 'complète'?

145
Rory

Doctrine n'envoie pas de "vraie requête SQL" au serveur de base de données: il utilise en réalité des instructions préparées, ce qui signifie:

  • Envoi de la déclaration, pour qu'elle soit préparée (c'est ce qui est retourné par $query->getSql())
  • Et, ensuite, envoi des paramètres (renvoyés par $query->getParameters())
  • et exécuter les déclarations préparées

Cela signifie qu'il n'y a jamais de "vraie" requête SQL du côté PHP. Doctrine ne peut donc pas l'afficher.

145
Pascal MARTIN

Un exemple..

$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();

Afficher SQL: $sql=$query->getSQL();

Afficher les paramètres: $parameters=$query->getParameters();

77
Andy.Diaz

Vous pouvez vérifier la requête exécutée par votre application si vous enregistrez toutes les requêtes dans mysql:

http://dev.mysql.com/doc/refman/5.1/en/query-log.html

il y aura plus de requêtes, non seulement celle que vous recherchez, mais vous pouvez le rechercher.

mais généralement ->getSql(); fonctionne

Modifier:

pour afficher toutes les requêtes mysql que j'utilise 

Sudo vim /etc/mysql/my.cnf 

et ajoutez ces 2 lignes: 

general_log = on
general_log_file = /tmp/mysql.log

et redémarrez mysql

33
alex toader

J'ai créé un enregistreur Doctrine2 qui fait exactement cela. Il "hydrate" la requête SQL paramétrée avec les valeurs à l'aide des converseurs de type de données propres à Doctrine 2.

<?php


namespace Drsm\Doctrine\DBAL\Logging;
use Doctrine\DBAL\Logging\SQLLogger,
    Doctrine\DBAL\Types\Type,
    Doctrine\DBAL\Platforms\AbstractPlatform;
/**
 * A SQL logger that logs to the standard output and
 * subtitutes params to get a ready to execute SQL sentence

 * @author  [email protected]
 */
class EchoWriteSQLWithoutParamsLogger implements SQLLogger

{
    const QUERY_TYPE_SELECT="SELECT";
    const QUERY_TYPE_UPDATE="UPDATE";
    const QUERY_TYPE_INSERT="INSERT";
    const QUERY_TYPE_DELETE="DELETE";
    const QUERY_TYPE_CREATE="CREATE";
    const QUERY_TYPE_ALTER="ALTER";

    private $dbPlatform;
    private $loggedQueryTypes;
    public function __construct(AbstractPlatform $dbPlatform, array $loggedQueryTypes=array()){
        $this->dbPlatform=$dbPlatform;
        $this->loggedQueryTypes=$loggedQueryTypes;
    }
    /**
     * {@inheritdoc}
     */
    public function startQuery($sql, array $params = null, array $types = null)

    {
        if($this->isLoggable($sql)){
            if(!empty($params)){
                foreach ($params as $key=>$param) {
                    $type=Type::getType($types[$key]);
                    $value=$type->convertToDatabaseValue($param,$this->dbPlatform);
                    $sql = join(var_export($value, true), explode('?', $sql, 2));
                }

            }
            echo $sql . " ;".PHP_EOL;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function stopQuery()
    {

    }
    private function isLoggable($sql){
        if (empty($this->loggedQueryTypes)) return true;
        foreach($this->loggedQueryTypes as $validType){
            if (strpos($sql, $validType) === 0) return true;
        }
        return false;
    }
}

Exemple d'utilisation:; La paix de code suivante s'appliquera à la sortie standard des phrases INSERT, UPDATE, DELETE SQL générées avec $ em Entity Manager, 

/**@var  \Doctrine\ORM\EntityManager $em */
$em->getConnection()
                ->getConfiguration()
                ->setSQLLogger(
                    new EchoWriteSQLWithoutParamsLogger(
                        $em->getConnection()->getDatabasePlatform(),
                        array(
                            EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_UPDATE,
                            EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_INSERT,
                            EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_DELETE
                        )
                    )
                );
16
dsamblas

getSqlQuery() affiche techniquement l’ensemble de la commande SQL, mais c’est beaucoup plus utile lorsque vous pouvez également voir les paramètres.

echo $q->getSqlQuery();
foreach ($q->getFlattenedParams() as $index => $param)
  echo "$index => $param";

Pour rendre ce modèle plus réutilisable, il existe une approche de Nice décrite dans comments dans Raw SQL de Doctrine Query Object .

13
ladenedge

Il n'y a pas d'autre requête réelle, voici comment fonctionnent les instructions préparées. Les valeurs sont liées dans le serveur de base de données, pas dans la couche d'application.

Voir ma réponse à cette question: Sous PHP avec PDO, comment vérifier la requête paramétrée SQL finale?

(Répété ici pour plus de commodité :)

L'utilisation d'instructions préparées avec des valeurs paramétrées n'est pas simplement un autre moyen de créer dynamiquement une chaîne de code SQL. Vous créez une instruction préparée dans la base de données, puis vous ne transmettez que les valeurs de paramètre.

Donc ce qui est probablement envoyé à la base de données sera un PREPARE ..., puis un SET ... et enfin un EXECUTE ....

Vous ne pourrez pas obtenir une chaîne SQL telle que SELECT * FROM ..., même si cela produirait des résultats équivalents, car aucune requête de ce type n'a été envoyée à la base de données.

13
Ben James

Ma solution:

 /**
 * Get SQL from query
 * 
 * @author Yosef Kaminskyi 
 * @param QueryBilderDql $query
 * @return int
 */
public function getFullSQL($query)
{
    $sql = $query->getSql();
    $paramsList = $this->getListParamsByDql($query->getDql());
    $paramsArr =$this->getParamsArray($query->getParameters());
    $fullSql='';
    for($i=0;$i<strlen($sql);$i++){
        if($sql[$i]=='?'){
            $nameParam=array_shift($paramsList);

            if(is_string ($paramsArr[$nameParam])){
                $fullSql.= '"'.addslashes($paramsArr[$nameParam]).'"';
             }
            elseif(is_array($paramsArr[$nameParam])){
                $sqlArr='';
                foreach ($paramsArr[$nameParam] as $var){
                    if(!empty($sqlArr))
                        $sqlArr.=',';

                    if(is_string($var)){
                        $sqlArr.='"'.addslashes($var).'"';
                    }else
                        $sqlArr.=$var;
                }
                $fullSql.=$sqlArr;
            }elseif(is_object($paramsArr[$nameParam])){
                switch(get_class($paramsArr[$nameParam])){
                    case 'DateTime':
                             $fullSql.= "'".$paramsArr[$nameParam]->format('Y-m-d H:i:s')."'";
                          break;
                    default:
                        $fullSql.= $paramsArr[$nameParam]->getId();
                }

            }
            else                     
                $fullSql.= $paramsArr[$nameParam];

        }  else {
            $fullSql.=$sql[$i];
        }
    }
    return $fullSql;
}

 /**
 * Get query params list
 * 
 * @author Yosef Kaminskyi <[email protected]>
 * @param  Doctrine\ORM\Query\Parameter $paramObj
 * @return int
 */
protected function getParamsArray($paramObj)
{
    $parameters=array();
    foreach ($paramObj as $val){
        /* @var $val Doctrine\ORM\Query\Parameter */
        $parameters[$val->getName()]=$val->getValue();
    }

    return $parameters;
}
 public function getListParamsByDql($dql)
{
    $parsedDql = preg_split("/:/", $dql);
    $length = count($parsedDql);
    $parmeters = array();
    for($i=1;$i<$length;$i++){
        if(ctype_alpha($parsedDql[$i][0])){
            $param = (preg_split("/[' ' )]/", $parsedDql[$i]));
            $parmeters[] = $param[0];
        }
    }

    return $parmeters;}

Exemple d'utilisation: 

$query = $this->_entityRepository->createQueryBuilder('item');
$query->leftJoin('item.receptionUser','users');
$query->where('item.customerid = :customer')->setParameter('customer',$customer)
->andWhere('item.paymentmethod = :paymethod')->setParameter('paymethod',"Bonus");
echo $this->getFullSQL($query->getQuery());
7
moledet

Vous pouvez facilement accéder aux paramètres SQL en utilisant l’approche suivante. 

   $result = $qb->getQuery()->getSQL();

   $param_values = '';  
   $col_names = '';   

   foreach ($result->getParameters() as $index => $param){              
            $param_values .= $param->getValue().',';
            $col_names .= $param->getName().',';
   } 

   //echo rtrim($param_values,',');
   //echo rtrim($col_names,',');    

Ainsi, si vous imprimez les $param_values et $col_names, vous pouvez obtenir les valeurs des paramètres en passant par les noms de colonne SQL et respectifs.

Remarque: Si $param renvoie un tableau, vous devez effectuer une nouvelle itération, car les paramètres à l'intérieur de IN (:?) sont généralement des tableaux imbriqués.

En attendant, si vous avez trouvé une autre approche, merci de bien vouloir partager avec nous :)

Je vous remercie!

7
Anjana Silva

Solution plus claire:

 /**
 * Get string query 
 * 
 * @param Doctrine_Query $query
 * @return string
 */
public function getDqlWithParams(Doctrine_Query $query){
    $vals = $query->getFlattenedParams();
    $sql = $query->getDql();
    $sql = str_replace('?', '%s', $sql);
    return vsprintf($sql, $vals);
}
6
dudapiotr

Vous pouvez utiliser :

$query->getSQL();

Si vous utilisez MySQL, vous pouvez utiliser Workbench pour afficher les instructions SQL en cours d'exécution . Vous pouvez également utiliser l'affichage de la requête en cours d'exécution à partir de mysql à l'aide des éléments suivants:

 SHOW FULL PROCESSLIST \G
5
lac_dev
Solution:1
====================================================================================

function showQuery($query)
{
    return sprintf(str_replace('?', '%s', $query->getSql()), $query->getParams());
}

// call function  
echo showQuery($doctrineQuery);

Solution:2
====================================================================================

function showQuery($query)
{
    // define vars              
    $output    = NULL;
    $out_query = $query->getSql();
    $out_param = $query->getParams();

    // replace params
   for($i=0; $i<strlen($out_query); $i++) {
       $output .= ( strpos($out_query[$i], '?') !== FALSE ) ? "'" .str_replace('?', array_shift($out_param), $out_query[$i]). "'" : $out_query[$i];
   }

   // output
   return sprintf("%s", $output);
}

// call function  
echo showQuery($doctrineQueryObject);
4
Sandip Patel

Peut-être que cela peut être utile pour quelqu'un:

// Printing the SQL with real values
$vals = $query->getFlattenedParams();
foreach(explode('?', $query->getSqlQuery()) as $i => $part) {
    $sql = (isset($sql) ? $sql : null) . $part;
    if (isset($vals[$i])) $sql .= $vals[$i];
}

echo $sql;
4
wcomnisky

J'ai écrit un simple enregistreur, qui peut enregistrer une requête avec les paramètres insérés . Installation:

composer require cmyker/doctrine-sql-logger:dev-master

Usage:

$connection = $this->getEntityManager()->getConnection(); 
$logger = new \Cmyker\DoctrineSqlLogger\Logger($connection);
$connection->getConfiguration()->setSQLLogger($logger);
//some query here
echo $logger->lastQuery;
1
Cmyker
$sql = $query->getSQL();

$parameters = [];
    foreach ($query->getParameters() as $parameter) {
        $parameters[] = $parameter->getValue();
    }

$result = $connection->executeQuery($sql, $parameters)
        ->fetchAll();
1
slk500

Fonction @dsamblas modifiée pour fonctionner lorsque les paramètres sont des chaînes de date telles que '2019-01-01' et lorsqu'un tableau est transmis à l'aide de IN comme

$qb->expr()->in('ps.code', ':activeCodes'),

. Donc, faites tout ce que dsamblas a écrit, mais remplacez startQuery par celui-ci ou voyez les différences et ajoutez mon code. (au cas où il aurait modifié quelque chose dans sa fonction et que ma version n’aurait pas de modifications).

public function startQuery($sql, array $params = null, array $types = null)

{
    if($this->isLoggable($sql)){
        if(!empty($params)){
            foreach ($params as $key=>$param) {

                try {
                    $type=Type::getType($types[$key]);
                    $value=$type->convertToDatabaseValue($param,$this->dbPlatform);
                } catch (Exception $e) {
                    if (is_array($param)) {
                        // connect arrays like ("A", "R", "C") for SQL IN
                        $value = '"' . implode('","', $param) . '"';
                    } else {
                        $value = $param; // case when there are date strings
                    }
                }

                $sql = join(var_export($value, true), explode('?', $sql, 2));
            }

        }
        echo $sql . " ;".PHP_EOL;
    }
}

Je n'ai pas beaucoup testé.

0
Darius.V