Доктрина-как распечатать реальный sql, а не только подготовленный оператор?
мы используем доктрину, PHP ORM. Я создаю такой запрос:
$q = Doctrine_Query::create()->select('id')->from('MyTable');
а затем в функции я добавляю в различные предложения where и вещи по мере необходимости, как это
$q->where('normalisedname = ? OR name = ?', array($string, $originalString));
позже, перед execute()
- в этом объекте запроса я хочу распечатать необработанный SQL, чтобы изучить его, и сделать это:
$q->getSQLQuery();
однако это только выводит подготовленный оператор, а не полный запрос. Я хочу посмотреть, что он отправляет в MySQL, но вместо этого он распечатывает подготовленное заявление, в том числе ?
's. Есть ли способ увидеть' полный ' запрос?
14 ответов:
Doctrine не отправляет" реальный SQL-запрос " на сервер базы данных : он фактически использует подготовленные операторы, что означает:
- отправка заявления, для его подготовки (это то, что возвращается
$query->getSql()
)- и, затем, отправка параметров (возвращается
$query->getParameters()
)- и выполнение подготовленных операторов
это означает, что на стороне PHP никогда не существует "реального" SQL-запроса-поэтому доктрина не может отображаться оно.
пример..
$qb = $this->createQueryBuilder('a'); $query=$qb->getQuery();
показать SQL:
$sql=$query->getSQL();
Показать Параметры:
$parameters=$query->getParameters();
вы можете проверить запрос, выполняемый вашим приложением, Если вы регистрируете все запросы в mysql:
http://dev.mysql.com/doc/refman/5.1/en/query-log.html
там будет больше запросов не только тот, который вы ищете, но вы можете grep для него.
но обычно
->getSql();
работаетEdit:
для просмотра всех запросов mysql я использую
sudo vim /etc/mysql/my.cnf
и добавьте эти 2 строки:
general_log = on general_log_file = /tmp/mysql.log
и перезапустить mysql
Я создал Doctrine2 регистратор, который делает именно это. Он "гидратирует" параметризованный sql-запрос со значениями, используя собственные конверсоры типа данных 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 dsamblas@gmail.com */ 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; } }
Пример Использования :; Следующий мир кода будет эхом на стандартном выходе любая вставка, обновление, удаление предложений SQL, созданных с помощью $ 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 ) ) );
другого реального запроса нет, так работают подготовленные операторы. Значения привязываются к серверу баз данных, а не к прикладному уровню.
смотрите мой ответ на этот вопрос: в PHP с PDO, как проверить окончательный SQL параметризованный запрос?
(повторяется здесь для удобства:)
использование подготовленных операторов с параметризованными значениями-это не просто еще один способ динамического создания строки SQL. Вы создаете подготовленный оператор в базе данных, а затем отправить значения параметров в одиночку.
так что, вероятно, отправлено в базу данных будет
PREPARE ...
, потомSET ...
и наконецEXECUTE ....
вы не сможете получить некоторые строки SQL, как
SELECT * FROM ...
, даже если это приведет к эквивалентным результатам, потому что такой запрос никогда не был отправлен в базу данных.
getSqlQuery()
технически показывает всю команду SQL, но это намного полезнее, когда вы также можете видеть параметры.echo $q->getSqlQuery(); foreach ($q->getFlattenedParams() as $index => $param) echo "$index => $param";
чтобы сделать этот шаблон более многоразовым, есть хороший подход, описанный в комментарии at Raw SQL из объекта запроса доктрины.
более четкое решение:
/** * 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); }
мое решение:
/** * 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 <yosefk@spotoption.com> * @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;}
пример использования:
$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());
вы можете легко получить доступ к параметрам SQL, используя следующий подход.
$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,',');
так что если вы распечатали
$param_values
и$col_names
, вы можете получить значения параметров, проходящих через SQL и соответствующие имена столбцов.Примечание : Если
$param
возвращает массив, вам нужно повторить итерацию, как параметры внутриIN (:?)
обычно приходит как вложенный массив.а если вы нашли другой подход, пожалуйста, будьте любезны, поделитесь с нами :)
спасибо!
Вы можете использовать :
$query->getSQL();
Если вы используете MySQL, вы можете использовать Workbench для просмотра запущенных инструкций SQL. Вы также можете использовать просмотр запущенного запроса из mysql, используя следующее:
SHOW FULL PROCESSLIST \G
может быть, это может быть полезно для кого-то:
// 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;
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);
Я написал простой регистратор, который может регистрировать запрос со вставленными параметрами. Установка:
composer require cmyker/doctrine-sql-logger:dev-master
использование:
$connection = $this->getEntityManager()->getConnection(); $logger = new \Cmyker\DoctrineSqlLogger\Logger($connection); $connection->getConfiguration()->setSQLLogger($logger); //some query here echo $logger->lastQuery;