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

基于AbstractCrudController,如何通过Doctrine ORM获取并展示MAX(updatedAt)值?

解决EasyAdmin中添加聚合函数后出现的"Cannot read property 'id' from an array"错误

错误原因

你通过addSelect添加聚合子查询后,Doctrine的查询结果会返回实体对象与标量值的混合数组(比如[$rolesEntity, $lastUpdatedAt]),而EasyAdmin的默认模板逻辑期望结果是纯实体对象,尝试直接读取id属性时,误将数组当成实体,导致报错。

解决方案

推荐两种优雅的处理方式:

方式一:使用Doctrine的HIDDEN关键字保留实体结构

HIDDEN关键字会让Doctrine仍然返回完整的实体对象,同时将聚合值作为隐藏字段附加到实体上,不会破坏EasyAdmin的默认 hydration 逻辑。

  1. 修改createIndexQueryBuilder方法:
public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
{
    $builder = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);

    // 添加HIDDEN关键字,确保Doctrine返回实体对象而非混合数组
    return $builder->addSelect('(SELECT MAX(roles.updatedAt) FROM App\Entity\Roles roles) AS HIDDEN lastUpdatedAt');
}
  1. 在configureFields中配置展示该字段:
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;

public function configureFields(string $pageName): iterable
{
    yield Field::new('id')->onlyOnIndex();
    yield Field::new('name');
    yield Field::new('updatedAt');
    
    // 配置聚合字段,用Twig表达式获取隐藏值
    yield Field::new('lastUpdatedAt')
        ->setLabel('全局最后更新时间')
        ->setTemplatePath('admin/fields/global_last_updated.html.twig');
}
  1. 创建对应的Twig模板(templates/admin/fields/global_last_updated.html.twig):
{% set globalLastUpdated = entity.lastUpdatedAt %}
{{ globalLastUpdated ? globalLastUpdated|date('Y-m-d H:i:s') : '无数据' }}

方式二:单独查询聚合值并传递到模板

如果不想修改主查询,可以在index方法中单独获取全局最大值,再传递到模板展示。

  1. 重写index方法:
use EasyCorp\Bundle\EasyAdminBundle\Dto\CrudDto;
use Symfony\Component\HttpFoundation\Response;
use App\Entity\Roles;

public function index(CrudDto $crudDto): Response
{
    // 单独查询全局最大更新时间
    $globalLastUpdatedAt = $this->getDoctrine()
        ->getRepository(Roles::class)
        ->createQueryBuilder('r')
        ->select('MAX(r.updatedAt)')
        ->getQuery()
        ->getSingleScalarResult();

    // 将值存入上下文,供模板使用
    $crudDto->getContext()->set('globalLastUpdatedAt', $globalLastUpdatedAt);

    return parent::index($crudDto);
}
  1. 自定义索引页模板(templates/admin/crud/roles_index.html.twig):
{% extends '@EasyAdmin/crud/index.html.twig' %}

{% block content_header %}
    {{ parent() }}
    <div class="mt-2">
        <span class="badge bg-secondary">全局最后更新时间:{{ globalLastUpdatedAt|date('Y-m-d H:i:s') }}</span>
    </div>
{% endblock %}
  1. 在configureCrud中指定自定义模板:
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;

public function configureCrud(Crud $crud): Crud
{
    return $crud
        ->setTemplate('index', 'admin/crud/roles_index.html.twig');
}

不推荐的方案

不要尝试修改EasyAdmin的结果处理逻辑来适配混合数组,这会破坏框架的默认行为,增加维护成本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:04:51