如何在Symfony2中将表单保存到数据库中,而不是实体
我目前正在使用Symfony2重新构建Web应用程序。我希望用户更新他们在我们的MySQL数据库上的特定记录。但是,用户对计算机用户并不自信,并且不喜欢Web应用程序(以其当前形式)的工作方式。 因此,从UI/UX的角度来看,我决定使用2种形式来编辑特定的数据,而不是用户不喜欢的当前1形式。如何在Symfony2中将表单保存到数据库中,而不是实体
有问题的MySQL数据库表包含许多信息领域,例如他们的个人信息和与他们相关的其他信息。表单被分割以反映这一点,其中一个表单更新个人细节,一个表单更新其余表单,因此用户不必处理一个长表单。
此刻虽然,当我去使用一个形式,我得到的错误:
The class 'Symfony\Component\Form\Form' was not found in the chain configured namespaces during Form Submission
此错误是在这个question解决。但是,这给我留下了一个问题。目前,由于我将表单分成两部分,因此我无法将此数据保存到数据库。我可以通过使用一种形式来克服这一点,但这违背了数据库用户的意愿。此外,我知道完全有可能使用两种或更多种形式将特定数据添加到单个数据库,就像我之前完成的那样,而不是在Symfony中。
有谁知道,或有建议,我该如何克服这一点?由于当前数据量庞大,因此更改数据库不存在问题。
UPDATE
这里是缺少视图,控制器并形成文件。
view.html.twig
<!-- Modal Windows: Edit Instructor Personal Details -->
<div id="editPersonal" style="display:none;">
<div class="modal-head">
<h2>Edit Personal Details For: <font-color="red !important">{{instructor.firstName}} {{instructor.surname}}</font></h2>
</div>
<div class="modal-body">
<form action="#" method="post" {{ form_enctype(ipde) }} id="editPersonalDetails" class="modaledit">
<table class="modalform-col1">
<tbody>
<tr class="hidden">
<th>{{ form_label(ipde.id, 'ID*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.id) }}
{{ form_widget(ipde.id, { 'attr': {'class': 'textfield'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.firstName, 'First Name*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.firstName) }}
{{ form_widget(ipde.firstName, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.surname, 'Surname*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.surname) }}
{{ form_widget(ipde.surname, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.address1, 'Address Line 1*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.address1) }}
{{ form_widget(ipde.address1, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.address2, 'Address Line 2', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.address2) }}
{{ form_widget(ipde.address2, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.town, 'Town*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.town) }}
{{ form_widget(ipde.town, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.county, 'County*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.county) }}
{{ form_widget(ipde.county, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.postcode, 'Postcode*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.postcode) }}
{{ form_widget(ipde.postcode, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ipde.email, 'Email*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ipde.email) }}
{{ form_widget(ipde.email, { 'attr': {'class': 'text'}}) }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<div class="modal-placeright">
<a href="#close" rel="modal:close" class="closebutton">Close Without Saving</a>
<input type="submit" value="Save Changes" id="savebuttonpr" class="savebutton" />
{{ form_rest(ipde) }}
</div>
</div>
</div>
DefaultController.php
<?php
namespace PCUK\InstructorBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use PCUK\InstructorBundle\Form\IpdeType;
use PCUK\InstructorBundle\Form\IrType;
use PCUK\InstructorBundle\Form\BaType;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function viewAction($instructor, Request $request)
{
// Database connection
$insrep = $this->getDoctrine()->getManager();
// Get Instructor from Entity for Form use
$instructorQ = $insrep->getRepository('InstructorBundle:MapInstructors')->find($instructor);
// Get Shared Branches from Entity for Form use
$instructorS = $insrep->getRepository('InstructorBundle:MapInstructorShared')->find($instructor);
// Generate Form to edit Instructor Personal Details
$ipde = $this->createForm(new IpdeType(), $instructorQ);
// Handle Form submission to edit Instructor Personal Details
if ($request->getMethod() == 'POST') {
$ipde->bind($request);
if ($ipde->isValid()) {
// perform some action, such as saving the task to the database
//if ($this->request->isXmlHttpRequest()){
//return data ajax requires.
//}
$em = $this->getDoctrine()->getManager();
$em->persist($ipde);
$em->flush();
return $this->redirect($this->generateUrl('task_success'));
}
}
// Generate Form to edit Instructor Records
$ir = $this->createForm(new IrType(), $instructorQ);
// Generate Form to edit Instructor Records
$ba = $this->createForm(new BaType(), $instructorS);
// Return data to view
return $this->render('InstructorBundle:Default:view.html.twig', array(
'ipde' => $ipde->createView(),
'ir' => $ir->createView(),
'ba' => $ba->createView()
));
}
}
IpdeType.php - 个人资料表格
<?php
// src/PCUK/InstructorBundle/Form/Type/IpdeType.php
// This is to handle forms for the Instructor Personal Details Form
namespace PCUK\InstructorBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FileField;
class IpdeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', 'integer', array('required'=>false));
//Personal Details
$builder->add('firstName', 'text', array('required'=>false));
$builder->add('surname', 'text', array('required'=>false));
$builder->add('address1', 'text', array('required'=>false));
$builder->add('address2', 'text', array('required'=>false));
$builder->add('town', 'text', array('required'=>false));
$builder->add('county', 'text', array('required'=>false));
$builder->add('postcode', 'text', array('required'=>false));
$builder->add('email', 'text', array('required'=>false));
$builder->add('phone', 'text', array('required'=>false));
$builder->add('mobile', 'text', array('required'=>false));
$builder->add('notes', 'text', array('required'=>false));
}
public function getName()
{
return 'ipde';
}
}
IrType.php - 其他信息表
<?php
// src/PCUK/InstructorBundle/Form/Type/IrType.php
// This is to handle forms for the Instructor Records Form
namespace PCUK\InstructorBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FileField;
class IrType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', 'integer', array('required'=>false));
$builder->add('primaryArea', 'integer', array('required'=>false));
$builder->add('primaryBranch','entity', array('class'=>'PCUK\InstructorBundle\Entity\MapBranches', 'property'=>'branchname'));
$builder->add('begin', 'date', array('required'=>false));
$builder->add('lastCrb', 'date', array('required'=>false));
$builder->add('latestCpd', 'date', array('required'=>false));
$builder->add('preferredLevel','entity', array('class'=>'PCUK\InstructorBundle\Entity\MapInstructorLevels', 'property'=>'name'));
$builder->add('preferredDiscipline','entity', array('class'=>'PCUK\InstructorBundle\Entity\MapInstructorLevels', 'property'=>'name'));
$builder->add('currentLevel','entity', array('class'=>'PCUK\InstructorBundle\Entity\MapInstructorLevels', 'property'=>'name'));
$builder->add('bhs', 'checkbox', array('required'=>false));
$builder->add('visiting','entity', array('class'=>'PCUK\InstructorBundle\Entity\MapInstructorVis', 'property'=>'name'));
}
public function getName()
{
return 'ir';
}
}
更新:29/04/13 继james_t的建议,我在两个,每个分别形式拆分实体。但是,原始错误仍然存在。
我也在控制器中创建了一个新的操作,并且由于拆分实体没有解决问题,我恢复为使用单个实体。我现在控制器看起来如下:
的viewAction 公共职能的viewAction($讲师,请求$请求) {// 数据库连接 $ insrep = $这个 - > getDoctrine() - > getManager();
// Get IPDE from Entity for Form use
$instructorIpde = $insrep->getRepository('InstructorBundle:MapInstructors')->find($instructor);
// Generate Form to edit Instructor Personal Details
$ipde = $this->createForm(new IpdeType(), $instructorIpde);
// Get IR from Entity for Form use
$instructorIr = $insrep->getRepository('InstructorBundle:MapInstructors')->find($instructor);
// Generate Form to edit Instructor Records
$ir = $this->createForm(new IrType(), $instructorIr);
// Get Shared Branches from Entity for Form use
$instructorBa = $insrep->getRepository('InstructorBundle:MapInstructorShared')->find($instructor);
// Generate Form to edit Instructor Records
$ba = $this->createForm(new BaType(), $instructorBa);
// Return data to view
return $this->render('InstructorBundle:Default:view.html.twig', array(
'pagename' => $iname . ' - Instructors',
'ipde' => $ipde->createView(),
'ir' => $ir->createView(),
'ba' => $ba->createView(),
'iid' => $instructor
));
}
ipdeAction
公共函数ipdeAction($指导员,请求$请求) { //数据库连接 $ insrep = $这 - > getDoctrine() - > getManager();
// Get IPDE from Entity for Form use
$instructorIpde = $insrep->getRepository('InstructorBundle:MapInstructors')->find($instructor);
// Generate Form to edit Instructor Personal Details
$ipde = $this->createForm(new IpdeType(), $instructorIpde);
// Handle Form submission to edit Instructor Personal Details
if ($request->getMethod() == 'POST') {
$ipde->bind($request);
if ($ipde->isValid()) {
// perform some action, such as saving the task to the database
//if ($this->request->isXmlHttpRequest()){
//return data ajax requires.
//}
$em = $this->getDoctrine()->getManager();
$em->persist($ipde);
$em->flush();
$params = array(
'instructor' => $instructor,
);
return $this->redirect($this->generateUrl('instructor_viewinstructor', $params));
}
}
}
我也修正了这个更新,我view.html.twig文件:
<form action="#" method="post" {{ form_enctype(ipde) }} id="editPersonalDetails" class="modaledit">
这样:
<form action="{{ path('instructor_viewinstructor_ipde', {'instructor' :iid}) }}" method="post" {{ form_enctype(ipde) }} id="editPersonalDetails" class="modaledit">
我的路由。 yml文件现在看起来也是这样:
instructor_viewinstructor:
pattern: /instructors/view/{instructor}
defaults: { _controller: InstructorBundle:Default:view }
instructor_viewinstructor_ipde:
pattern: /instructors/view/{instructor}/ipde
defaults: { _controller: InstructorBundle:Default:ipde }
requirements:
_method: POST
经过大量搜索,我在Symfony2 Cookbook中遇到了这个条目。
说实话,我跳过了构建表格等的整个部分,因为我觉得我没有做错(这是辩论我知道)。我觉得这个问题关心的是将这些数据保存到数据库中,因此我跳到了cookbook的这一部分,该部分讲述了将数据保存到数据库的代码。
我的代码一直是这样的:
// Database connection
$insrep = $this->getDoctrine()->getManager();
if ($ipde->isValid()) {
// perform some action, such as saving the task to the database
//if ($this->request->isXmlHttpRequest()){
//return data ajax requires.
//}
$em = $this->getDoctrine()->getManager();
$em->persist($ipde);
$em->flush();
$params = array(
'instructor' => $instructor,
);
return $this->redirect($this->generateUrl('instructor_viewinstructor', $params));
}
在本文档中,它是:
if ($form->isValid()) {
$registration = $form->getData();
$em->persist($registration->getUser());
$em->flush();
return $this->redirect(...);
}
return $this->render(
'AcmeAccountBundle:Account:register.html.twig',
array('form' => $form->createView())
);
我发现我一直在使用$em = $this->getDoctrine()->getManager();
中,而文档建议您使用的差异$registration = $form->getData();
。我改变了我的代码,使用->getData()
代码,它的工作原理。没有更多的错误,并且数据被保存到数据库!
这似乎有点奇怪我的事实,你正在生成3种形式(其中1两次相同的实体,$ IR和$ IPDE)。你得到的错误可能与此有关。
我过去就这么做过,我可以分享一些指引你:
你不需要为每个表单一个单独的实体,您需要为您的实体单独的形式。不要误会我的意思,它可以以任何方式工作,但它并没有真正改变。在你的代码中,你只需要使用比所需连接多两倍的连接。
您正在向您的视图传递3个表单,但只呈现一个,为什么?,除非您使用AJAX,否则它没有任何意义。
让我描述的路要走:
创建实体您的应用需求(每桌通常一个),你可以将它们分割但是这通常只需要更多的工作时,复杂的逻辑(即不同用户对同一行的不同类型的许可/所有权,虽然这是smelly)。
如果你想使用验证组件,以确保您得到良好的数据,使用validation groups,一组为每个表单要构建)。
为每个要构建的表单创建表单类型。听起来很明显。你可以为任何实体提供尽可能多的表单类型。在每个表单上,只包含您感兴趣的字段。
为每个表单创建一个视图。非常直截了当。
-
这里是事情变化最大的地方。你真正需要的是,每个表格一个控制器动作,不要混合它们,你什么都不存储。每个控制器动作应:
- 加载您有兴趣
- 实体构建正确类型的一种形式。
- 如果GET,然后渲染只是一个的形式并返回。
- 如果POST,绑定请求,保存并做一些事情。
基本工作流程是这样的:
- 用户想编辑的实体ID = X,
- 你叫可以满足您要编辑的数据控制器和返回表单。
- 用户提交表单并保存数据。你可以玩重定向链接进程的两个部分,只是重定向到你的第二个处理扩展信息的控制器的url。如果您正在链接,则可能会发现使用flash messages可让用户知道信息已保存。
希望这会有所帮助。
我已经玩过使用单独的控制器操作来执行保存,但这个该死的错误仍然出现。我觉得这个问题是无法将表单提交给数据库。我打算使用AJAX,但我想要一个回落,以防他们没有Javascript。所以我试图让它在最基本的层面上工作,然后再进入AJAX调用。此外,有三种形式用于UX用途,所以我不会用大量数据压倒计算机文盲。 – mickburkejnr 2013-04-29 15:03:44
我从用户体验的角度了解您的意思,但从服务器应用程序设计角度来看,混合这样的东西似乎并不是一个好习惯。有一种叫做[单一责任原则](http://en.wikipedia.org/wiki/Single_responsibility_principle)。您还需要在使用AJAX时保持控制器分离,并使用模板继承将所有3种表单合并到同一页面上。保持它自己的位置可以帮助你防止这些错误。 – Xocoatzin 2013-04-29 15:13:39
我不认为我完全关注你对控制器的分离。我知道我应该分开控制器,现在我已经完成了。但是这个错误仍然会发生,当它不应该? – mickburkejnr 2013-04-29 15:15:24
表单和控制器的代码将会很有帮助。表单是单独编辑还是一个接一个地进行编辑(如设置向导)? – 2013-04-25 09:42:13
对不起,知道有些事我忘了!我已经将它们添加到了那里,供您查看。 – mickburkejnr 2013-04-25 09:50:31
当你说数据库,你的意思是表?将Native Queries与Doctrine一起使用并读取请求变量没有什么问题,它只是更多的代码,但是它可以工作。另外,表单不需要实体。这听起来像你有一个显示问题,只需使用[嵌入窗体](http://symfony.com/doc/current/book/forms.html#embedding-a-single-object),然后使用你的模板来制作它'容易'为您的用户。 – 2013-04-25 17:54:49