🎻
[Symfony] エンティティに対応するFormTypeの全項目が空だった場合はエンティティ自体を削除する
やりたいこと
例えば、以下のように Person
が OneToOne
で Profile
を持っているようなエンティティ構成を考えます。
/**
* @ORM\Entity()
*/
class Person
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $screenName;
/**
* @ORM\OneToOne(targetEntity="Profile", cascade={"persist", "remove"})
*/
private $profile;
// ...
}
/**
* @ORM\Entity()
*/
class Profile
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $fullName;
/**
* @ORM\Column(type="string", length=255)
*/
private $email;
// ...
}
そして、これらのエンティティを以下のように PersonType
でまとめて編集できるようにします。
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('screenName', TextType::class)
->add('profile', ProfileType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}
class ProfileType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fullName', TextType::class, [
'required' => false,
])
->add('email', TextType::class, [
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Profile::class,
]);
}
}
これで、 PersonType
をレンダリングすると
-
screenName
(必須) -
fullName
(任意) -
email
(任意)
の3つのフォーム項目が出力されますね。
さて、このフォームにおいて、 fullName
と email
の両方が空欄で送信されたときに、 Profile
エンティティ自体を削除する、という振る舞いを実装したいとしましょう。
どうすればいいでしょうか?🤔
やり方
結論としては、
-
OneToOne
リレーションの設定にorphanRemoval=true
をつけておいて - FormTypeのEventListenerで、全項目が空欄だったら項目自体に
null
をセットし直す
という方法で実現可能です。
まず、 Person
エンティティの $profile
プロパティの OneToOne
の 設定に以下のように orphanRemoval=true
を追記します。
/**
- * @ORM\OneToOne(targetEntity="Profile", cascade={"persist", "remove"})
+ * @ORM\OneToOne(targetEntity="Profile", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $profile;
これで、 Person
エンティティの $profile
プロパティに null
がセットされたときに Profile
エンティティが自動的に削除されるようになります。(参考)
あとは、 PersonType
にEventListenerをセットして、
-
profile.fullName
とprofile.email
が両方空だったら -
profile
にnull
をセットする
という処理をフォーム送信前に行うようにしてあげれば、目的を果たせます。
具体的には以下のような実装で実現できます。
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('screenName', TextType::class)
->add('profile', ProfileType::class)
// これを追加
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
if (!array_filter($event->getData()['profile'])) {
$event->getForm()->get('profile')->setData(null);
}
})
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}
ちなみに
逆に、全項目が空で送信された場合にも、「全項目が空なエンティティ」を作成したいという場合には、SUBMIT
イベントフックを利用して以下のような感じで対応できます。
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
/** @var Person $person */
$person = $event->getData();
if (!$person->profile) {
$person->profile = new Profile();
}
}
まとめ
Symfonyで「エンティティに対応するFormTypeの全項目が空だった場合はエンティティ自体を削除する」という要件は、
- リレーションの設定に
orphanRemoval=true
をつけておいて - FormTypeのEventListenerで、全項目が空欄だったら項目自体に
null
をセットし直す
という方法で実現できます、というお話でした。
Discussion