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'?
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:
$query->getSql()
)$query->getParameters()
)Cela signifie qu'il n'y a jamais de "vraie" requête SQL du côté PHP. Doctrine ne peut donc pas l'afficher.
Un exemple..
$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();
Afficher SQL: $sql=$query->getSQL();
Afficher les paramètres: $parameters=$query->getParameters();
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
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
)
)
);
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 .
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 unSET ...
et enfin unEXECUTE ....
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.
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());
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!
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);
}
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
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);
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;
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;
$sql = $query->getSQL();
$parameters = [];
foreach ($query->getParameters() as $parameter) {
$parameters[] = $parameter->getValue();
}
$result = $connection->executeQuery($sql, $parameters)
->fetchAll();
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é.