【发布时间】:2014-08-23 05:22:45
【问题描述】:
我在数据库中的
Server实体和Client实体之间有一个OneToMany关联。 一台服务器可以有多个客户端。我想制作一个表单,用户可以从下拉列表中选择服务器,为新客户端填写一些详细信息,然后提交。
目标
要创建一个表单,用户可以将数据输入到 Client 的字段中,请从下拉列表中选择 Server,然后单击提交并通过 Doctrine 保留此数据(和关联)。
很简单,对吧?一定不行。我们会解决的。这是目前的漂亮形式:
注意事项:
- 服务器由
Server实体 (EntityRepository::findAll()) 填充 - 客户端是带有硬编码值的下拉菜单
- 端口、端点、用户名和密码都是文本字段
客户实体
以我的无限智慧,我声明我的Client 实体具有以下构造函数签名:
class Client
{
/** -- SNIP -- **/
public function __construct($type, $port, $endPoint, $authPassword, $authUsername);
/** -- SNIP -- **/
}
这不会改变。要创建有效的Client 对象,存在上述构造函数参数。它们是非可选的,如果没有在对象实例化时给出上述参数,则无法创建此对象。
潜在问题:
type属性是不可变的。创建客户端后,您无法更改类型。我没有
type的设置器。它只是一个构造函数参数。这是因为一旦创建了客户端,您就无法更改类型。因此,我在实体级别执行此操作。结果,没有setType()或changeType()方法。我没有标准的
setObject命名约定。我声明要更改端口,例如,方法名称是changePort()而不是setPort()。这就是我在使用 ORM 之前要求我的对象 API 发挥作用的方式。
服务器实体
我使用__toString() 连接name 和ipAddress 成员以显示在表单下拉列表中:
class Server
{
/** -- SNIP -- **/
public function __toString()
{
return sprintf('%s - %s', $this->name, $this->ipAddress);
}
/** -- SNIP -- **/
}
自定义表单类型
我使用Building Forms with Entities 作为我的代码的基线。
这是我为构建表单而创建的ClientType:
class ClientType extends AbstractType
{
/**
* @var UrlGenerator
*/
protected $urlGenerator;
/**
* @constructor
*
* @param UrlGenerator $urlGenerator
*/
public function __construct(UrlGenerator $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** Dropdown box containing the server name **/
$builder->add('server', 'entity', [
'class' => 'App\Model\Entity\Server',
'query_builder' => function(ServerRepository $serverRepository) {
return $serverRepository->createQueryBuilder('s');
},
'empty_data' => '--- NO SERVERS ---'
]);
/** Dropdown box containing the client names **/
$builder->add('client', 'choice', [
'choices' => [
'transmission' => 'transmission',
'deluge' => 'deluge'
],
'mapped' => false
]);
/** The rest of the form elements **/
$builder->add('port')
->add('authUsername')
->add('authPassword')
->add('endPoint')
->add('addClient', 'submit');
$builder->setAction($this->urlGenerator->generate('admin_servers_add_client'))->setMethod('POST');
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'data_class' => 'App\Model\Entity\Client',
'empty_data' => function(FormInterface $form) {
return new Client(
$form->getData()['client'],
$form->getData()['port'],
$form->getData()['endPoint'],
$form->getData()['authPassword'],
$form->getData()['authUsername']
);
}
]);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'client';
}
}
上面的代码实际上是生成要在客户端使用的表单(通过 twig)。
问题
首先,使用上面的代码,提交表单给我:
NoSuchPropertyException 在 PropertyAccessor.php 第 456 行: 属性“port”和“addPort()”/“removePort()”、“setPort()”、“port()”、“__set()”或“__call()”方法之一都不存在并且具有公共在“App\Model\Entity\Client”类中访问。
所以它找不到port 方法。那是因为它是changePort(),正如我之前解释的那样。我如何告诉它应该使用 changePort() 代替?根据docs,我必须将entity 类型用于端口、端点等。但它们只是文本字段。我该如何以正确的方式解决这个问题?
我试过了:
- 在端口、authUsername 等上设置
['mapped' => false]。这给了我所有客户端字段的null,但它确实似乎有相关的服务器详细信息。不管怎样,$form->isValid()返回 false。这是var_dump()向我展示的内容:
- 其他事物的组合,包括将每个 on 字段设置为“实体”等等。
基本上,“它不起作用”。但这是我所拥有的。 我做错了什么?我一遍又一遍地阅读手册,但一切都相距甚远,我不知道我是否应该使用DataTransformer、Entity Field Type 或其他方式。我已经接近完全放弃 Symfony/Forms 并且只用十分之一的时间自己编写这个。
有人可以给我一个关于如何到达我想去的地方的可靠答案吗?这也可能对未来的用户有所帮助:-)
【问题讨论】:
标签: php symfony doctrine-orm symfony-forms