Php Laravel DB::语句无法为涉及列表达式的查询正确准备值

Php Laravel DB::语句无法为涉及列表达式的查询正确准备值,php,mysql,laravel,laravel-5,eloquent,Php,Mysql,Laravel,Laravel 5,Eloquent,我想知道DB::statement和DB::unprepared查询之间出现的问题,因此显然DB::statement方法无法处理对列本身执行表达式的查询,类似这样的情况: 更新标记集?=?+2在哪里?>?用户_id=?和tree_id= 这将导致SQL为: 更新标记集rgt=rgt+2,其中rgt>2,用户id=1,树id=1 ^当在mysql解释器中使用此查询时,它工作得非常好,但使用laravel的DB::statement方法(使用未准备好的方法btw时,它工作得很好)会让它变得糟糕 这

我想知道DB::statement和DB::unprepared查询之间出现的问题,因此显然DB::statement方法无法处理对列本身执行表达式的查询,类似这样的情况:

更新标记集?=?+2在哪里?>?用户_id=?和tree_id=

这将导致SQL为:

更新标记集rgt=rgt+2,其中rgt>2,用户id=1,树id=1

^当在mysql解释器中使用此查询时,它工作得非常好,但使用laravel的DB::statement方法(使用未准备好的方法btw时,它工作得很好)会让它变得糟糕

这种不匹配背后的原因是什么

它弹出的错误是
SQLSTATE[42000]:语法错误或访问冲突:1064您的SQL语法中有一个错误
,这很奇怪,因为它的语法正确,并且已经可以与mysql一起正常工作

所以语句方法就是这样做的:

    {
        return $this->run($query, $bindings, function ($query, $bindings) {
            if ($this->pretending()) {
                return true;
            }
            $statement = $this->getPdo()->prepare($query);
            $this->bindValues($statement, $this->prepareBindings($bindings));
            $this->recordsHaveBeenModified();
            return $statement->execute();
        });
    }
而毫无准备的人:

    {
        return $this->run($query, [], function ($query) {
            if ($this->pretending()) {
                return true;
            }
            $this->recordsHaveBeenModified(
                $change = ($this->getPdo()->exec($query) === false ? false : true)
            );
            return $change;
        });
    }
我认为问题在于
prepare
方法

在其他地方发布的参考资料也无法确定问题:

用户建议高级查询应使用未准备好的方法?此查询尽可能基本:

另一位用户建议使用unprepared,而不是分解为更简单的查询:

奖金问题: 此外,如果你知道任何有说服力的方法来处理这个问题,那就太棒了!(从某种意义上讲,如何通过本机、where、update等方法处理列级表达式。)

编辑: 这个问题不是重复的,因为它仍然涉及对列值进行硬编码<代码>数据库::原始('column*2')

我的问题纯粹是不做任何硬编码语句,让laravel做绑定

例如:此查询-

DB::update(DB::raw(
            'UPDATE tags SET ? = ? ? ? WHERE ? >= ? AND user_id = ? AND tree_id = ?'),
            [$flag, $flag, $operator, $integer, $flag, $controlIndex, $user_id, $tree_id]
        );
产生如下错误:
SQLSTATE[42000]:语法错误或访问冲突:1064您的SQL语法有错误;请查看与MySQL服务器版本对应的手册,以了解在“?=?”附近使用的正确语法?在哪里?用户_id=?第1行的tree_id=?(SQL:UPDATE tags SET rgt=rgt+2,其中rgt>=1,user_id=1,tree_id=1)


只需看看SQL查询:
更新标记集rgt=rgt+2,其中rgt>=1,user_id=1,tree_id=1
——这是完全合法的,如果放在mysql解释器中就可以使用

好的,经过进一步检查,我发现不能绑定表/列名,只能绑定查询中的值

更多参考:

要理解为什么绑定表(或列)名不起作用,您必须理解预处理语句中的占位符是如何工作的:它们不是简单地替换为(适当转义的)字符串,而是执行结果SQL。相反,被要求“准备”语句的DBMS会给出一个完整的查询计划,说明它将如何执行该查询,包括它将使用哪些表和索引,无论您如何填写占位符,这些表和索引都是相同的

看起来你也不能绑定数据库名称——所以这不是Laravel或PHP的缺陷,而是数据库的设计方式

从my_表中选择名称(其中id=:value)的计划将与替换:value的内容相同,但无法计划看似相似的从:表中选择名称(其中id=:value),因为DBMS不知道实际要从哪个表中选择

因此,应该做的是拥有一个列的白名单,并确保在执行查询之前根据该白名单进行过滤。向开发人员提供了防止任何SQL注入的任务。我本以为Laravel可能会实现基于列的绑定,但它可能有自己的优缺点

  • 运行查询以查找正在进行列绑定的情况下的所有列
  • 将白名单作为模型类本身中的某种变量,如
    $filleble
    存在
  • 然而,我能想出的最优雅的解决方案如下:

    DB::table('tags')->where([
                ['user_id', $user_id],
                ['tree_id', $tree_id],
                [$flag, '>', $controlIndex]
            ])->update([
                $flag => DB::raw($flag . $operator . $integer)
            ]);
    
    Tags::where([
                ['user_id', $user_id],
                ['tree_id', $tree_id],
                [$flag, '>', $controlIndex]])
        ->crement($flag, $integer, $operator);
    
    
    其中,
    $flag
    $operator
    $integer
    只是枚举

    更新:

    当有人向我推荐
    递增
    递减
    方法时,我在一些聊天论坛上问了同样的问题,这完全符合问题的解决方案!因此,我的更新查询调用如下所示:

    DB::table('tags')->where([
                ['user_id', $user_id],
                ['tree_id', $tree_id],
                [$flag, '>', $controlIndex]
            ])->update([
                $flag => DB::raw($flag . $operator . $integer)
            ]);
    
    Tags::where([
                ['user_id', $user_id],
                ['tree_id', $tree_id],
                [$flag, '>', $controlIndex]])
        ->crement($flag, $integer, $operator);
    
    

    crement
    只是一个附加到
    Tags
    模型的局部作用域方法,用于指示调用是递增还是递减。

    可能重复的调用可以这样做@Sahil-我刚刚编辑了这个问题,请注意,您链接的方法仍然涉及到对列的表达式进行硬编码,特别是
    DB::raw('column*2')
    ,有关更多上下文,请参阅问题的末尾,我在此处添加了编辑部分以解决您的评论。