본문 바로가기
Programming/Backend

[Laravel] Eloquent ORM, Query Builder로 실행한 full query 확인

by BitSense 2020. 5. 30.
반응형

개발 중 자주 쿼리를 확인하는 일이 있다. 그런데 ->toSql()로 하게 되면 바인딩 전 쿼리만 나와서 해당 값이 정상 들어갔는지 확인이 어려울 수 있다.

이번에는 mysql 4.x old 버전에 쿼리 질의를 할 일이 생겼는데, php 5.x 버전 부터 mysql 4.x 버전을 지원하지 않고, 어느 언어를 사용하더라도 4.x 버전을 미지원이라는 장벽을 맞이하게 되었다. 결국 원격으로 php 5.x 저 버전에 raw 쿼리를 던져서 질의 하도록 미들웨어 개념의 api를 만들었다. 기본 어드민 툴은 laravel에서 사용을 해야 하니 raw query를 생성해서 던지는 작업을 해야하는 경우가 생긴 것이다.

머 이유가 어찌 됐건 일단 바인딩된 최종 raw query 획득을 위한 작업을 진행하기 위해서 설정 작업이 필요하다. Laravel에서 eloquent orm 이나 query builder로 쿼리를 실행하게 되면 QueryExecuted 이벤트가 발생을 한다. 해당 이벤트 발생시 실행된 쿼리를 수집하기 위해서는 AppServiceProvider.php에 추가를 한다.

Event 추가

// app/Provides/AppServiceProvider.php

use App\Http\Models\Query; // 추가한 Query class

    public function register()
    {
        //
        \Event::listen('Illuminate\Database\Events\QueryExecuted', function ($query) {
            Query::set($query);
        });
    }

 

Query Class 추가

class Query
{
    static $query;

    //
    static function get()
    {
        return self::debug_query();
    }

    static function set($query)
    {
        self::$query = $query;
    }
    
     /**
     * query build 내용을 raw query로 변경
     * @param $query
     * @return mixed|string
     */
    static function debug_query() {
        $queryRaw = self::$query;

        $query = vsprintf(str_replace('?', '%s', $queryRaw->sql), collect($queryRaw->bindings)->map(function ($binding) {
            return is_numeric($binding) ? $binding : "'{$binding}'";
        })->toArray());

        $double_linebreak_words = ['(', ')'];
        $double_linebreak_words_replace = array_map(function($str){ return PHP_EOL . $str . PHP_EOL; }, $double_linebreak_words);
        $query = str_replace($double_linebreak_words, $double_linebreak_words_replace, $query);


        $mysql_keywords = ['ADD', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC', 'AUTO_INCREMENT', 'BDB', 'BERKELEYDB', 'BETWEEN', 'BIGINT', 'BINARY', 'BLOB', 'BOTH', 'BTREE', 'BY', 'CASCADE', 'CASE', 'CHANGE', 'CHAR', 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN', 'COLUMNS', 'CONSTRAINT', 'CREATE', 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'DATABASE', 'DATABASES', 'DAY_HOUR', 'DAY_MINUTE', 'DAY_SECOND', 'DEC', 'DECIMAL', 'DEFAULT', 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DISTINCT', 'DISTINCTROW', 'DIV', 'DOUBLE', 'DROP', 'ELSE', 'ENCLOSED', 'ERRORS', 'ESCAPED', 'EXISTS', 'EXPLAIN', 'FALSE', 'FIELDS', 'FLOAT', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULLTEXT', 'FUNCTION', 'GEOMETRY', 'GRANT', 'GROUP', 'HASH', 'HAVING', 'HELP', 'HIGH_PRIORITY', 'HOUR_MINUTE', 'HOUR_SECOND', 'IF', 'IGNORE', 'INDEX', 'INFILE', 'INNER', 'INNODB', 'INSERT', 'INTEGER', 'INTERVAL', 'INTO', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LEADING', 'LEFT', 'LIKE', 'LIMIT', 'LINES', 'LOAD', 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCK', 'LONG', 'LONGBLOB', 'LONGTEXT', 'LOW_PRIORITY', 'MASTER_SERVER_ID', 'MATCH', 'MEDIUMBLOB', 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT', 'MINUTE_SECOND', 'MOD', 'MRG_MYISAM', 'NATURAL', 'NOT', 'NULL', 'NUMERIC', 'ON', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', 'ORDER', 'OUTER', 'OUTFILE', 'PRECISION', 'PRIMARY', 'PRIVILEGES', 'PROCEDURE', 'PURGE', 'READ', 'REAL', 'REFERENCES', 'REGEXP', 'RENAME', 'REPLACE', 'REQUIRE', 'RESTRICT', 'RETURNS', 'REVOKE', 'RIGHT', 'RLIKE', 'RTREE', 'SELECT', 'SET', 'SHOW', 'SMALLINT', 'SOME', 'SONAME', 'SPATIAL', 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT', 'SSL', 'STARTING', 'STRAIGHT_JOIN', 'STRIPED', 'TABLE', 'TABLES', 'TERMINATED', 'THEN', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'TO', 'TRAILING', 'TRUE', 'TYPES', 'UNION', 'UNIQUE', 'UNLOCK', 'UNSIGNED', 'UPDATE', 'USAGE', 'USE', 'USER_RESOURCES', 'USING', 'VALUES', 'VARBINARY', 'VARCHAR', 'VARCHARACTER', 'VARYING', 'WARNINGS', 'WHEN', 'WHERE', 'WITH', 'WRITE', 'XOR', 'YEAR_MONTH', 'ZEROFILL', 'INT', 'OR', 'IS', 'IN'];
        $mysql_keywords = array_map(function($str){ return " $str "; }, $mysql_keywords);
        $mysql_keywords_lc = array_map(function($str){ return strtolower($str); }, $mysql_keywords);
        $query = str_replace($mysql_keywords_lc, $mysql_keywords, $query);


        $linebreak_before_words = ['INNER JOIN', 'LEFT JOIN', 'OUTER JOIN', 'RIGHT JOIN', 'WHERE', 'FROM', 'GROUP BY', 'SELECT'];
        $linebreak_before_words_replace = array_map(function($str){ return PHP_EOL . $str; }, $linebreak_before_words);
        $query = str_replace($linebreak_before_words, $linebreak_before_words_replace, $query);


        $linebreak_after_words = [','];
        $linebreak_after_words_replace = array_map(function($str){ return $str . PHP_EOL; }, $linebreak_after_words);
        $query = str_replace($linebreak_after_words, $linebreak_after_words_replace, $query);

        $query = str_replace('select ', 'SELECT ', $query);

        return $query;
    }
 }

사용 방법

namespace App\Http\Models;

use User;
use Illuminate\Database\Eloquent\Model;

class test extends Model
{
    public index()
    {
        $user = new User;
        
        $user->first();
        
        $query = Query::get();
        
        echo $query;
    }
}

결과

SELECT * FROM `users` LIMIT 1

 

 

 

반응형