Laravel Eloquent firstOrCreate在Linux服务器失效,本地Windows正常
问题分析
你遇到的问题是firstOrCreate在Linux服务器上抛出SQLSTATE[HY000]: General error: 2036异常,核心原因是生成的SQL中字符串类型的字段(ip_address和data)没有被正确添加引号,导致数据库无法识别合法的查询语法。而Windows本地环境正常,大概率是环境间的PHP类型处理差异或MySQL配置差异(比如严格模式)导致的。
从你提供的实际执行SQL可以看到:
select * from
search_historieswhere (ip_address= 27.34.72.242 anddata= {"address":"Surry Hills, New South Wales, Australia","radius":"10",...}) limit 1
这里ip_address被当作数值处理(无引号),data的JSON字符串也没有被包裹引号,完全不符合SQL语法规范。
解决方法
1. 强制转换字段值为字符串类型
显式将ip_address和data的值转换为字符串,确保Laravel的查询构造器将它们识别为字符串参数,自动添加引号:
// 强制转换IP为字符串,避免被识别为数值 $ipAddress = (string) request()->ip(); // 生成JSON字符串并确保类型为string,防止类型歧义 $queryData = (string) json_encode(request()->query(), JSON_UNESCAPED_UNICODE); $search = SearchHistory::firstOrCreate( [ 'ip_address' => $ipAddress, 'data' => $queryData ], [ 'user_id' => auth()->id() ?? 0, 'user_agent' => request()->userAgent() ] );
2. 检查数据库字段类型(可选优化)
如果你的data字段设计为MySQL的JSON类型,建议直接传入数组而非json_encode后的字符串,Laravel会自动处理JSON字段的查询逻辑,避免手动编码可能带来的问题:
$search = SearchHistory::firstOrCreate( [ 'ip_address' => (string) request()->ip(), 'data' => request()->query() // 直接传数组,Laravel自动生成合法的JSON查询 ], [ 'user_id' => auth()->id() ?? 0, 'user_agent' => request()->userAgent() ] );
3. 验证MySQL sql_mode配置(辅助排查)
检查Linux服务器上的MySQLsql_mode设置,确保没有过于严格的模式导致语法错误被放大(虽然这不是根本原因,但可以辅助确认环境差异):
SELECT @@sql_mode;
如果包含STRICT_TRANS_TABLES等严格模式,可临时调整测试,但不建议长期关闭严格模式,最好从代码层面规范类型处理。
为什么Windows环境正常?
Windows下PHP对字符串类型的判断可能更宽松,或者本地MySQL的sql_mode未启用严格语法检查,使得不规范的SQL(无引号字符串)侥幸执行成功。但这种写法本身存在隐患,必须从代码层面规范类型处理。
内容的提问来源于stack exchange,提问作者Sanish Gurung

