You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何禁止Laravel查询构造器执行INSERT语句,仅允许SELECT语句

嘿,这个需求太常见了——不管是做只读数据服务、权限隔离,还是怕误操作搞坏数据,都得限制Laravel查询构造器只执行SELECT语句。我给你分享几个实用的实现方案,你可以根据自己的场景挑选:

方案1:给查询构造器打「只读补丁」(自定义宏)

这种方式最简单快速,直接拦截查询构造器的所有写操作方法,一旦调用就抛出异常或者返回错误提示。

你只需要在App\Providers\AppServiceProvider的boot方法里注册几个宏,覆盖掉insert、update、delete这些写操作的方法:

use Illuminate\Database\Query\Builder;

public function boot()
{
    // 拦截INSERT操作
    Builder::macro('insert', function () {
        throw new \RuntimeException('禁止执行INSERT操作,当前仅允许SELECT查询');
    });

    // 拦截UPDATE操作
    Builder::macro('update', function () {
        throw new \RuntimeException('禁止执行UPDATE操作,当前仅允许SELECT查询');
    });

    // 拦截DELETE操作
    Builder::macro('delete', function () {
        throw new \RuntimeException('禁止执行DELETE操作,当前仅允许SELECT查询');
    });

    // 按需拦截其他写操作,比如insertGetId、updateOrInsert等
    Builder::macro('insertGetId', function () {
        throw new \RuntimeException('禁止执行INSERT操作,当前仅允许SELECT查询');
    });
}

优缺点:

  • 优点:零侵入,不用修改现有模型或查询代码,几分钟就能搞定。
  • 缺点:只能拦截查询构造器的方法,如果有人直接用DB::statement()执行原生写SQL,这个拦不住。

方案2:从数据库底层锁死(只读用户)

这是最安全的方案——直接给应用分配一个只有SELECT权限的数据库账号,从根源上禁止写操作。

以MySQL为例,创建只读用户的SQL语句如下:

-- 创建只读用户
CREATE USER 'laravel_readonly'@'%' IDENTIFIED BY 'your_strong_password';
-- 给该用户分配指定数据库的SELECT权限
GRANT SELECT ON your_database_name.* TO 'laravel_readonly'@'%';
-- 刷新权限
FLUSH PRIVILEGES;

然后在.env文件里把数据库账号换成这个只读用户:

DB_USERNAME=laravel_readonly
DB_PASSWORD=your_strong_password

优缺点:

  • 优点:绝对安全,不管代码里写什么写操作,数据库都会直接拒绝,连原生SQL也拦得住。
  • 缺点:如果你的应用部分功能需要写操作,就得配置双数据库连接(一个只读、一个读写),稍微麻烦一点。

方案3:给特定模型定制只读查询构造器

如果只是部分模型需要只读(比如日志、统计类模型),可以给这些模型单独定制一个只读的查询构造器。

首先创建一个自定义的只读构造器类:

namespace App\Database;

use Illuminate\Database\Query\Builder as BaseBuilder;

class ReadOnlyBuilder extends BaseBuilder
{
    // 重写INSERT方法
    public function insert(array $values)
    {
        throw new \RuntimeException('该模型仅允许执行SELECT查询,禁止写操作');
    }

    // 重写UPDATE方法
    public function update(array $values)
    {
        throw new \RuntimeException('该模型仅允许执行SELECT查询,禁止写操作');
    }

    // 重写DELETE方法
    public function delete()
    {
        throw new \RuntimeException('该模型仅允许执行SELECT查询,禁止写操作');
    }

    // 按需重写其他写操作方法
}

然后在需要只读的模型里,指定使用这个构造器:

use App\Database\ReadOnlyBuilder;
use Illuminate\Database\Eloquent\Model;

class Log extends Model
{
    /**
     * 创建自定义查询构造器实例
     */
    protected function newBaseQueryBuilder()
    {
        $connection = $this->getConnection();
        return new ReadOnlyBuilder(
            $connection,
            $connection->getQueryGrammar(),
            $connection->getPostProcessor()
        );
    }
}

优缺点:

  • 优点:精准控制,只影响指定模型,其他模型正常使用写操作。
  • 缺点:需要给每个只读模型单独配置,或者创建一个基类模型让所有只读模型继承。

总结一下

  • 如果整个应用都是只读的:优先用数据库只读用户,安全可靠。
  • 如果只是部分查询需要只读:用自定义宏快速实现。
  • 如果只是部分模型需要只读:用自定义查询构造器精准控制。

内容的提问来源于stack exchange,提问作者paranoid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:54:28