Shopware 6中使用Criteria过滤JSON空数据遇问题
如何用Criteria对JSON字段做空值检查?
你的问题核心是默认的existence-filter针对JSON数组字段生成的JSON_CONTAINS无法正确判断空数组或NULL的情况,以下是几种不需要写复杂装饰器的解决办法:
方法一:自定义筛选器,手动构建Criteria逻辑
放弃existence-filter,自己写一个带handler的自定义筛选器,直接在前端拼接需要的查询条件:
options['vat-id-filter'] = { property: 'orderCustomer.customer.vatIds', type: 'custom-vat-filter', label: this.$tc('sw-order-list.filters.vat-id.placeholder'), options: [ { label: this.$tc('sw-order-list.filters.vat-id.textHasCriteria'), value: 'has' }, { label: this.$tc('sw-order-list.filters.vat-id.textNoCriteria'), value: 'none' } ], handler: (filterValue, criteria) => { if (filterValue === 'has') { // 筛选有税号的:JSON字段非空且不是空数组 criteria.addFilter(Criteria.not('empty', 'orderCustomer.customer.vatIds')); criteria.addFilter(Criteria.not('equals', 'orderCustomer.customer.vatIds', '[]')); } else if (filterValue === 'none') { // 筛选无税号的:JSON字段为NULL或空数组 criteria.addFilter(Criteria.or([ Criteria.empty('orderCustomer.customer.vatIds'), Criteria.equals('orderCustomer.customer.vatIds', '[]') ])); } } };
这种方式直接绕开默认的ListField解析逻辑,完全由自己控制生成的查询条件。
方法二:给实体添加计算字段
在后端实体定义里新增一个基于JSON字段的计算字段,把“是否有税号”转成布尔值,前端直接用布尔筛选器:
后端修改(CustomerDefinition类)
protected function defineFields(): FieldCollection { $fields = parent::defineFields(); $fields->add( new JsonField('vat_ids', 'vatIds'), // 新增计算字段:通过JSON_LENGTH判断数组是否有内容 new CalculatedField( 'has_vat_id', 'JSON_LENGTH(vat_ids) > 0', ['vat_ids'], CalculatedField::TYPE_BOOLEAN ) ); return $fields; }
前端筛选器修改
options['vat-id-filter'] = { property: 'orderCustomer.customer.hasVatId', type: 'boolean-filter', label: this.$tc('sw-order-list.filters.vat-id.placeholder'), labelTrue: this.$tc('sw-order-list.filters.vat-id.textHasCriteria'), labelFalse: this.$tc('sw-order-list.filters.vat-id.textNoCriteria'), };
这种方式把JSON的复杂判断转成简单的布尔值筛选,维护成本更低。
方法三:装饰SqlQueryParser(仅迫不得已时用)
如果必须保留existence-filter的原有逻辑,才需要装饰SqlQueryParser的parseEqualsFilter方法,修改ListField对应的SQL生成:
// 自定义装饰器类 class DecoratedSqlQueryParser extends SqlQueryParser { protected function parseEqualsFilter(Filter $filter, QueryBuilder $query, EntityDefinition $definition, string $root): void { // 针对vatIds字段的空值判断做特殊处理 if ($filter instanceof EqualsFilter && $filter->getField() === 'vatIds' && $definition instanceof CustomerDefinition ) { $column = $this->getField($definition, $filter->getField(), $root); if ($filter->getValue() === null) { // 空值逻辑:判断JSON_LENGTH为0或字段为NULL $query->andWhere($query->expr()->orX( $query->expr()->isNull($column), "JSON_LENGTH({$column}) = 0" )); return; } } // 其他情况走默认逻辑 parent::parseEqualsFilter($filter, $query, $definition, $root); } }
然后在services.xml里注册装饰器:
<service id="YourPlugin\Core\Framework\DataAbstractionLayer\Sql\Parser\DecoratedSqlQueryParser" decorates="Shopware\Core\Framework\DataAbstractionLayer\Sql\Parser\SqlQueryParser"> <argument type="service" id=".inner"/> <argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\FieldSerializer\FieldSerializerRegistry"/> </service>
内容的提问来源于stack exchange,提问作者Ümit
相关产品推荐
相关产品推荐

