![](/img/trans.png)
[英]How to create a form with quantities linked with an EntityType field in Symfony 5?
[英]How to test a Form having a field being EntityType in Symfony
(使用框架 Symfony 4.4)
我嘗試了解如何測試具有作為 EntityType 表單字段的字段的表單。
請參見下面的示例:
class VaccinationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('vaccine_brand_uid', EntityType::class, ['class' => ViewVaccineBrand::class])
->add('administration_date', DateTimeType::class, [
'widget' => 'single_text',
'model_timezone' => 'UTC',
'view_timezone' => 'UTC',
]);
}
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'data_class' => VaccinationInput::class,
'method' => 'POST',
'csrf_protection' => false
]);
}
}
如您所見,字段 vaccine_brand_uid 是一個 EntityType 字段,因此它確保提交表單時給定的值是ViewVaccineBrand
表的一部分。
下面是相關的VaccinationInput
object:
namespace App\Services\Domain\Vaccination\DTO;
use App\Entity\ViewVaccineBrand;
...
class VaccinationInput
{
/**
* @Assert\Type(type=ViewVaccineBrand::class, message="api_missingBrand")
* @Assert\NotBlank(message="api_missingBrand")
* @var ViewVaccineBrand|int
*/
public $vaccine_brand_uid;
/**
* @Assert\DateTime(message="api_missingAdministrationDate")
*/
public $administration_date;
}
因此,在嘗試為此表單創建測試 class 時,我在 Symfony 存儲庫中找到了這個示例: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Tests/Form/Type /EntityTypeTest.php
所以我復制/粘貼這個 class 以適應我自己的測試!
我對其進行了一些調整,使其更易於重用(添加 getTypes() 函數):
/**
* Inspired by https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
*/
abstract class EntityTypeTestCase extends TypeTestCase
{
/**
* @var EntityManager
*/
protected $em;
/**
* @var MockObject&ManagerRegistry
*/
protected $emRegistry;
protected function setUp(): void
{
$this->em = DoctrineTestHelper::createTestEntityManager();
$this->emRegistry = $this->createRegistryMock('default', $this->em);
parent::setUp();
$schemaTool = new SchemaTool($this->em);
$classes = array_map(fn($c) => $this->em->getClassMetadata($c), $this->registeredTypes());
try {
$schemaTool->dropSchema($classes);
} catch (\Exception $e) {
}
try {
$schemaTool->createSchema($classes);
} catch (\Exception $e) {
}
}
protected function tearDown(): void
{
parent::tearDown();
$this->em = null;
$this->emRegistry = null;
}
protected function getExtensions(): array
{
return array_merge(parent::getExtensions(), [
new DoctrineOrmExtension($this->emRegistry),
]);
}
protected function createRegistryMock($name, $em): ManagerRegistry
{
$registry = $this->createMock(ManagerRegistry::class);
$registry->expects($this->any())
->method('getManager')
->with($this->equalTo($name))
->willReturn($em);
return $registry;
}
protected function persist(array $entities)
{
foreach ($entities as $entity) {
$this->em->persist($entity);
}
$this->em->flush();
}
protected function getTypes()
{
return array_merge(parent::getTypes(), []);
}
/**
* @return array An array of current registered entity type classes.
*/
abstract protected function registeredTypes(): array;
}
所以我想測試我的 VaccinationType 表格,下面是我所做的。
~/symfony/tests/Service/Domain/Vaccination/Type/VaccinationTypeTest
<?php
namespace App\Tests\Service\Domain\Vaccination\Type;
...
class VaccinationTypeTest extends EntityTypeTestCase
{
public function testWhenMissingBrandThenViolation()
{
$model = new VaccinationInput();
$entity1 = (new ViewVaccineBrand())->setVaccineBrandName('test')->setIsActive(true)->setVaccineCode('code');
$this->persist([$entity1]);
// $model will retrieve data from the form submission; pass it as the second argument
$form = $this->factory->create(VaccinationType::class, $model);
$form->submit(['vaccine_brand_uid' => 2, 'administration_date' => DateTime::formatNow()]);
$violations = $form->getErrors(true);
$this->assertCount(1, $violations); // There is no vaccine brand for uid 2
}
protected function getTypes()
{
return array_merge(parent::getTypes(), [new EntityType($this->emRegistry)]);
}
protected function registeredTypes(): array
{
return [
ViewVaccineBrand::class,
ViewVaccineCourse::class,
];
}
protected function getExtensions(): array
{
$validator = Validation::createValidator();
return array_merge(parent::getExtensions(), [
new ValidatorExtension($validator),
]);
}
}
php bin/phpunit
執行的實際結果如下:
有 1 個錯誤:
- App\Tests\Service\Domain\Vaccination\Type\VaccinationTypeTest::testWhenMissingBrandThenViolation Symfony\Component\Form\Exception\RuntimeException: Class "App\Entity\ViewVaccineBrand" 似乎不是托管的 Doctrine 實體。 你忘了map嗎?
/app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 /app/xxxxapi/vendor /symfony/options-resolver/OptionsResolver.php:1035 /app/xxxxapi/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:130 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.88145283851953 :915 /app/xxxxapi/vendor/symfony/options-resolver/OptionsResolver.php:824 /app/xxxxapi/vendor/symfony/form/ResolvedFormType.php:97 /app/xxxxapi/vendor/symfony/form/FormFactory.881452835199 :76 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:94 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:244 /app/xxxxapi/vendor/symfony/form/FormBuilder.php:195 /app/xxxxapi/vendor/symfony/form/FormFactory.php:30 /app/xxxxapi/tests/Service/Domain/Vaccination/Type/VaccinationTypeTest.php:23
我認為這是因為出於某種原因,在 EntityTypeTestCase 中創建的 entitymanager 與 /app/xxxxx/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203 中使用的/app/xxxxx/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:203
......
但我不知道如何指定,在這個測試的情況下,我希望DoctrineType
(它是EntityType
的父級)使用這個特殊的測試 entityManager 而不是默認的。
使測試工作斷言應該是成功的。
我添加實體以獲取額外信息
namespace App\Entity;
/**
* VaccineBrand
*
* @ORM\Table(name="dbo.V_HAP_VACCINE_BRAND")
* @ORM\Entity(repositoryClass="App\Repository\ViewVaccineBrandRepository")
*/
class ViewVaccineBrand
{
/**
* @var int
*
* @ORM\Column(name="VACCINE_BRAND_UID", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $vaccineBrandUid;
/**
* @var string
* @ORM\Column(name="VACCINE_BRAND_NAME", type="string", nullable=false)
*/
protected $vaccineBrandName;
/**
* @var string
* @ORM\Column(name="VACCINE_BRAND_CODE", type="string", nullable=true)
*/
protected $vaccineBrandCode;
// ... Other fields are not relevant
}
我已經讀過那些沒有運氣的人:
這是一個復雜的情況,因此我將簡要概述我認為您需要做的事情。
EntityType
(對於相關實體)更改為ChoiceType
CallbackChoiceLoader
在數據庫中查詢所有按名稱(或任何你想成為標簽)索引的 uid 字段,即: ['BigPharm'=>123, 'Other Brand'=>564, ]
@Assert\Type
更改為@Assert\Choice
callback
選項與將在與上述CallbackChoiceLoader
相同的結果上調用array_values
的方法一起使用,即: [123, 564, ]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.