Symfony 6 使用动态实体填充下拉列表

Symfony 6 populate Dropdown with Dynamic entities

我有一个 CreditCard 实体,在表单屏幕上有一个包含用户银行实体的下拉列表,我想根据所选银行动态填充 BankAccount 下拉列表,我已经尝试了 SO 的几个解决方案并在我的表单中添加了一个事件监听器但是当我更改银行下拉列表时,我得到“违反完整性约束:kart_adi(实体上的信用卡名称字段)不能为空。我正在使用带有 php 8.1.2

的 Symfony 6

KrediKarti.php:

class KrediKarti
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;

#[ORM\ManyToOne(targetEntity: Banka::class)]
#[ORM\JoinColumn(nullable: false)]
private $banka;

#[ORM\ManyToOne(targetEntity: BankaHesap::class, inversedBy: 'krediKartlari')]
#[ORM\JoinColumn(nullable: false)]
private $hesap;

#[ORM\Column(type: 'string', length: 255)]
private $kart_adi;

#[ORM\Column(type: 'string', length: 255)]
private $kart_no;

#[ORM\Column(type: 'integer')]
private $hesap_kesim_gunu;

#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $aciklama;

#[ORM\Column(type: 'float')]
private $borc;

#[ORM\Column(type: 'float')]
private $alacak;

#[ORM\Column(type: 'datetime_immutable')]
private $created_at;

public function getId(): ?int
{
    return $this->id;
}

public function getBanka(): ?Banka
{
    return $this->banka;
}

public function setBanka(?Banka $banka): self
{
    $this->banka = $banka;

    return $this;
}

public function getHesap(): ?BankaHesap
{
    return $this->hesap;
}

public function setHesap(?BankaHesap $hesap): self
{
    $this->hesap = $hesap;

    return $this;
}

public function getKartAdi(): ?string
{
    return $this->kart_adi;
}

public function setKartAdi(string $kart_adi): self
{
    $this->kart_adi = $kart_adi;

    return $this;
}

public function getKartNo(): ?string
{
    return $this->kart_no;
}

public function setKartNo(string $kart_no): self
{
    $this->kart_no = $kart_no;

    return $this;
}

public function getHesapKesimGunu(): ?int
{
    return $this->hesap_kesim_gunu;
}

public function setHesapKesimGunu(int $hesap_kesim_gunu): self
{
    $this->hesap_kesim_gunu = $hesap_kesim_gunu;

    return $this;
}

public function getAciklama(): ?string
{
    return $this->aciklama;
}

public function setAciklama(?string $aciklama): self
{
    $this->aciklama = $aciklama;

    return $this;
}

public function getBorc(): ?float
{
    return $this->borc;
}

public function setBorc(float $borc): self
{
    $this->borc = $borc;

    return $this;
}

public function getAlacak(): ?float
{
    return $this->alacak;
}

public function setAlacak(float $alacak): self
{
    $this->alacak = $alacak;

    return $this;
}

public function getCreatedAt(): ?\DateTimeImmutable
{
    return $this->created_at;
}

public function setCreatedAt(\DateTimeImmutable $created_at): self
{
    $this->created_at = $created_at;

    return $this;
}
}

KrediKartiType.php:

$builder
        ->add('kart_adi',TextType::class,['label' => 'Kart Adı','required' => false, 'attr' =>['class' => 'form-control', 'placeholder' => 'Kart Adı']])
        ->add('kart_no',TextType::class,['label' => 'Kart No','required' => false, 'attr' =>['class' => 'form-control', 'placeholder' => 'Kart No']])
        ->add('hesap_kesim_gunu', NumberType::class, [
            'label' => 'Hesap Kesim Tarihi', 'attr' =>['class' => 'form-control', 'placeholder' => 'Hesap Kesim Günü']
        ])
        ->add('aciklama',TextType::class,['label' => 'Açıklama','required' => false, 'attr' =>['class' => 'form-control', 'placeholder' => 'Açıklama']])
        ->add('borc', NumberType::class,
            [
                'label' => 'Borç', 'attr' =>['class' => 'form-control', 'placeholder' => "Borç",'value' => '0.00','style' => 'text-align: right']
            ])
        ->add('alacak', NumberType::class,
            [
                'label' => 'Alacak', 'attr' =>['class' => 'form-control', 'placeholder' => "Alacak",'value' => '0.00','style' => 'text-align: right']
            ])
        ->add('banka',  EntityType::class, array(
            'class' => Banka::class,
            'choice_label'=> 'adi',
            'placeholder' => 'Lütfen bankanızı seçiniz',
            'attr' =>['class' => 'form-control']
        ))

    ;

    $formModifier = function (FormInterface $form, Banka $banka = null) {
        $banka_hesap = null === $banka ? [] : $banka->getBankaHesaplari();

        $form->add('hesap', EntityType::class, [
            'class' => BankaHesap::class,
            'placeholder' => '',
            'choices' => $banka_hesap,
        ]);
    };

    $builder->addEventListener(
        FormEvents::PRE_SET_DATA,
        function (FormEvent $event) use ($formModifier) {
            $data = $event->getData();

            $formModifier($event->getForm(), $data->getBanka());
        }
    );

    $builder->get('banka')->addEventListener(
        FormEvents::POST_SUBMIT,
        function (FormEvent $event) use ($formModifier) {
            $banka = $event->getForm()->getData();

            $formModifier($event->getForm()->getParent(), $banka);
        }
    );

new.html.twig:

<script>
var $banka = $('#kredi_karti_banka');
var $token = $('#kredi_karti__token');
$banka.change(function() {
    var $form = $(this).closest('form');
    var data = {};
    data[$banka.attr('name')] = $banka.val();
    data[$token.attr('name')] = $token.val();
    $.ajax({
    url : $form.attr('action'),
    type: $form.attr('method'),
    data : data,
    complete: function(html) {
    // Replace current position field ...
    $('#kredi_karti_hesap').replaceWith(
    // ... with the returned one from the AJAX response.
    $(html.responseText).find('#meetup_position')
    );
    // Position field now displays the appropriate positions.
    }
    });
});
</script>

我只是想在银行更改时根据所选银行获取银行帐户。

您可以在创建表单时向其添加选项。因此,您可以创建一个端点,您可以在其中创建带有额外选项的表单,这些选项可以是 bank id,然后仅呈现您想要的 select 元素,在您的情况下为 bank_accounts select。您可以通过 JS 将此端点调用到 banks select 的更改事件。您还需要修改 KrediKartiType 以便 bank_accounts select 应该从查询构建器中获取值。例如:

class BankAccountsController extends AbstractController
{
    #[Route('/bank-accounts-select', name: 'bank_accounts_select', methods: ['GET'])]
    public function getBankAccountsSelect(Request $request): Response
    {
        $form = $this->createForm(
            KrediKartiType::class,
            null,
            ['bankId' => $request->query->get('bankId')]
        );

        return $this->render('_bank-accounts-select.html.twig', [
            'form' => $form->createView(),
        ]);
    }
}

bank_accounts 键的表单类型如下所示:

->add('bank_accounts', EntityType::class, [
      'class' => BankaHesap::class,
      'query_builder' => function (BankaHesapRepository $bankaHesapRepository) use ($options) {
           return $bankaHesapRepository->bankAccountsByBankId((int) $options['bankId']);
       },
       'label_attr' => ['class' => 'mb-0 d-block'],
])

从 bankId 获取银行账户的 Repository 方法:

public function bankAccountsByBankId(?int $bankId): QueryBuilder
{
   return $this->createQueryBuilder('bh')
     ->join('bh.bank', 'b')
     ->where('b.id = :bankId')
     ->setParameter('bankId', $bankId)
     ->orderBy('bh.name', 'ASC') // this is optional :)
   ;
}

您还需要呈现 select 元素的 twig 文件 _bank-accounts-select.html.twig:

{{ form_row(form.bank_accounts, {
    attr: {
        'class': 'mb-3 form-control'
    }
}) }}

最后是 Js 部分,需要调用端点 bank-accounts-select,将 select 替换为从端点获得的那个:

$bankSelect.on('change', (event) => {
  const bankId = event.target.value;
  $.ajax({
      url: BASE_URL+'/bank-accounts-select',
      data: {
        bankId: bankId,
      },
      success: function (html) {
        const $bankAccountsSelect = REGION_SELECT_FROM_CLASS_NAME //replace this with your jQuery/Javascript selector;
        $bankAccountsSelect.html($(html).find("option"));
        $bankAccountsSelect.val("").trigger("change");
      },
    });
});

这应该可以,而且我建议您在代码中使用英语 ;)