Laravel如何通过绑定获取查询?

时间:2014-12-05 11:07:16

标签: laravel eloquent

我有一些查询需要使用查询构建器

传递给另一个查询
$query = DB::table('table')->whereIn('some_field', [1,2,30])->toSql();

Model::join(DB::raw("({$query}) as table"), function($join) {
    $join->on('model.id', '=', 'table.id');
})

应该以

结果
Select * from model join (select * from table where some_field in (1,2,30)) as table on model.id = table.id

但是没有传递绑定,这迫使我做

   $query = DB::table('table')->whereRaw('some_field in ('. join(',', [1,2,30]) .')')->toSql();

有时可能不安全。如何通过绑定获取查询?

14 个答案:

答案 0 :(得分:18)

查看getBindings()

上的Builder方法

getBindings()

$query = DB::table('table')->whereIn('some_field', [1,2,30]);

$sql = $query->toSql();

$bindings = $query->getBindings();

答案 1 :(得分:5)

public static function getQueries(Builder $builder)
{
    $addSlashes = str_replace('?', "'?'", $builder->toSql());
    return vsprintf(str_replace('?', '%s', $addSlashes), $builder->getBindings());
}

答案 2 :(得分:1)

以下函数通过将?括为'?'

,确保生成的SQL不会将绑定与列混淆
    public static function getFinalSql($query)
    {
        $sql_str = $query->toSql();
        $bindings = $query->getBindings();

        $wrapped_str = str_replace('?', "'?'", $sql_str);

        return str_replace_array('?', $bindings, $wrapped_str);
    }

答案 3 :(得分:1)

由于其他答案未正确引用表达式,因此这是我的方法。它使用属于当前数据库连接的转义功能。

它用相应的绑定一一替换问号,该绑定通过array_shift()从$ bindings中检索,在此过程中消耗了数组。请注意,必须通过引用传递$ bindings才能起作用。

function getSql($query)
{
        $bindings = $query->getBindings();

        return preg_replace_callback('/\?/', function ($match) use (&$bindings, $query) {
            return $query->getConnection()->getPdo()->quote(array_shift($bindings));
        }, $query->toSql());
}

答案 4 :(得分:1)

您可以执行以下操作:

$escapedBindings = array();

foreach($query->getBindings() as $item) {$escapedBindings[] = '"'.$item.'"';}

$sql_with_bindings = Str::replaceArray('?', $escapedBindings, $query->toSql());

答案 5 :(得分:1)

这是一个非常老的问题(2015年),但是由于这是Google的第一个结果,我认为也有必要提供我的解决方案,以防对下一个人有用。

雄辩的语言(我认为5.7以后,我还没有测试较新的或更早的版本)提供了一种方法来更改构建器的from来包装子查询:

# Taken from Illuminate/Database/Query/Builder.php - Line 272
public function fromSub($query, $as) {
    [$query, $bindings] = $this->createSub($query);

    return $this->fromRaw('('.$query.') as '.$this->grammar->wrapTable($as), $bindings);
}

但是,这需要\Illuminate\Database\Query\Builder的现有实例。为了制作一个空的,您可以执行以下操作:

use Illuminate\Database\Capsule\Manager as DB;

$fancy = DB::table("videogames")->where("uses_sdl2", 1);
$empty = DB::table(null);

# Wrap the fancy query and set it as the "from" clause for the empty one
# NOTE: the alias is required
$empty = $empty->fromSub($fancy, "performant_games");

这将保证绑定得到正确的处理,因为它们将由Eloquent本身处理。

答案 6 :(得分:1)

$sqlQuery = Str::replaceArray(
    '?',
    collect($query->getBindings())
        ->map(function ($i) {
            if (is_object($i)) {
                $i = (string)$i;
            }
            return (is_string($i)) ? "'$i'" : $i;
        })->all(),
    $query->toSql());

答案 7 :(得分:0)

如果要从查询日志中获取包含绑定的已执行查询:

\DB::enableQueryLog();
\DB::table('table')->get();
dd(str_replace_array('?', \DB::getQueryLog()[0]['bindings'], 
      \DB::getQueryLog()[0]['query']));

答案 8 :(得分:0)

我创建了这个功能。它是局部的,可能是未涵盖的参数,对我来说就足够了。
非常欢迎在评论中添加您的改进!

function getFullSql($query) {
  $sqlStr = $query->toSql();
  foreach ($query->getBindings() as $iter=>$binding) {

    $type = gettype($binding);
    switch ($type) {
      case "integer":
      case "double":
        $bindingStr = "$binding";
        break;
      case "string":
        $bindingStr = "'$binding'";
        break;
      case "object":
        $class = get_class($binding);
        switch ($class) {
          case "DateTime":
            $bindingStr = "'" . $binding->format('Y-m-d H:i:s') . "'";
            break;
          default:
            throw new \Exception("Unexpected binding argument class ($class)");
        }
        break;
      default:
        throw new \Exception("Unexpected binding argument type ($type)");
    }

    $currentPos = strpos($sqlStr, '?');
    if ($currentPos === false) {
      throw new \Exception("Cannot find binding location in Sql String for bundung parameter $binding ($iter)");
    }

    $sqlStr = substr($sqlStr, 0, $currentPos) . $bindingStr . substr($sqlStr, $currentPos + 1);
  }

  $search = ["select", "distinct", "from", "where", "and", "order by", "asc", "desc", "inner join", "join"];
  $replace = ["SELECT", "DISTINCT", "\n  FROM", "\n    WHERE", "\n    AND", "\n    ORDER BY", "ASC", "DESC", "\n  INNER JOIN", "\n  JOIN"];
  $sqlStr = str_replace($search, $replace, $sqlStr);

  return $sqlStr;
}

答案 9 :(得分:0)

Laravel现在可以直接在Builder上进行调试!!!

https://laravel.com/docs/queries#debugging

\App\User::where('age', '18')->dump();
\App\User::where('age', '18')->dd();

输出

"select * from `users` where `age` = ?"
[
    0 => "18"
]

答案 10 :(得分:0)

您可以将以下代码块定义为辅助函数,并在需要时使用。 它将数字和字符串值用引号绑定。

public static function getSqlWithBindings($query)
{
    return vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding) {
        return is_numeric($binding) ? $binding : "'{$binding}'";
    })->toArray());
}

示例:

$query = Document::where('model', 'contact')->where('model_id', '1');
dd(Document::getSqlWithBindings($query));

输出:

"select * from `document` where `model` = 'contact' and `model_id` = 1"

答案 11 :(得分:0)

Douglas.Sesar的答案为基础。

我发现我还需要将绑定放在单引号中,以便能够轻松将其粘贴到我的数据库IDE中。

$sql = $query->toSql();
$bindings = $query->getBindings();

$sql_with_bindings = preg_replace_callback('/\?/', function ($match) use ($sql, &$bindings) {
    return "'" . array_shift($bindings) . "'";
}, $sql);

答案 12 :(得分:-1)

将所有已插入绑定的查询从最慢查询到最快排序的日志输出到日志:

import ReactDOM from "react-dom";
import React from "react";

class Todo extends React.Component {
  state = {
    text1: "",
    text2: "",
    interval: null
  };

  componentDidMount() {
    this.setState({
      interval: setInterval(() => {
        const { text1, text2 } = this.state;

        const autoSaveObject = {
          text1,
          text2
        };

        console.log(JSON.stringify(autoSaveObject));
      }, 3000)
    });
  }

  componentWillUnmount() {
    clearInterval(this.state.interval);
  }

  render() {
    return (
      <div>
        <h1>TODO LIST</h1>
        <form>
          <input
            value={this.state.text1}
            onChange={e => this.setState({ text1: e.target.value })}
          />
          <input
            value={this.state.text2}
            onChange={e => this.setState({ text2: e.target.value })}
          />
        </form>
      </div>
    );
  }
}

ReactDOM.render(<Todo />, document.getElementById("root"));

答案 13 :(得分:-3)

这里都解释了..... https://ajcastro29.blogspot.com/2017/11/laravel-join-derived-tables-properly.html

我为那个东西创建了一个范围查询。我认为它也可以在宏中......

public function scopeJoinDerived($query, $derivedQuery, $table, $one, $operator = null, $two = null, $type = 'inner', $where = false)
{
    $query->join(DB::raw("({$derivedQuery->toSql()}) as `{$table}`"), $one, $operator, $two, $type, $where);
    $join = last($query->getQuery()->joins);
    $join->bindings =  array_merge($derivedQuery->getBindings(), $join->bindings);

    return $query;
}