PHP silex Formbuilder 和对象与对象数组



我正在使用带有Silex框架的PHP,经过几个小时试图找到一个令人满意的解决方案,我仍然被以下关于包含对象数组的表单和对象的问题所阻止。花费的时间使我能够找到可行的解决方案,但我希望有更好的方法来使用Silex。

在我的应用程序中,我有一个User类,其定义如下:

class User implements UserInterface
{
private $id;
private $username;
private $displayname;
private $capacities;
//...
}

$capacities变量包含来自另一个类(Capacity)的对象数组。Capacity是我的应用程序中具有各种信息(标签、容量位置等)的特定角色,我已经在该Capacity类中添加了一个布尔值,用于指示容量是否对特定用户处于活动状态,当通过$capacities数组附加到用户时。

目前,我能够使用以下代码创建看起来像我想要的表单:

use PlanningDomainUser;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormExtensionCoreTypeChoiceType;
use SymfonyComponentFormExtensionCoreTypeHiddenType;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentValidatorConstraints as Assert;

class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class, array(
'required'    => true,
'label' => "Login (pour Jean Durant → jdurant)"
))
->add('displayname', TextType::class, array(
'label' => "Nom à afficher"
));
$choices = array();
$choicesActive = array();
foreach ($builder->getData()->getCapacities() as $id => $capacity) {
$choices[$capacity->getLabel()] = $capacity->getId();
if ($capacity->getActive()) {
$choicesActive[] = $capacity->getId();
}
}
$builder->add('capacities', ChoiceType::class, array(
'choices' => $choices,
'data' => $choicesActive,
'label' => "Groupes",
'multiple' => True,
'expanded' => True
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
}
public function getName()
{
return 'capacity';
}
}

但是我在验证表单后得到的User对象包含容量:

[capacities:PlanningDomainUser:private] => Array
(
[0] => 2
[1] => 4
[2] => 1
)

capacities变量包含在表单中选中的复选框的值列表。问题是我的User对象与其定义不一致,该定义说capacities属性应该是Capacity对象的数组。我目前正在做的是,我在$userForm->isSubmitted()$userForm->isValid()时将以下代码添加到我的控制器中:

// Getting the array from the returned user, should contain Capacity object but just contains the IDs.
$capacitiesChecked = $userForm->getData()->getCapacities();
// We regenerate the full array of capacities for this user
$user->setCapacities($app["dao.capacity"]->findAll());
// Then we will activate capacities that have been checked, one by one
foreach ($capacitiesChecked as $capacityChecked) {
$user->getCapacityById($capacityChecked)->setActive(True);
}

这是有效的,我对此感到满意,但是作为Silex和框架世界的新手,我很惊讶我无法找到一种更简单的方法来回答我认为应该很常见的问题。

我可能从Silex/Symfony哲学中遗漏了一些东西,我希望那里的人能够指出我正确的位置以获取更多信息或找到解决方案!

编辑以下@dbrumann答案

由于可能不清楚我的数据是如何组织的,以下是我的数据库中的表格:

  • 用户
    • 编号
    • 用户名
    • 显示名称
  • 能力
    • 编号
    • 标签
    • 地方
  • user_capacity
    • id_user
    • id_capacity

我的项目建模可能存在问题,但我有一个Capacity类和一个User类,User有一个属性,其中包含一个数组,其中包含数据库中所有可用Capacity,并且每个Capacity对象都有一个active属性,如果表中有一个条目user_capacity链接用户和容量,则该属性设置为 True。我想要的是一种表单,它允许我将数据正确更新为表用户和user_capacity。

如果我理解正确Capacity是您要链接的另一个实体。在这种情况下,您可以使用更具体的EntityType,然后使用query_builder属性筛选值数,如文档中所述:

$builder->add('users', EntityType::class, array(
'class' => Capacity::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('capacity')
->where('capacity.active = 1')
->orderBy('capacity.id', 'ASC');
},
'choice_label' => 'id',
));

我找到了一个我认为可以接受的解决方案,并且可能对登陆此页面的人有用。所以我所做的是我更改了我的User类,以便$capacities属性现在只包含与用户相关的Capacity对象。但是为了获得表单上所有可用的容量,我将它们作为一个选项(allCapacities)传递,并迭代它们以查找User->capacities中存在的容量以检查表单中的容量。

用于生成窗体的更新类如下所示:

class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class, array(
'required'    => true,
'label' => "Login (pour Jean Durant → jdurant)"
))
->add('displayname', TextType::class, array(
'label' => "Nom à afficher"
));
$choices = array();
$choicesActive = array();
foreach ($options["allCapacities"] as $id => $capacity) {
$choices[] = $capacity;
if ($builder->getData()->hasCapacity($capacity)) {
$choicesActive[] = $capacity;
}
}
$builder->add('capacities', ChoiceType::class, array(
'choices' => $choices,
'data' => $choicesActive,
'choice_label' => function($category, $key, $index) {
return $category->getLabel();
},
'label' => "Groupes",
'multiple' => True,
'expanded' => True,
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => User::class,
));
$resolver->setRequired(array(
'allCapacities',
));

}
public function getName()
{
return 'capacity';
}
}

这按预期工作,似乎过于复杂,但通过更改设计可能会有更简单的解决方案,欢迎对此发表任何评论!

最新更新