在Doctrine/Symfony 5中按条件查询持久化多维数组字段
问题描述
我尝试从数据库中持久化的多维数组字段里检索数据,数组结构如下:
$valid = [ "technical" => [ "sender" => [ "uid" => "first.last", "date" => new \DateTime(), "recipient" => 75306 ], "receiver" => [] ] ];
这个数组在数据库里以longtext字段存储,序列化后的内容示例:
a:1:{s:9:"technical";a:2:{s:6:"sender";a:4:{s:3:"uid";s:10:"first.last";s:4:"date";O:8:"DateTime":3:{s:4:"date";s:26:"2023-05-02 09:47:39.229914";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/Berlin";}s:9:"recipient";i:75306;}s:8:"receiver";a:0:{}}}
我想用Doctrine的QueryBuilder通过SQL查询Valid实体,条件是technical.sender.recipient等于$unity,写的代码如下:
return $this->createQueryBuilder('r') ->join('r.valid','valids') ->where('valids.technical.sender.recipient IN (:unity)') ->setParameter('unity', $unity) ->getQuery() ->getResult();
但运行时报错:Class App\Entity\Valid has no field or association named technical.sender.recipient,试了直接写字段和partials都没用。
解决方案
因为你的technical是序列化后存在数据库的字符串,Doctrine没法直接识别嵌套数组属性,给你三种处理方案:
方法1:临时用SQL字符串匹配
利用PHP序列化字符串的特征,直接匹配包含目标值的片段(注意如果recipient是字符串类型,要调整匹配规则):
return $this->createQueryBuilder('r') ->join('r.valid', 'valids') // 替换your_serialized_field为实体中存储序列化数组的字段名 ->where('valids.your_serialized_field LIKE :recipient') ->setParameter('recipient', '%s:9:"recipient";i:' . (int)$unity . ';%') ->getQuery() ->getResult();
注意:这种方法性能差,大数据量场景别用;而且如果PHP序列化格式变了(比如字符串长度计算规则调整),匹配会失效。
方法2:改用JSON字段存储(推荐长期方案)
如果你的数据库支持JSON类型(比如MySQL 5.7+、PostgreSQL),可以把字段改成JSON类型:
- 修改实体字段:
// Valid实体类中 /** * @ORM\Column(type="json") */ private $technical;
- 执行数据库迁移,把原序列化数据批量转成JSON格式(可以写个脚本处理)。
- 之后就能用数据库的JSON函数查询嵌套属性:
// MySQL 示例 return $this->createQueryBuilder('r') ->join('r.valid', 'valids') ->where('JSON_EXTRACT(valids.technical, "$.technical.sender.recipient") = :unity') ->setParameter('unity', $unity) ->getQuery() ->getResult(); // PostgreSQL 示例 return $this->createQueryBuilder('r') ->join('r.valid', 'valids') ->where('valids.technical->>\'technical.sender.recipient\' = :unity') ->setParameter('unity', (string)$unity) ->getQuery() ->getResult();
方法3:自定义Doctrine DQL函数
如果不能改字段类型,可以自定义DQL函数来解析序列化数组,但实现比较复杂,新手不推荐。核心思路是写一个SQL函数,反序列化字符串并提取指定嵌套值,然后在DQL中调用。
内容的提问来源于stack exchange,提问作者Zkimi
相关产品推荐
相关产品推荐

