Doctrine - Bagaimana cara mencetak sql yang asli, bukan hanya pernyataan yang disiapkan?


167

Kami menggunakan Doctrine, sebuah ORM PHP. Saya membuat kueri seperti ini:

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

dan kemudian dalam fungsi saya menambahkan berbagai klausa mana dan hal-hal yang sesuai, seperti ini

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

Kemudian, sebelum execute()-ing objek permintaan itu, saya ingin mencetak SQL mentah untuk memeriksanya, dan lakukan ini:

$q->getSQLQuery();

Namun itu hanya mencetak pernyataan yang disiapkan, bukan permintaan penuh. Saya ingin melihat apa yang dikirimkannya ke MySQL, tetapi sebaliknya ia mencetak pernyataan yang sudah disiapkan, termasuk ?. Apakah ada cara untuk melihat kueri 'lengkap'?


Cara terbaik yang saya temukan untuk melihat permintaan penuh dijelaskan dalam jawaban ini: stackoverflow.com/a/678310/229077
Marek

Anda dapat mengambil keuntungan dari pekerjaan yang dilakukan oleh Doctrine (profiler menampilkan kueri runnable). Lihat jawaban saya di bawah ini untuk perincian
Vincent Pazeller

Jawaban:


164

Doctrine tidak mengirim "permintaan SQL nyata" ke server database: itu sebenarnya menggunakan pernyataan yang disiapkan, yang berarti:

  • Mengirim pernyataan, untuk dipersiapkan (inilah yang dikembalikan oleh $query->getSql())
  • Dan, kemudian, mengirim parameter (dikembalikan oleh $query->getParameters())
  • dan melaksanakan pernyataan yang disiapkan

Ini berarti tidak pernah ada permintaan SQL "nyata" di sisi PHP - jadi, Doktrin tidak dapat menampilkannya.


14
Pascal: Anda tidak harus mengatakan itu bukan "permintaan SQL nyata" karena pernyataan yang disiapkan adalah permintaan SQL nyata, hanya saja parameter yang dikirim secara terpisah. Kata-kata ini dapat membingungkan orang (mis. Olivierpons.fr/2014/03/22/symfony-2-avantages-et-inconvenients ).
Matthieu Napoli

$query->getParameters();TIDAK akan mengembalikan parameter dalam urutan yang benar, karena akan muncul dalam pernyataan permintaan yang disiapkan
gondo

4
Saya pikir di sini penulis pertanyaan tidak peduli doktrin yang dikirim atau tidak. Apa yang ingin saya dan pengguna ketahui adalah bagaimana mendapatkan kueri yang dapat kita salin tempel dan jalankan tanpa harus secara manual mengganti tanda tanya dengan parameter. Seperti di codeigniter. Saya pikir saya telah menemukan ini di symfony debugger, tetapi saya masih tidak dapat menemukan ketika saya menjalankan skrip dari baris perintah.
Darius.V

104

Contoh kerja:

$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();
// SHOW SQL: 
echo $query->getSQL(); 
// Show Parameters: 
echo $query->getParameters();

5
Meskipun berfungsi sebagai penugasan variabel, Anda mungkin ingin mempertimbangkan hal ini: print $ query-> getSQL (); foreach ($ query-> getParameters () sebagai $ param) {print "{$ param-> getName ()} -> {$ param-> getValue ()} \ n"; } karena Anda akan mendapatkan hasil yang lebih mudah dibaca
Justin Finkelstein

itu memberi sedikit manfaat. Ketika saya menyalin sql, saya masih memiliki parameter pencarian wichi di mana untuk memasukkan secara manual, dibutuhkan banyak waktu. Kami ingin kueri dengan parameterrs yang dimasukkan, mengapa kami tidak dapat menemukannya selama ini? Bahkan dalam kerangka codeigniter sejauh yang saya ingat, di profiler Anda dapat menyalin kueri dan langsung berjalan tanpa secara manual. Kita perlu hal yang sama pada symfony.
Darius.V

35

Anda dapat memeriksa kueri yang dieksekusi oleh aplikasi Anda jika Anda mencatat semua pertanyaan di mysql:

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

akan ada lebih banyak pertanyaan tidak hanya yang Anda cari tetapi Anda dapat menangkapnya.

tapi biasanya ->getSql();berhasil

Edit:

untuk melihat semua permintaan mysql yang saya gunakan

sudo vim /etc/mysql/my.cnf 

dan tambahkan 2 baris itu:

general_log = on
general_log_file = /tmp/mysql.log

dan mulai kembali mysql


17

Saya telah membuat Logger Doctrine2 yang melakukan ini. Ini "menghidrasi" kueri sql parametrized dengan nilai menggunakan konversi tipe data Doctrine 2 sendiri.

<?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;
    }
}

Contoh Penggunaan :; Kedamaian kode berikut akan menggemakan output standar setiap INSERT, UPDATE, DELETE kalimat SQL yang dihasilkan dengan $ 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
                        )
                    )
                );

1
Tidak berfungsi ketika parameter adalah string tanggal seperti '2019-01-01'
Darius.V

14

getSqlQuery() secara teknis menunjukkan seluruh perintah SQL, tetapi jauh lebih berguna ketika Anda dapat melihat parameternya juga.

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

Untuk membuat pola ini lebih dapat digunakan kembali, ada pendekatan bagus yang dijelaskan dalam komentar di Raw SQL dari Doctrine Query Object .


Saya tahu ini adalah posting lama, tetapi kedua tautan Anda mengarah ke halaman 404. Bisakah Anda memperbarui jawaban Anda? Saya bertanya, karena saya tidak yakin dengan apa yang Anda maksud $q. Tampaknya bukan kueri atau pembuat kueri.
k00ni

1
Saya khawatir saya tidak dapat menemukan kode yang lebih dapat digunakan kembali. $qdalam hal ini adalah kueri Ajaran 1. Anda mungkin menggunakan Doctrine 2, dalam hal ini Anda akan menginginkan sesuatu seperti $qb = $this->createQueryBuilder('a'); $q = $qb->getQuery(); $sql = $q->getSQL(); $params = $q->getParameters(); Semoga bermanfaat!
ladenedge

13

Tidak ada permintaan nyata lainnya, ini adalah bagaimana pernyataan disiapkan bekerja. Nilai-nilai terikat di server basis data, bukan di lapisan aplikasi.

Lihat jawaban saya untuk pertanyaan ini: Dalam PHP dengan PDO, bagaimana cara memeriksa kueri parametrized SQL final?

(Diulang di sini untuk kenyamanan :)

Menggunakan pernyataan yang disiapkan dengan nilai parametris bukan hanya cara lain untuk secara dinamis membuat string SQL. Anda membuat pernyataan yang disiapkan di database, dan kemudian mengirim nilai parameter sendirian.

Jadi apa yang mungkin dikirim ke database akan menjadi PREPARE ..., dulu SET ...dan akhirnyaEXECUTE ....

Anda tidak akan bisa mendapatkan beberapa string SQL SELECT * FROM ..., bahkan jika itu akan menghasilkan hasil yang setara, karena tidak ada permintaan seperti itu yang pernah dikirim ke database.


9

Solusi saya:

 /**
 * 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;}

Contoh penggunaan:

$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());

Terima kasih untuk ini: D
Saad Achemlal

sangat bagus. berfungsi dengan kueri normal tetapi saya telah mendapatkan kueri dengan regexp dan sepertinya tidak mendukung $ qb = $ this-> createQueryBuilder ('r') -> innerJoin ('r.profile', 'p') -> addSelect (' p ') -> di mana (' REGEXP (: fileNamePattern, r.fileNamePattern) = 1 ') -> dan Di mana (' p.incomingLocation =: incomingLocation ') -> setParameters ([' fileNamePattern '=> $ fileName,' incomingLocation ' => $ location]) -> getQuery ();
Fahim

Tidak berfungsi dengan semua kueri. Ketika saya memiliki ini -> setParameters (array ('insuranceCarrier' => $ insuranceCarrier, 'dateFrom' => format $ dateFrom-> ('Ym-d'), 'dateTo' => $ dateTo-> format ('Ym- d '),)) yang tersisa? tanda dalam sql.
Darius.V

9

Anda dapat dengan mudah mengakses parameter SQL menggunakan pendekatan berikut.

   $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,',');    

Jadi, jika Anda mencetak $param_valuesdan $col_names, Anda bisa mendapatkan nilai parameter melewati sql dan nama kolom masing-masing.

Catatan: Jika $parammengembalikan array, Anda harus mengulanginya, karena parameter di dalam IN (:?)biasanya datang sebagai array bersarang.

Sementara itu jika Anda menemukan pendekatan lain, silakan berbaik hati untuk berbagi dengan kami :)

Terima kasih!


6

Solusi yang lebih jelas:

 /**
 * 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);
}

$ query-> getFlattenedParams (); tidak ada
Pengembang

5
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);

5

Kamu bisa memakai :

$query->getSQL();

Jika Anda menggunakan MySQL, Anda bisa menggunakan Workbench untuk melihat menjalankan pernyataan SQL. Anda juga dapat menggunakan tampilan kueri yang berjalan dari mysql dengan menggunakan yang berikut ini:

 SHOW FULL PROCESSLIST \G

4

Mungkin ini bisa bermanfaat bagi seseorang:

// 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;

2

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...

dalam file ranting Anda, gunakan filter pembantu ranting Doctrine:

// 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 }}

Penjelasan:

Jawaban lain yang menyebutkan bahwa pernyataan Disiapkan sebenarnya "permintaan nyata" benar, tetapi mereka tidak menjawab harapan penanya yang jelas ... Setiap pengembang ingin menampilkan "kueri runnable" untuk debugging (atau untuk menampilkannya kepada pengguna) .

Jadi, saya melihat ke sumber profiler Symfony untuk melihat bagaimana mereka melakukannya. Bagian Doktrin adalah tanggung jawab Doktrin sehingga mereka membuat kumpulan doktrin untuk diintegrasikan dengan Symfony. Setelah melihat doctrine-bundle/Resources/views/Collector/db.html.twigfile tersebut, Anda akan mengetahui bagaimana mereka melakukannya (ini dapat berubah antar versi). Menariknya, mereka membuat filter ranting yang dapat kita gunakan kembali (lihat di atas).

Agar semuanya berfungsi, kita perlu mengaktifkan Logging untuk permintaan kita. Ada beberapa cara untuk melakukan ini dan di sini saya menggunakan DebugStack yang memungkinkan untuk mencatat permintaan tanpa benar-benar mencetaknya. Ini juga memastikan bahwa ini akan berfungsi dalam mode produksi jika ini yang Anda butuhkan ...

Jika Anda membutuhkan pemformatan lebih lanjut, Anda akan melihat bahwa mereka menyertakan beberapa CSS dalam tag gaya, jadi cukup "curi" itu ^^:

.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; }

Semoga, ini akan membantu ;-)


1

Saya menulis logger sederhana, yang dapat mencatat permintaan dengan parameter yang dimasukkan. Instalasi:

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

Pemakaian:

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

1
$sql = $query->getSQL();

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

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

Anda harus menambahkan beberapa teks ke jawaban Anda menjelaskan apa yang kode lakukan.
DarkMukke

0

Modifikasi @dsamblas berfungsi ketika parameter adalah string tanggal seperti ini '2019-01-01' dan ketika ada array yang dilewatkan menggunakan IN seperti

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

. Jadi lakukan semua yang ditulis dsamblas, tetapi ganti startQuery dengan yang ini atau lihat perbedaannya dan tambahkan kode saya. (kalau-kalau dia memodifikasi sesuatu dalam fungsinya dan versi saya tidak memiliki modifikasi).

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;
    }
}

Tidak banyak tes.


0

Saya membuat beberapa riset untuk topik ini, karena saya ingin men-debug query SQL yang dihasilkan dan menjalankannya di editor sql. Seperti yang terlihat di semua jawaban, ini adalah topik yang sangat teknis.

Ketika saya berasumsi bahwa pertanyaan awal didasarkan pada dev-env, satu jawaban yang sangat sederhana hilang pada saat ini. Anda bisa menggunakan build di profiler Symfony. Cukup klik pada Tab Doctrine, Gulir ke kueri yang ingin Anda periksa. Kemudian klik pada "view runnable query" dan Anda dapat menempelkan kueri Anda langsung di editor SQL Anda

Lebih banyak pendekatan berbasis UI tetapi sangat cepat dan tanpa overhead kode debug.

masukkan deskripsi gambar di sini


0
$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));
            }
        }
    }
}

-1

Untuk mencetak kueri SQL di Doctrine, gunakan:

$query->getResult()->getSql();

jangan lupa untuk menambahkan deskripsi dengan Jawaban Anda? Hanya satu liner tanpa deskripsi, tidak dapat diterima.
HaveNoDisplayName

1
Untuk mencetak kueri sql di Doctrine gunakan $ query-> getResult () -> getSql (); Terima kasih
Jaydeep Patel

2
alih-alih menambahkan commnet, Edit jawaban Anda
HaveNoDisplayName
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.