Laravel 4 - logging SQL queries - php

There are already several questions in regards to logging the SQL query in Laravel 4. But I've tried almost all of them and it's still not working the way I want.
Here's my situation
in my php view file, I make AJAX request to the server
The AJAX request is received and runs a RAW parameterized Postgres SQL query (e.g.
DB::select('select * from my_table where id=?', array(1))
If I use
Event::listen('illuminate.query', function($sql)
{
Log::error($sql);
});
I just get "select * from my_table where id=?" as the log message without the ID value actually populated.
If I use
$queries = DB::getQueryLog();
$last_query = end($queries);
Log::error(print_r($last_query, true));
I still don't have the final SQL query with the ID populated.
Finally, if I use a logging tool like https://github.com/loic-sharma/profiler - it doesn't display anything since I'm making an AJAX request.
Have I exhausted my options? Is there still another better way?

Here is what I am currently using for logging of sql queries. You should be able to drop this into your main routes file then add 'log' => true into your database config.
if (Config::get('database.log', false))
{
Event::listen('illuminate.query', function($query, $bindings, $time, $name)
{
$data = compact('bindings', 'time', 'name');
// Format binding data for sql insertion
foreach ($bindings as $i => $binding)
{
if ($binding instanceof \DateTime)
{
$bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
}
else if (is_string($binding))
{
$bindings[$i] = "'$binding'";
}
}
// Insert bindings into query
$query = str_replace(array('%', '?'), array('%%', '%s'), $query);
$query = vsprintf($query, $bindings);
Log::info($query, $data);
});
}
Thanks to Jeemusu answer for the bit about inserting the bindings into the prepared statement.

You should be able to find the bindings by passing $bindings as the second parameter of the Event function.
Event::listen('illuminate.query', function($sql, $bindings, $time){
echo $sql; // select * from my_table where id=?
print_r($bindings); // Array ( [0] => 4 )
echo $time; // 0.58
// To get the full sql query with bindings inserted
$sql = str_replace(array('%', '?'), array('%%', '%s'), $sql);
$full_sql = vsprintf($sql, $bindings);
});
In Laravel 3.x I think the event listener was called laravel.query

Continuing on #Collin James answer.
If you want to log to a seperate file only for sql, you can do it with this:
if (Config::get('database.log', false)) {
Event::listen('illuminate.query', function($query, $bindings, $time, $name) {
$data = compact('bindings', 'time', 'name');
// Format binding data for sql insertion
foreach ($bindings as $i => $binding) {
if ($binding instanceof \DateTime) {
$bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
} else if (is_string($binding)) {
$bindings[$i] = "'$binding'";
}
}
// Insert bindings into query
$query = str_replace(array('%', '?'), array('%%', '%s'), $query);
$query = vsprintf($query, $bindings);
$log = new Logger('sql');
$log->pushHandler(new StreamHandler(storage_path().'/logs/sql-' . date('Y-m-d') . '.log', Logger::INFO));
// add records to the log
$log->addInfo($query, $data);
});
}
With this at the top of your file:
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
This will log all your queries to a file named sql-YYYY-mm-dd.log in storage/logs/.

While the accepted answer stands correct, this answer explains how to update loic-sharma profiler when making Ajax requests using jQuery. Using this approach one doesn't need to read file logs.
Step 1
The first problem is to send the updated profiler data to the client on every Ajax-request. This can be solved using the "after" events of the Laravel application.
app/filters.php:
App::after(function($request, $response)
{
// If it's a JsonResponse and the profiler is enabled, include it in the response.
if($response instanceof \Illuminate\Http\JsonResponse && Profiler::isEnabled()) {
$profiler = Profiler::getFacadeRoot();
$profilerJson = json_encode($profiler->render());
$content = substr($response->getContent(), 0, -1) . ',"profiler":' . $profilerJson . '}';
$response->setContent($content);
}
});
The App::after filter will run upon every Laravel request. The first line of the closure above, makes sure that it will only continue if a the response is of type JsonResponse and the profiler is enabled. If that is the case, render the profiler and append the HTML to the JSON object.
Note: this code assumes that the returned JSON is a object. So it will fail for arrays: Response::json(array(1,2,3)).
Step 2
Now that the updated profiler HTML is being sent to the client, we must update the DOM with the new profiler HTML using javascript. This should happen every time the client gets a JSON response. jQuery provides global Ajax event handlers, which is perfect to achive this.
$(document).ajaxSuccess(function(event, request, settings) {
try {
json = jQuery.parseJSON(request.responseText);
if(json.profiler) {
renderProfiler(json.profiler);
}
} catch(error) {
// Its not JSON.
return;
}
});
Here's a method to replace the old profiler with the new one:
renderProfiler = function(data) {
// Remove previous
$anbu = $('.anbu');
$anbu.prev().remove(); // Removes <style> tag of profiler
$anbu.next().next().remove(); // Removes the inline script tag of profiler
$anbu.next().remove(); // Removes jQuery script tag by the profiler
$anbu.remove(); // Removes the <div> tag of profiler
$(document.body).append(data);
};
Using it
Now it is as simple as returning responses as:
return Response::json(array(
'user' => Auth::user()
));
Laravel will append the profiler HTML. The javascript will catch the JSON response and update the DOM. You will have the SQL queries and timings right on the web page.
Note
While the code is tested, there might be a bug or two. This is also not exactly how I do it. Instead of sending the HTML in the json response, I extend the object with the actual data from the profiler. On the client side I render the profiler using a mustache template.

While the question was originally targeted at Laravel 4, I still ended up here through google, but I'm using Laravel 5.
There are new ways to log all queries in Laravel 5 using Middleware, but if you prefer the same approach here is the same code provided by Collin James but working for Laravel 5
if (Config::get('database.log', false))
{
Event::listen('Illuminate\Database\Events\QueryExecuted', function($query)
{
$bindings = $query->bindings;
$time = $query->time;
$name = $query->connection->getName();
$data = compact('bindings', 'time', 'name');
// Format binding data for sql insertion
foreach ($bindings as $i => $binding)
{
if ($binding instanceof \DateTime)
{
$bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
}
else if (is_string($binding))
{
$bindings[$i] = "'$binding'";
}
}
// Insert bindings into query
$query = str_replace(array('%', '?'), array('%%', '%s'), $query->sql);
$query = vsprintf($query, $bindings);
Log::info($query, $data);
});
}

That's what I've been using:
DB::listen(function ($query, $bindings, $time, $connection) {
$fullQuery = vsprintf(str_replace(array('%', '?'), array('%%', '%s'), $query), $bindings);
$result = $connection . ' (' . $time . '): ' . $fullQuery;
dump($result);
// You can of course log the $result
});

Related

PDO update issue

I'm trying to write a bit of a controller to do some CRUD with PDO and have this issue where nothing happens and doesn't throw. I am pretty sure i am binding correctly but after dumping out, I can't see any issues with this and the table doesn't update.
Can someone please have a look?.
public function update($query, $array)
{
try
{
$stm = $this->conn->prepare($query);
foreach($array AS $key=>$value)
{
if(gettype($value) == "integer")
{
$stm->bindParam($key,$value,PDO::PARAM_INT);
}
if(gettype($value) == "string")
{
$stm->bindParam($key,$value,PDO::PARAM_STR);
}
}
$stm->execute();
return ($stm->rowCount()<=0) ? FALSE : $stm->rowCount();
}
catch(Exception $e)
{
echo 'Error with the query on line: ' . __LINE__ . ' in file: ' . __FILE__;
}
}
$test = new SQL('127.0.0.1', 'test', '*************', 'mike_test');
$pull = $test->update('UPDATE names SET name=:name WHERE id=:id;',[':name'=>'James',':id'=>2]);
The problem is the use of bindParam() which binds to a variable, and since you bind each parameter to the same variable ($value), you essentially use the last value for all parameters.
Solution: use bindValue().
I can't see any issues with this and the table doesn't update.
Then the ID value doesn't match anything in the table, as it did update on my test system.
You just made you code too bloated. But there is a rule: the more code you write, the more errors you introduce. So make this function this way
public function update($query, $array)
{
$stm = $this->conn->prepare($query);
$stm->execute($array);
return $stm->rowCount();
}

Where do cache calls go in MVC

So I'm adding Redis to an already developed project and I'm wondering where exactly to put these cache calls.
There are existing models, and I am wondering if I can just inject redis into the models and then wrap each query with cache code, like this:
$cacheKey = "table/{$id}";
// If table entity is not in cache
if (!$predis->exists($cacheKey)) {
// Pre-existing database code
$this->db->query('SELECT * FROM table WHERE table.id = "'.$id.'" ');
$query = $this->db->get();
$result = $query->result_array();
// Set entity in redis cache
$predis->set($cacheKey, json_encode($result[0]));
return $result[0];
}
// Return cached entity from redis
return json_decode($predis->get($cacheKey), true);
But I'm just wondering if this is a dirty hack, or in fact the best way of doing things, and is it the most appropriate place to put the cache code?
I've learned from previous projects that it's better to do things the right way, the first time around!
You should start with profiling your application first and finding which parts are most often called and which are slowest.
You will get best result if you cache whole HTML parts, not single database rows. ( http://kiss-web.blogspot.com/2016/02/memcached-vs-redisphpredis-vs-predis-vs.html ).
Personally I think that Cache::remeber is best pattern:
class Cache {
protected $connection;
public function __construct($options) {
$this->connection = new Client($options);
}
public function remember($key, $closure, $expiration = 86400) {
$result = $this->connection->get($key);
if($result!==false) {
return unserialize($result);
}
$result = $closure();
$this->connection->set($key, serialize($result), 'ex', $expiration);
return $result;
}
}
Now your code would look like:
$cacheKey = "table/{$id}";
return $cache->remember($cacheKey, function() use ($id) {
$this->db->query('SELECT * FROM table WHERE table.id = "'.$id.'" ');
$query = $this->db->get();
$result = $query->result_array();
return $result[0];
});

How to handle query errors in Zend?

I have a simple question.
I am working on Zend framework 2.
I am trying to make an AJAX call to fetch all booking data from table booking by passing the booking_id. Trouble is, the query is failing for reasons unknown. The actual query is complex and its working when I replace $booking_id with an actual booking_id like '22432'. Hence, I believe that the query is fine, there is some other issue.
But I don't know how to fetch the query errors/exceptions in an Ajax call. Can someone help me with this?
Javascript:
$.post("dashboard/getBookingDataByBookingId", {
booking_id: bookingId,
},
function(data){
if(data.response == true) {
alert(data);
} else {
alert('failed');
}
}, 'json');
Controller
public function getBookingDataByBookingIdAction()
{
$request = $this->getRequest();
$response = $this->getResponse();
if ($request->isPost())
{
$post_data = $request->getPost();
$booking_id = $post_data['booking_id'];
$booking_data = array();
$booking_data = $this->getBookingTable()->getBookingByUserIdAndBookingId($booking_id);
if (!$booking_data)
$response->setContent(\Zend\Json\Json::encode(array('response' => false, 'booking_data' => $booking_data)));
else {
$response->setContent(\Zend\Json\Json::encode(array('response' => true, 'booking_data' => $booking_data)));
}
}
return $response;
}
The bookingTable model has a public function:
public function getBookingByUserIdAndBookingId($booking_id)
{
$sql = "Select * from booking where id='".$booking_id."';
try {
$statement = $this->adapter->query($sql);
$res = $statement->execute();
return $res->current();
} catch (Exception $ex) {
return $ex;
}
}
You are posting a variable named 'id':
{
id: bookingId,
}
So you should access it as:
$post_data = $request->getPost();
$booking_id = $post_data['id'];
or more concisely:
$booking_id = $request->getPost('id');
You should also be using parameterised queries to avoid SQL injection.
For Getting errors/exceptions in an Ajax call use:
In Google Chrome use: POSTMAN Extension
In FireFox user: FIREBUG Plugin

ADODB / SQL Server mssql_execute stored procedure fails but works if debug is set to true

I am connecting to a SQL Server database from PHP using ADODB. I have several stored procedures that I have been executing just fine. The last call that I have created for some reason keeps failing. I put debug as true and it works just fine. I have no idea why this would be happening.
here is the error I get
Error: mssql_execute() [<a href='function.mssql-execute'>function.mssql-execute</a>]: stored procedure execution failed in /path/to/adodb/lib/adodb5/drivers/adodb-mssql.inc.php on line 768
Here is the method I created to pass all my stored procedures to, like I said it works fine with other stored procedures but this one and I have double checked that all the spellings of the parameters and stored procedure are correct.
protected function _getSpArray($db, $sp, array $spParams) {
try {
$stmt = $db->PrepareSP($sp);
foreach ($spParams as $param) {
$db->InParameter($stmt, $param[0], $param[1]);
}
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs = $db->Execute($stmt);
$rsArray = array();
if (!$rs) {
echo 'No records found \n';
return;
}
else {
foreach ($rs as $k => $row) {
$rsArray[$k] = $row;
}
}
return $rsArray;
} catch (ErrorException $e) {
echo $e->getMessage();
}
}
It is failing on this line #768 of adodb5/drivers/adodb-mssql.inc.php
$rez = mssql_execute($sql[1]);
and the sql array has these values
[0] stored_procedure_name
[1] resource id='657' type='mssql statement'
I have seen the following comments on PHP.net, I changed my freetds host name to be something different then the IP address and still nothing. I am not sure about the free result since I am using adodb.
http://www.php.net/manual/en/function.mssql-execute.php#93938
I am using adodb 5.11
When you set debug as true ADODB uses other function to execute the statement. In this case function _adodb_debug_execute(&$zthis, $sql, $inputarr)
If debug is set to false ADODB uses function &_Execute($sql,$inputarr=false). When you check the source for both methods you can clearly see the difference.
<?php
function _adodb_debug_execute(&$zthis, $sql, $inputarr)
{
//ADODB prepares debug information dump...
$qID = $zthis->_query($sql,$inputarr);
//Here ADODB makes the difference
if ($zthis->databaseType == 'mssql') {
// ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6
if($emsg = $zthis->ErrorMsg()) {
if ($err = $zthis->ErrorNo()) ADOConnection::outp($err.': '.$emsg);
}
} else if (!$qID) {
ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
}
if ($zthis->debug === 99) _adodb_backtrace(true,9999,2);
return $qID;
}
?>
Here is the _Execute function
<?php
function &_Execute($sql,$inputarr=false){
//Here ADODB chooses which fn to use
if ($this->debug) {
global $ADODB_INCLUDED_LIB;
if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
$this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
} else {
$this->_queryID = #$this->_query($sql,$inputarr);
}
//...
if ($this->_queryID === false) { // error handling if query fails
//If debug ADODB prints backtrace regardless the result
if ($this->debug == 99) adodb_backtrace(true,5);
$fn = $this->raiseErrorFn;
if ($fn) {
$fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
}
$false = false;
//Returns false no matter what...
return $false;
}
//...
}
?>
Try adding this to your script to test the behaviour of the script and keep in mind that if the execute fails it will return a false value. So take care with the returned value.
protected function _getSpArray($db, $sp, array $spParams) {
try {
$stmt = $db->PrepareSP($sp);
foreach ($spParams as $param) {
$db->InParameter($stmt, $param[0], $param[1]);
}
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs = $db->Execute($stmt);
$rsArray = array();
if (!$rs) {
echo 'No records found \n';
return;
}
else {
foreach ($rs as $k => $row) {
$rsArray[$k] = $row;
}
}
//add this line to free the resources after use.
mssql_free_result($rs);
return $rsArray;
} catch (ErrorException $e) {
echo $e->getMessage();
}
}
Hope it helps!!
Ok, after finding the php.net comments, even though my freetds wasn't setup with the proper name, I was using the ip address. I also wasn't using the V 8.0 I was using 7.0 and everything was working fine up until this point with the store procedures. I tried the mssql_free_result solution provide in the comments and DarkThrones answer and that didn't work but when I used adodb's close() method it is working. It uses free result and passes a query id.
If someone can comment as to why this is necessary that would be great. Is it because I was using up to much memory or something?

Doctrine - How to print out the real sql, not just the prepared statement?

We're using Doctrine, a PHP ORM. I am creating a query like this:
$q = Doctrine_Query::create()->select('id')->from('MyTable');
and then in the function I'm adding in various where clauses and things as appropriate, like this
$q->where('normalisedname = ? OR name = ?', array($string, $originalString));
Later on, before execute()-ing that query object, I want to print out the raw SQL in order to examine it, and do this:
$q->getSQLQuery();
However that only prints out the prepared statement, not the full query. I want to see what it is sending to the MySQL, but instead it is printing out a prepared statement, including ?'s. Is there some way to see the 'full' query?
Doctrine is not sending a "real SQL query" to the database server : it is actually using prepared statements, which means :
Sending the statement, for it to be prepared (this is what is returned by $query->getSql())
And, then, sending the parameters (returned by $query->getParameters())
and executing the prepared statements
This means there is never a "real" SQL query on the PHP side — so, Doctrine cannot display it.
A working example:
$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();
// SHOW SQL:
echo $query->getSQL();
// Show Parameters:
echo $query->getParameters();
You can check the query executed by your app if you log all the queries in mysql:
http://dev.mysql.com/doc/refman/5.1/en/query-log.html
there will be more queries not only the one that you are looking for but you can grep for it.
but usually ->getSql(); works
Edit:
to view all the mysql queries I use
sudo vim /etc/mysql/my.cnf
and add those 2 lines:
general_log = on
general_log_file = /tmp/mysql.log
and restart mysql
Edit 2
In case you dont find the mysql config (it can be in many places), just set those variables from mysql command line.
mysql -u root -p
SHOW VARIABLES LIKE 'general_log_file';
SHOW VARIABLES LIKE 'general_log';
SET GLOBAL general_log = 'on';
SET GLOBAL general_log_file = '/tmp/mysql.log';
//view the queries
sudo tail -f /tmp/mysql.log
The life of those settings is until MySQL is restarted. Or the laptop. So they are not permanent - which is great in my opinion - I just need them when I debug and I dont need to worry to edit the config then to remove them. If you dont remove the logging, it might grow too much if you forget about it.
I have created a Doctrine2 Logger that does exactly this. It "hydrates" the parametrized sql query with the values using Doctrine 2 own data type conversors.
<?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;
}
}
Usage Example:;
The following peace of code will echo on standard output any INSERT,UPDATE,DELETE SQL sentences generated with $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() does technically show the whole SQL command, but it's a lot more useful when you can see the parameters as well.
echo $q->getSqlQuery();
foreach ($q->getFlattenedParams() as $index => $param)
echo "$index => $param";
To make this pattern more reusable, there's a nice approach described in the comments at Raw SQL from Doctrine Query Object.
There is no other real query, this is how prepared statements work. The values are bound in the database server, not in the application layer.
See my answer to this question: In PHP with PDO, how to check the final SQL parametrized query?
(Repeated here for convenience:)
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
You can easily access the SQL parameters using the following approach.
$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,',');
So if you printed out the $param_values and $col_names , you can get the parameter values passing through the sql and respective column names.
Note : If $param returns an array, you need to re iterate, as parameters inside IN (:?) usually comes is as a nested array.
Meantime if you found another approach, please be kind enough to share with us :)
Thank you!
My 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 <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;}
Example of usage:
$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());
More clear solution:
/**
* 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);
}
You can use :
$query->getSQL();
If you are using MySQL you can use Workbench to view running SQL statements.
You can also use view the running query from mysql by using the following :
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);
TL;DR
$qb = ... // your query builder
$query = $qb->getQuery();
// temporarily enable logging for your query (will also work in prod env)
$conf = $query->getEntityManager()->getConnection()->getConfiguration();
$backupLogger = $conf->getSQLLogger();
$logger = new \Doctrine\DBAL\Logging\DebugStack();
$conf->setSQLLogger($logger);
// execute query
$res = $query->getResult();
$conf->setSQLLogger($backupLogger); //restore logger for other queries
$params = [
'query' => array_pop($logger->queries) //extract query log details
//your other twig params here...
]
return $params; //send this to your twig template...
in your twig files, use Doctrine's twig helpers filters:
// show raw query:
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)
// highlighted
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query(highlight_only = true) }}
// highlighted and formatted (i.e. with tabs and newlines)
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query }}
Explanation:
The other answers mentioning that Prepared statement are actually "real queries" are right, but they don't answer the obvious asker's expectation... Every developer wants to display a "runnable query" for debugging (or to display it to the user).
So, I looked into Symfony profiler's source to see how they do it. The Doctrine part is Doctrine's responsibility so they made a doctrine-bundle to integrate with Symfony. Having a look at the doctrine-bundle/Resources/views/Collector/db.html.twig file, you will find out how they do it (this might change across versions). Interestingly, they created twig filters that we can reuse (see above).
For everything to work we need to enable Logging for our query. There are multiple ways to do this and here I use DebugStack which allows to log queries without actually printing them. This also ensure that this will work in production mode if this is what you need...
If you need further formatting, you will see that they include some CSS in a style tag, so simply "steal" it ^^:
.highlight pre { margin: 0; white-space: pre-wrap; }
.highlight .keyword { color: #8959A8; font-weight: bold; }
.highlight .word { color: #222222; }
.highlight .variable { color: #916319; }
.highlight .symbol { color: #222222; }
.highlight .comment { color: #999999; }
.highlight .backtick { color: #718C00; }
.highlight .string { color: #718C00; }
.highlight .number { color: #F5871F; font-weight: bold; }
.highlight .error { color: #C82829; }
Hope, this will help ;-)
Maybe it can be useful for someone:
// 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;
I wrote a simple logger, which can log query with inserted parameters.
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;
I made some research for this topic, because i wanted to debug a generated SQL query and execute it in the sql editor. As seen in all the answers, it is a highly technical topic.
When i assume that the initial question is base on dev-env, one very simple answer is missing at the moment. You can just use the build in Symfony profiler. Just click on the Doctrine Tab, Scroll to the query you want to inspect. Then click on "view runnable query" and you can paste your query directly in your SQL editor
More UI base approach but very quick and without debugging code overhead.
$sql = $query->getSQL();
$parameters = [];
foreach ($query->getParameters() as $parameter) {
$parameters[] = $parameter->getValue();
}
$result = $connection->executeQuery($sql, $parameters)
->fetchAll();
Modified #dsamblas function to work when parameters are date strings like this '2019-01-01' and when there is array passed using IN like
$qb->expr()->in('ps.code', ':activeCodes'),
. So do everything what dsamblas wrote, but replace startQuery with this one or see the differences and add my code. (in case he modified something in his function and my version does not have 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;
}
}
Did not test much.
$sql = $query->getSQL();
$obj->mapDQLParametersNamesToSQL($query->getDQL(), $sql);
echo $sql;//to see parameters names in sql
$obj->mapDQLParametersValuesToSQL($query->getParameters(), $sql);
echo $sql;//to see parameters values in sql
public function mapDQLParametersNamesToSQL($dql, &$sql)
{
$matches = [];
$parameterNamePattern = '/:\w+/';
/** Found parameter names in DQL */
preg_match_all($parameterNamePattern, $dql, $matches);
if (empty($matches[0])) {
return;
}
$needle = '?';
foreach ($matches[0] as $match) {
$strPos = strpos($sql, $needle);
if ($strPos !== false) {
/** Paste parameter names in SQL */
$sql = substr_replace($sql, $match, $strPos, strlen($needle));
}
}
}
public function mapDQLParametersValuesToSQL($parameters, &$sql)
{
$matches = [];
$parameterNamePattern = '/:\w+/';
/** Found parameter names in SQL */
preg_match_all($parameterNamePattern, $sql, $matches);
if (empty($matches[0])) {
return;
}
foreach ($matches[0] as $parameterName) {
$strPos = strpos($sql, $parameterName);
if ($strPos !== false) {
foreach ($parameters as $parameter) {
/** #var \Doctrine\ORM\Query\Parameter $parameter */
if ($parameterName !== ':' . $parameter->getName()) {
continue;
}
$parameterValue = $parameter->getValue();
if (is_string($parameterValue)) {
$parameterValue = "'$parameterValue'";
}
if (is_array($parameterValue)) {
foreach ($parameterValue as $key => $value) {
if (is_string($value)) {
$parameterValue[$key] = "'$value'";
}
}
$parameterValue = implode(', ', $parameterValue);
}
/** Paste parameter values in SQL */
$sql = substr_replace($sql, $parameterValue, $strPos, strlen($parameterName));
}
}
}
}
You can build an sql string by combining the sql prepared statement with bindings like this way:
$sql = str_replace_array('?', $query->getBindings(), $query->toSql())
str_replace_array(string $search, array $replacement, string $subject): string
PHP's str_replace_array function replaces each instance of $search in $subject with values from $replacement array sequentially.
To print out an SQL query in Doctrine, use:
$query->getResult()->getSql();

Categories