php - query - sql symfony 3
Doctrine-¿Cómo imprimir el sql real, no solo la declaración preparada? (14)
Estamos usando Doctrine, un PHP ORM. Estoy creando una consulta como esta:
$q = Doctrine_Query::create()->select(''id'')->from(''MyTable'');
y luego en la función que estoy agregando en varias cláusulas donde y cosas como sea apropiado, como este
$q->where(''normalisedname = ? OR name = ?'', array($string, $originalString));
Más tarde, antes de execute()
-ing ese objeto de consulta, quiero imprimir el SQL sin procesar para examinarlo, y hacer esto:
$q->getSQLQuery();
Sin embargo, eso solo imprime la declaración preparada, no la consulta completa. Quiero ver lo que está enviando a MySQL, pero en su lugar está imprimiendo una declaración preparada, ?
incluida ?
es ¿Hay alguna forma de ver la consulta ''completa''?
Doctrine no está enviando una "consulta SQL real" al servidor de la base de datos: en realidad está utilizando declaraciones preparadas, lo que significa:
- Enviar la declaración, para que esté preparada (esto es lo que devuelve
$query->getSql()
) - Y, luego, enviando los parámetros (devuelto por
$query->getParameters()
) - y ejecutar las declaraciones preparadas
Esto significa que nunca hay una consulta SQL "real" en el lado de PHP, por lo que Doctrine no puede mostrarla.
Escribí un registrador simple, que puede registrar la consulta con los parámetros insertados. Instalación:
composer require cmyker/doctrine-sql-logger:dev-master
Uso:
$connection = $this->getEntityManager()->getConnection();
$logger = new /Cmyker/DoctrineSqlLogger/Logger($connection);
$connection->getConfiguration()->setSQLLogger($logger);
//some query here
echo $logger->lastQuery;
He creado un registrador de Doctrine2 que hace exactamente esto. "Hidrata" la consulta sql parametrizada con los valores utilizando conversores de tipos de datos propios de 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;
}
}
Ejemplo de uso :; La siguiente paz de código hará eco en la salida estándar de INSERT, UPDATE, DELETE oraciones SQL generadas con $ 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
)
)
);
Mi solución:
/**
* 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;}
Ejemplo de uso:
$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());
No hay otra consulta real, así es como funcionan las declaraciones preparadas. Los valores están vinculados en el servidor de la base de datos, no en la capa de la aplicación.
Ver mi respuesta a esta pregunta: en PHP con PDO, ¿cómo verificar la consulta final parametrizada de SQL?
(Se repitió aquí por conveniencia :)
El uso de declaraciones preparadas con valores parametrizados no es simplemente otra forma de crear dinámicamente una cadena de SQL. Crea una declaración preparada en la base de datos y luego envía los valores de los parámetros solo.
Entonces lo que probablemente se envíe a la base de datos será
PREPARE ...
, luegoSET ...
y finalmenteEXECUTE ....
No podrá obtener ninguna cadena SQL como
SELECT * FROM ...
, incluso si produzca resultados equivalentes, porque nunca se envió dicha consulta a la base de datos.
Para imprimir una consulta SQL en Doctrine, use:
$query->getResult()->getSql();
Puede acceder fácilmente a los parámetros de SQL utilizando el siguiente enfoque.
$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,'','');
Por lo tanto, si imprimió los $param_values
y $col_names
, puede obtener los valores de los parámetros que pasan por el sql y los respectivos nombres de columna.
Nota: Si $param
devuelve una matriz, necesita volver a iterar, ya que los parámetros dentro de IN (:?)
Generalmente vienen como una matriz anidada.
Mientras tanto, si encuentra otro enfoque, tenga la amabilidad de compartirlo con nosotros :)
¡Gracias!
Puede verificar la consulta ejecutada por su aplicación si registra todas las consultas en mysql:
http://dev.mysql.com/doc/refman/5.1/en/query-log.html
Habrá más consultas, no solo la que está buscando, sino que puede buscarla.
pero generalmente ->getSql();
trabajos
Editar:
para ver todas las consultas de mysql que uso
sudo vim /etc/mysql/my.cnf
y agrega esas 2 líneas:
general_log = on
general_log_file = /tmp/mysql.log
y reiniciar mysql
Puedes usar :
$query->getSQL();
Si está utilizando MySQL, puede usar Workbench para ver las sentencias SQL en ejecución. También puede usar view the running query desde mysql usando lo siguiente:
SHOW FULL PROCESSLIST /G
Tal vez puede ser útil para alguien:
// 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;
Un ejemplo..
$qb = $this->createQueryBuilder(''a'');
$query=$qb->getQuery();
Mostrar SQL: $sql=$query->getSQL();
Mostrar parámetros: $parameters=$query->getParameters();
Una solución más clara:
/**
* 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);
}
getSqlQuery()
muestra técnicamente todo el comando SQL, pero es mucho más útil cuando puede ver los parámetros también.
echo $q->getSqlQuery();
foreach ($q->getFlattenedParams() as $index => $param)
echo "$index => $param";
Para que este patrón sea más reutilizable, hay un buen enfoque descrito en los comments en Raw SQL desde Doctrine Query Object .
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);