addiks / symfony_rdm
帮助在 symfony/doctrine 应用程序中使用领域驱动设计和丰富的领域模型。
Requires
- php: ^8.0.0 || ^8.1.0
- doctrine/annotations: *
- doctrine/dbal: 2.13.* || ^2.14 || ^3.5
- doctrine/orm: 2.13.* || ^2.14
- psr/cache: 1.0.* || ^2.0
- symfony/framework-bundle: ^4.2 || ^5.4 || ^6.2
- symfony/yaml: ^4.2 || ^5.4 || ^6.2
Requires (Dev)
- infection/infection: *
- malukenho/docheader: *
- phpunit/phpunit: *
- symfony/expression-language: ^4.2
- symfony/form: ^4.0
- symfony/thanks: *
- vimeo/psalm: >4.0
- v3.6.3
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.0
- v3.4.6
- v3.4.5
- v3.4.4
- v3.4.3
- v3.4.2
- v3.4.1
- v3.4.0
- v3.3.4
- v3.3.3
- v3.3.2
- v3.3.1
- v3.3.0
- v3.2.14
- v3.2.13
- v3.2.12
- v3.2.11
- v3.2.10
- v3.2.9
- v3.2.8
- v3.2.7
- v3.2.6
- v3.2.5
- v3.2.4
- v3.2.3
- v3.2.2
- v3.2.1
- v3.2.0
- v3.1.20
- v3.1.19
- v3.1.18
- v3.1.17
- v3.1.16
- v3.1.15
- v3.1.14
- v3.1.13
- v3.1.12
- v3.1.11
- v3.1.10
- v3.1.9
- v3.1.8
- v3.1.7
- v3.1.6
- v3.1.5
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.1
- v3.0.0
- v2.2.1
- v2.2.0
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.2
- v2.0.1
- v2.0
- dev-master / 1.0.x-dev
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-entity_code_generation
- dev-example_for_infection_trait_issue
This package is auto-updated.
Last update: 2024-09-11 19:23:09 UTC
README
是什么
本项目旨在丰富 doctrine2-ORM 映射能力,使得实体不再需要专门为 doctrine 开发,而是可以借助 doctrine 将任何对象映射到数据库,即使它并非为此目的而开发。它提供了许多将数据从数据库映射到对象(及反向)的新方法。
当前实现的功能包括以下:
- 将(symfony-)服务加载到实体字段中。
- 有选择地加载字段中的映射。
- 将具有静态键的数组加载到字段中。
- 将包含其他映射的动态长度列表加载到字段中。
- 将具有自身内部映射的任何对象加载到字段中。(您可以使用不同的方式映射相同的类。)
- 使任何映射可为空。
- 使映射始终映射到 NULL。
- 从另一个文件导入映射。
这些可以与任何其他组合使用,从而提供非常动态的 ORM 映射能力。
如何
它挂钩到 doctrine 的事件并使用描述的值填充标记的字段。有几种方式可以定义哪些映射应该放在服务的哪些字段中:通过注解、YAML、XML、PHP 或静态 PHP。YAML 映射尚未完全实现,可能很快会被删除。
我建议您使用 XML(或 YAML)映射,因为实体应该是框架无关的。我本人更喜欢 XML,因为至少 XML 有模式,而 YAML 通常需要猜测允许哪些键、所有键的含义以及谁在使用它们。有关更多详细信息,请参阅上述链接文档。
设置
要启用此功能,首先使用以下命令通过 composer(symfony 通常自带 composer)安装项目:composer require addiks/symfony_rdm
然后 在您的 symfony 应用程序中注册该捆绑包。在 symfony-4.0 之前,这是在 "app/AppKernel.php" 文件中的 "registerBundles" 方法内完成的。从 4.0 开始,这是在 "config/bundles.php" 文件中完成的。(如果您知道如何自动化此过程,请告知我。)
Symfony 2.x & 3.x
// app/AppKernel.php public function registerBundles() { $bundles = array( new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), # ... new Addiks\RDMBundle\AddiksRDMBundle(), # <== Add this line new AppBundle\AppBundle(), ); … return $bundles; }
Symfony >= 4.0
// config/bundles.php return [ // 'all' means that the bundle is enabled for any Symfony environment Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], … Addiks\RDMBundle\AddiksRDMBundle::class => ['all' => true], # <== Add this line ];
之后,此捆绑包应该可以工作。如果不行,请在此处创建一个问题,并尽可能提供有关使用环境的详细信息,我可能能够帮助。
两种获取数据的方式:安全或快速
使用此扩展扩展 doctrine 实体的 ORM 映射可能会(根据 ORM 映射配置)引入 doctrine 通常不知道的新数据库列。在加载或存储这些附加列的数据时,有两种方式与之交互
安全但慢的方法
处理这些额外数据库列的安全但缓慢的方法是在Doctrine之外逐个加载它们,每次实体被实体化时,当Doctrine实体管理器刷新时,将所有已修改的实体存储(更新/插入)到数据库中。这种方法比快速方法更安全,因为所有操作都在Doctrine的作用域之外,并且Doctrine或其他Doctrine插件无法干扰加载和存储过程的功能。同时,这种方法比快速方法慢得多——有时甚至慢得多——因为现在我们可能需要为每个实体化实体执行一个额外的选择语句。这可能会对性能产生严重影响!尽管如此,这种方法是默认方法,因为虽然速度较慢,但更安全。
快速但不稳定的方法
在Doctrine之外通过额外的选择语句加载和存储所有数据到和从数据库的替代方案是让Doctrine通过它自己的机制为我们加载和存储这些数据。这种方式性能(几乎)与这些列是原生Doctrine列时的性能相同,不需要执行额外的选择、更新或插入语句。这个解决方案的问题在于,为了使其工作,我们需要让Doctrine了解所有这些额外的数据库列,这样Doctrine就可以为我们处理它们,而无需Doctrine在它自己的实体化过程中实际使用这些数据。这些是数据库列,但不是实体字段。如果Doctrine试图将其映射到实体字段,它将会失败,因为这些字段没有对应实体字段。为了避免这种情况,这种方法深入到Doctrine自己的反射机制中,并伪造这些实体字段以供Doctrine使用。从Doctrine的角度来看,即使它们是伪造的,这些实体字段实际上也存在。这种深入到Doctrine内部挂钩的结构对Doctrine的内部结构做了很多假设。如果其中任何一个假设失败(因为Doctrine在新的版本中更改了它们,或者另一个扩展更改了它们),那么这个扩展可能无法正常工作!由于这种不稳定/不确定性,这种方法不是默认的,而是可选的。要使用此方法,您必须定义一个名为addiks_rdm.data_loader.stability
的symfony服务参数,并将其设置为fast-and-unstable
。
app/config/config.yml
:
parameters:
addiks_rdm.data_loader.stability: 'fast-and-unstable'
之后,您应该在入口脚本(index.php
、bin/console
、app.php
、app_dev.php
等)中调用一个名为symfony_rdm_composer_hook
的设置函数,在包含composers的vendor/autoload.php
之后,以及symfony内核创建之后。composers的vendor/autoload.php
文件返回类加载器对象,这应该与symfony内核一起传递给提到的函数。
示例
# ... use Composer\Autoload\ClassLoader; use function Addiks\SymfonyRDM\symfony_rdm_composer_hook; # ... /** @var ClassLoader $loader */ $loader = require('/vendor/autoload.php'); # ... $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); # ... symfony_rdm_composer_hook($loader, $kernel); # ...
这个函数symfony_rdm_composer_hook
在项目的根目录下的一个名为composer_hook.php
的文件中声明。在正常情况下,此文件应该始终由composer自动包含。如果没有,您可能会收到一个错误消息,指出该函数是未知的。在这种情况下,您可能(暂时)需要手动包含该文件。
require_once('vendor/addiks/symfony_rdm/composer_hook.php');
服务-表单类型
此包还提供了一个名为“ServiceFormType”的新表单类型,这应该与该包的服务实体化能力结合使用非常有价值。它允许指定一个服务ID列表作为选择,可以在表单中选择,并设置在实体上。
<?php use Addiks\RDMBundle\Symfony\FormType\ServiceFormType; class MyEntityFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add("someField", ServiceFormType::class, [ 'required' => false, 'choices' => [ 'app.example_services.foo' => 'foo', 'app.example_services.bar' => 'bar', ] ]); } … }
未来
该项目未来可能会扩展更多功能,以下是我的一些想法:
- 允许在实体中、之间以及之间允许对象装饰(自定义代理对象)。
- 允许使用简单数组而不是doctrine集合对象或甚至是自定义集合。
- 将服务容器参数注入到实体中(类似于服务)。
- 在多个字段(也许甚至在多个实体)中重复使用一个字段的重复数据(也许甚至在多个实体之间)。
- 从生成器服务(或其他程序)生成非对象值,以填充未映射的字段。
- 使用数据库中的聚合数据填充字段。
- 允许为最终实体使用自定义(非生成)代理类。