0

0

Symfony 如何把审计记录转为数组

月夜之吻

月夜之吻

发布时间:2025-08-07 16:27:01

|

1057人浏览过

|

来源于php中文网

原创

核心答案是使用symfony serializer组件将审计记录转换为数组;2. 首先确定审计数据来源(如gedmo logentry、auditbundle或自定义实现),不同来源的数据结构决定后续处理方式;3. 对于实体类审计记录,利用serializer的normalize方法配合datetimenormalizer和objectnormalizer将其转为数组,并通过上下文参数控制序列化行为;4. 若审计实体中包含json字符串字段(如data字段),需在序列化后额外调用json_decode($data, true)解析为数组;5. 使用序列化组(@groups)精确控制输出字段,避免敏感信息泄露和循环引用问题,尤其适用于关联对象(如user实体)的扁平化输出;6. 当默认序列化器不足时,可创建自定义normalizer实现复杂逻辑,如在审计上下文中仅输出用户id和用户名;7. 不同审计bundle策略不同:gedmo需特别处理data字段的json解析,simplethingsauditbundle结构较扁平易于序列化,encorelabs/auditbundle等复杂bundle需结合其api和内部实体结构定制序列化方案;8. 自定义审计实现若以json存储则直接json_decode,若为多列扁平结构可手动构建数组或映射到对象后序列化;9. 直接sql查询审计表不推荐,因难以解析非结构化数据和处理关联信息,易导致性能问题和逻辑重复;10. 最终推荐方案是结合serializer、序列化组、自定义normalizer及后期批量处理,实现高效、可控、可维护的审计数组转换。

Symfony 如何把审计记录转为数组

在Symfony中将审计记录转换为数组,核心在于理解你的审计数据是如何存储的。无论是通过Doctrine事件监听器、特定的审计Bundle,还是自定义的逻辑,最终目标都是将这些分散、可能带有复杂关联的数据,整理成一个易于处理的、扁平化的PHP数组。这通常会涉及Symfony的序列化组件,或者一些手工的数据映射工作。

解决方案

将Symfony审计记录转换为数组,最直接且推荐的方式是利用Symfony的序列化组件(Serializer Component)。它能处理实体对象、集合,并将其转换为各种格式,包括数组。

  1. 确定审计记录的来源和类型:

    • 如果你使用的是如Gedmo DoctrineExtensions的Loggable行为: 审计记录通常以
      Gedmo\Loggable\Entity\LogEntry
      实体形式存在。这些实体包含了变更的
      objectId
      objectClass
      version
      data
      (通常是序列化或JSON格式的变更详情)、
      loggedAt
      username
    • 如果你使用的是如SimpleThingsAuditBundle或Encorelabs/AuditBundle: 它们会有自己的审计实体(例如
      AuditEntry
      ),这些实体通常已经封装了变更的细节。
    • 如果你是自定义审计: 你的审计记录可能直接是数据库中的行,或者存储为JSON/序列化字符串的字段。
  2. 利用Symfony Serializer组件:

    • 对于实体对象(如LogEntry或AuditEntry): 这是最常见的情况。

      use Symfony\Component\Serializer\Serializer;
      use Symfony\Component\Serializer\Encoder\JsonEncoder;
      use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
      use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
      use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
      use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; // For PHP 8+ attributes
      
      // 假设你有一个审计实体 $auditRecord
      // 例如:$auditRecord = $entityManager->getRepository(LogEntry::class)->find(123);
      
      $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
      $normalizers = [
          new DateTimeNormalizer(), // 处理日期时间对象
          new ObjectNormalizer($classMetadataFactory) // 处理普通对象
      ];
      $encoders = [new JsonEncoder()];
      
      $serializer = new Serializer($normalizers, $encoders);
      
      // 使用normalize方法将其转换为数组
      // 可以通过上下文参数控制序列化深度、循环引用等
      $auditArray = $serializer->normalize($auditRecord, 'json', [
          'groups' => ['audit_read'], // 如果你定义了序列化组
          ObjectNormalizer::ENABLE_MAX_DEPTH => true, // 启用最大深度限制
          ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) {
              return $object->getId(); // 处理循环引用,返回ID
          }
      ]);
      
      // 特别处理Gedmo LogEntry的'data'字段,它通常是JSON字符串
      if (isset($auditArray['data']) && is_string($auditArray['data'])) {
          $auditArray['data'] = json_decode($auditArray['data'], true);
      }
    • 定义序列化组(Serialization Groups): 在你的审计实体或相关实体(如User实体)上使用

      #[Groups(['audit_read'])]
      注解,可以精确控制哪些字段会被序列化,避免暴露敏感信息或不必要的复杂关联。

    • 对于存储为JSON或序列化字符串的字段: 如果你的审计记录直接在数据库中存储为JSON字符串(例如一个

      json_data
      字段),那么你需要先取出这个字符串,然后用
      json_decode($string, true)
      将其转换为数组。如果是PHP的
      serialize()
      存储的,则用
      unserialize()

  3. 自定义Normalizer(可选但推荐): 当默认的

    ObjectNormalizer
    无法满足你对复杂关联对象或特定字段的转换需求时,你可以创建自定义的Normalizer。例如,你可能希望将审计记录中的
    user
    对象只转换为
    ['id' => 123, 'username' => 'John Doe']
    ,而不是完整的User实体。

    // 示例:一个简化版的自定义Normalizer来处理User对象
    // 这通常需要实现NormalizerInterface和DenormalizerInterface
    // 并在服务配置中注册
    /*
    class UserAuditNormalizer implements NormalizerInterface
    {
        public function normalize($object, string $format = null, array $context = [])
        {
            if (!$object instanceof User) {
                return null;
            }
            // 假设你在审计上下文中需要扁平化的用户数据
            if (isset($context['audit_context']) && $context['audit_context'] === true) {
                return [
                    'id' => $object->getId(),
                    'username' => $object->getUsername(),
                    // ... 其他你需要的字段
                ];
            }
            // 否则,让ObjectNormalizer处理
            return null;
        }
    
        public function supportsNormalization($data, string $format = null, array $context = [])
        {
            return $data instanceof User;
        }
    }
    */
    // 然后在serializer的normalizers数组中,将你的自定义normalizer放在ObjectNormalizer之前
    // $normalizers = [new UserAuditNormalizer(), new ObjectNormalizer($classMetadataFactory)];

为什么直接从数据库读取审计记录可能不够理想?

直接通过SQL查询来获取审计记录,听起来是直接了当,但对于复杂的审计场景来说,这往往不够“优雅”,甚至可能带来不少麻烦。审计记录通常不只是简单的行数据,它们包含了历史版本、字段变更对比、关联用户、时间戳等等,这些信息可能以非结构化(比如JSON字符串)、或者跨多表关联的形式存在。

你直接去查,拿到手可能是一堆ID,或者一些序列化过的、你看不懂的字符串。比如,一个

LogEntry
里存的
data
字段,它可能是个JSON字符串,里面详细记录了
oldValue
newValue
,你光用SQL是没法直接把这些解析成可用的数组结构的。而且,审计记录往往只存了关联实体的ID(比如哪个用户修改的),你还得再手动去查这个ID对应的用户是谁,如果记录量大,这会是大量的额外查询。

更关键的是,许多审计Bundle提供了高层级的API来查询和解释这些数据,它们帮你处理了版本回溯、数据差异比对等逻辑。你直接绕过这些API去读原始数据,等于放弃了这些便利,还得自己重新实现一遍这些复杂的业务逻辑。这不仅费时费力,还容易出错,尤其是在处理不同版本的数据结构差异时,手动解析会非常痛苦。

如何优雅地处理审计记录中的关联对象?

审计记录里,谁做了什么修改,这“谁”通常是个用户对象,或者其他关联实体。光一个ID肯定不够,我们希望看到用户的名字、邮箱,甚至角色。但如果把整个用户对象都序列化到审计记录里,那数据量就太大了,而且还可能遇到循环引用问题。所以,得找个平衡点。

BibiGPT-哔哔终结者
BibiGPT-哔哔终结者

B站视频总结器-一键总结 音视频内容

下载

一种非常实用的方式是利用Symfony Serializer的序列化组(Serialization Groups)。你可以在你的User实体上定义一个特定的组,比如

audit_read
,然后只把
id
username
email
这些字段标记到这个组里。

// src/Entity/User.php
use Symfony\Component\Serializer\Annotation\Groups;

class User
{
    #[Groups(['audit_read'])]
    private ?int $id = null;

    #[Groups(['audit_read'])]
    private ?string $username = null;

    #[Groups(['audit_read'])]
    private ?string $email = null;

    // ... 其他字段和方法
}

然后在序列化审计记录时,告诉Serializer只序列化

audit_read
组:

$auditArray = $serializer->normalize($auditRecord, 'json', [
    'groups' => ['audit_read']
]);

这样,当审计记录中包含一个User对象时,它就只会以一个包含

id
username
email
的扁平数组形式出现,既提供了足够的信息,又避免了数据冗余和复杂性。

如果这种方式还不够,或者你需要更复杂的逻辑(比如根据上下文动态决定输出哪些字段),那么自定义Normalizer就是你的终极武器。你可以编写一个专门的Normalizer来处理User实体,当它在审计上下文中被序列化时,就按照你想要的方式输出。这能让你对输出的粒度有完全的控制。

最后,一个比较“粗暴”但有时有效的方法是后期处理。先将审计记录序列化成数组,然后遍历这个数组,如果发现某个字段是关联实体的ID,就根据这个ID去数据库或者缓存里查出你需要的信息,然后替换掉原来的ID。这种方法在需要批量处理大量记录时,可以考虑批量查询关联数据,提高效率。

针对不同审计Bundle,转换策略有何不同?

市面上的Symfony审计Bundle不少,它们的设计哲学和数据存储方式各有侧重,所以转换策略自然也会有所不同。

  1. Gedmo DoctrineExtensions (Loggable):

    • 特点: 这是非常流行的一个Bundle,它通过Doctrine事件监听器,将每次实体变更记录为独立的
      LogEntry
      实体。
      LogEntry
      实体包含了
      objectId
      objectClass
      version
      data
      (一个JSON或序列化字符串,记录了字段的旧值和新值)、
      loggedAt
      username
    • 转换策略:
      • LogEntry
        实体本身可以直接通过Symfony Serializer进行序列化。
      • 最关键的是
        data
        字段,它通常是一个JSON字符串。在序列化
        LogEntry
        后,你需要额外一步对
        data
        字段进行
        json_decode(true)
        ,将其内部的变更详情解析为PHP数组。
      • 如果
        username
        字段存储的是关联用户的ID,你可能需要配合上面提到的序列化组或自定义Normalizer来加载用户详情。
  2. SimpleThingsAuditBundle:

    • 特点: 这个Bundle也通过Doctrine事件来记录实体变更,但它的数据结构通常更直接。它将变更记录为
      AuditEntry
      实体,其中包含了
      entityType
      entityId
      changes
      (一个
      Change
      对象的集合)、
      revision
      等。
      Change
      对象通常会包含
      fieldName
      oldValue
      newValue
    • 转换策略:
      • AuditEntry
        实体及其内部的
        Change
        对象通常设计得比较友好,可以直接通过Symfony Serializer进行序列化。
      • oldValue
        newValue
        字段可能存储的是原始数据类型,或者关联实体的ID。对于ID,同样需要配合序列化组或自定义Normalizer来处理。这个Bundle的数据结构通常比较扁平,序列化起来相对省心。
  3. Encorelabs/AuditBundle (或类似更复杂的Bundle):

    • 特点: 一些更重量级的审计Bundle可能会提供更细粒度的控制,例如记录哪个字段被修改、旧值和新值,甚至支持数据回滚。它们的数据结构可能更复杂,涉及多个关联表,可能还有自己的版本概念。
    • 转换策略:
      • 首先,要查阅该Bundle的文档,了解它提供的API来获取审计历史。通常这些Bundle会提供方法来获取一个“版本”或“历史”对象。
      • 拿到这些对象后,再通过Symfony Serializer进行转换。由于其内部实体结构可能比较复杂,你可能需要深入了解其内部实体,并定义更精细的序列化组,或者编写多个自定义Normalizer来处理其特有的数据结构。这可能需要更多的时间来调试和理解。
  4. 自定义审计实现:

    • 特点: 如果你没有使用现成的Bundle,而是自己实现了审计逻辑,那么存储格式完全由你控制。你可能直接将变更记录为JSON字符串存储在一个字段里,或者将每次变更扁平化存储在多列中。
    • 转换策略:
      • 如果你的字段直接存储了JSON字符串,那么取出后直接
        json_decode(true)
        即可。
      • 如果你的审计记录是扁平化的多列数据(例如
        user_id
        ,
        entity_id
        ,
        field_name
        ,
        old_value
        ,
        new_value
        ),那么你可能需要手动构建一个数组结构,或者将其映射到一个临时的PHP对象,再通过Serializer转换。优势是灵活性高,但你需要自己维护所有的解析逻辑。

总的来说,无论使用哪种Bundle或自定义方案,Symfony Serializer都是将审计记录转换为数组的核心工具。关键在于理解你的审计数据是如何存储的,然后灵活运用序列化组、自定义Normalizer,以及适当的后期处理,来达到你想要的数组结构。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
PHP Symfony框架
PHP Symfony框架

本专题专注于PHP主流框架Symfony的学习与应用,系统讲解路由与控制器、依赖注入、ORM数据操作、模板引擎、表单与验证、安全认证及API开发等核心内容。通过企业管理系统、内容管理平台与电商后台等实战案例,帮助学员全面掌握Symfony在企业级应用开发中的实践技能。

78

2025.09.11

数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

727

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

327

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

350

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1242

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

360

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

820

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

581

2024.04.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.2万人学习

Pandas 教程
Pandas 教程

共15课时 | 1.0万人学习

ASP 教程
ASP 教程

共34课时 | 4.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号