问题
I am looking for a tutorial or example module that uses ZF2 and Doctrine to join two or more tables, creates a form, and updates all tables with form input/changes. (It would be an added bonus to find something that includes a method to add records in associated tables when the query doesn’t find records to join.)
The pattern I’m trying to replicate in ZF2/Doctrine is something like this: perhaps there is a members
table and a personal_info
table. While there is a one-to-one relationship between the tables, we’ll only need to log personal information for a fraction of the members, so to avoid burdening the system we will only add matching records to the personal_info
table as needed. Our form will include boxes to update data from both tables: a phone
input would update a record from the members
table and a spouse
input would update a record from the personal_info
table.
A php version might follow these steps to construct the form: 1) query the personal_info
table to determine whether a record exists for a user-supplied member id
; 2) if it doesn’t exist, add a new record to personal_info
; 3) create a query joining the tables; and 4) create a form populated with data from the query. The action triggered by a user’s update of the form would have two steps: 1) update the members
table, and 2) update the personal_info
table.
In searching for tutorials and examples, I haven’t come across anything that updates two tables from one form or that adds a missing record to a joined table when needed.
EDIT:
Following Sam's suggestion, I have followed the examples in the DoctrineModule but I can’t make it work. I’ve set up the Entities, Fieldsets, Forms, Views and Controller, but data doesn’t get passed from the database to the rendered form and back. Neither the update form nor the create form will update the database; and database data is not fed to the value attributes of the update form elements.
I know that data is extracted from the database because putting a non-existent id in the url router gives an error. I can't isolate whether there is a problem between the entities and the fieldsets, between the fieldsets and the forms, in the controller or in the view(s). Here are my files:
Member Entity:
<?php
namespace Members\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Members\Entity\PersonalInfo;
/**
* Members
*
* @ORM\Entity
* @ORM\Table(name="members")
* @property string $memberFirstName
* @property string $memberLastName
* @property int $memberID
*/
class Member
{
/**
* @ORM\Id
* @ORM\Column(type="integer");
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $memberID;
/**
* @ORM\Column(type="string")
*/
protected $memberFirstName;
/**
* @ORM\Column(type="string")
*/
protected $memberLastName;
/**
* @ORM\OneToMany(targetEntity="Members\Entity\PersonalInfo", mappedBy="member", cascade={"persist"})
*/
protected $personalInfo;
/**
* initialize collections
*/
public function __construct()
{
$this->personalInfo = new ArrayCollection();
}
/**
* Get MemberID
*
* @return integer
*/
public function getMemberID()
{
return $this->memberID;
}
/**
* Get MemberLastName
*
* @return string
*/
public function getMemberLastName()
{
return $this->memberLastName;
}
/**
* Set MemberLastName
*
* @param string $memberLastName
*/
public function setMemberLastName($memberLastName)
{
$this->memberLastName = $memberLastName;
return $this;
}
/**
* Get MemberFirstName
*
* @return string
*/
public function getMemberFirstName()
{
return $this->memberFirstName;
}
/**
* Set MemberFirstName
*
* @param string $memberFirstName
*/
public function setMemberFirstName($memberFirstName)
{
$this->memberFirstName = $memberFirstName;
return $this;
}
/**
* @param Collection $personalInfo
*/
public function addPersonalInfo(Collection $personalInfo)
{
foreach ($personalInfo as $memberPersonalInfo) {
$memberPersonalInfo->setMember($this);
$this->personalInfo->add($memberPersonalInfo);
}
}
/**
* @param Collection $personalInfo
*/
public function removePersonalInfo(Collection $personalInfo)
{
foreach ($personalInfo as $memberPersonalInfo) {
$memberPersonalInfo->setMember(null);
$this->personalInfo->removeElement($memberPersonalInfo);
}
}
/**
* @return Collection
*/
public function getPersonalInfo()
{
return $this->personalInfo;
}
}
PersonalInfo Entity:
<?php
namespace Members\Entity;
use Doctrine\ORM\Mapping as ORM;
use Members\Entity\Member;
/**
* Personal Info
*
* @ORM\Entity
* @ORM\Table(name="members_personal")
* @property string $spouse
* @property string $hobbies
* @property int $memberID
* @property int $personalInfoID
*/
class PersonalInfo
{
/**
* @ORM\Column(type="integer");
*/
protected $memberID;
/**
* @ORM\Id
* @ORM\Column(type="integer");
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $personalInfoID;
/**
* @ORM\ManyToOne(targetEntity="Members\Entity\Member", inversedBy="personalInfo")
* @ORM\JoinColumn(name="memberID", referencedColumnName="memberID")
*/
protected $member;
/**
* @ORM\Column(type="string")
*/
protected $spouse;
/**
* @ORM\Column(type="string")
*/
protected $hobbies;
/**
* Get PersonalInfoID
*
* @return integer
*/
public function getPersonalInfoID()
{
return $this->personalInfoID;
}
/**
* Allow null to remove association
*
* @param Member $member
*/
public function setMember(Member $member = null)
{
$this->member = $member;
}
/**
* @return Member
*/
public function getMember()
{
return $this->member;
}
/**
* Set Spouse
*
* @param string $spouse
*/
public function setSpouse($spouse)
{
$this->spouse = $spouse;
}
/**
* Get Spouse
*
* @return string
*/
public function getSpouse()
{
return $this->spouse;
}
/**
* Set Hobbies
*
* @param string $hobbies
*/
public function setHobbies($hobbies)
{
$this->hobbies = $hobbies;
}
/**
* Get Hobbies
*
* @return string
*/
public function getHobbies()
{
return $this->hobbies;
}
}
MemberFieldset:
<?php
namespace Members\Form;
use Members\Entity\Member;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class MemberFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('member');
$this->setHydrator(new DoctrineHydrator($objectManager, 'Members\Entity\Member'))
->setObject(new Member());
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'memberID',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'memberLastName',
'attributes' => array(
'required' => 'required',
'type' => 'text',
),
'options' => array(
'label' => 'Last Name',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'memberFirstName',
'attributes' => array(
'required' => 'required',
'type' => 'text',
),
'options' => array(
'label' => 'First Name',
),
));
$personalInfoFieldset = new PersonalInfoFieldset($objectManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'personalInfo',
'options' => array(
'count' => 1,
'target_element' => $personalInfoFieldset
)
));
}
public function getInputFilterSpecification()
{
return array(
'memberID' => array(
'required' => true
),
);
return array(
'memberLastName' => array(
'required' => true
),
);
return array(
'memberFirstName' => array(
'required' => true
),
);
}
}
PersonalInfoFieldset:
<?php
namespace Members\Form;
use Members\Entity\PersonalInfo;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class PersonalInfoFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('personal-info');
$this->setHydrator(new DoctrineHydrator($objectManager, 'Members\Entity\PersonalInfo'))
->setObject(new PersonalInfo());
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'personalInfoID',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'spouse',
'type' => 'Zend\Form\Element\Text',
'attributes' => array(
'required' => 'required',
'type' => 'text',
),
'options' => array(
'label' => 'Page Title',
),
));
$this->add(array(
'name' => 'hobbies',
'type' => 'Zend\Form\Element\Text',
'attributes' => array(
'required' => 'required',
'type' => 'text',
),
'options' => array(
'label' => 'Page Name',
),
));
}
public function getInputFilterSpecification()
{
return array(
'personalInfoID' => array(
'required' => true
),
);
return array(
'spouse' => array(
'required' => true
),
);
return array(
'hobbies' => array(
'required' => true
),
);
}
}
CreateMemberForm:
<?php
namespace Members\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
class CreateMemberForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('update-member-form');
// The form will hydrate an object of type "Member"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Members\Entity\Member'));
// Add the user fieldset, and set it as the base fieldset
$memberFieldset = new MemberFieldset($objectManager);
$memberFieldset->setUseAsBaseFieldset(true);
$this->add($memberFieldset);
// submit elements
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'memberID',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'memberLastName',
'attributes' => array(
'id' => 'memberLastName',
'type' => 'text',
'class' => 'col-lg-10',
),
'options' => array(
'label' => 'Last Name',
'label_attributes' => array(
'class' => 'col-lg-2 control-col-label'
),
),
));
$this->add(array(
'name' => 'memberFirstName',
'attributes' => array(
'id' => 'memberFirstName',
'type' => 'text',
'class' => 'col-lg-10',
),
'options' => array(
'label' => 'First Name',
'label_attributes' => array(
'class' => 'col-lg-2 control-col-label'
),
),
));
$this->add(array(
'name' => 'spouse',
'attributes' => array(
'id' => 'spouse',
'type' => 'text',
'class' => 'col-lg-10',
),
'options' => array(
'label' => 'Spouse',
'label_attributes' => array(
'class' => 'col-lg-2 control-col-label'
),
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'id' => 'submit',
'type' => 'submit',
'value' => 'Go',
'id' => 'submitbutton',
),
));
// Optionally set your validation group here
}
}
UpdateMemberForm:
<?php
namespace Members\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
class UpdateMemberForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('update-member-form');
// The form will hydrate an object of type "Member"
$this->setHydrator(new DoctrineHydrator($objectManager, 'Members\Entity\Member'));
// Add the user fieldset, and set it as the base fieldset
$memberFieldset = new MemberFieldset($objectManager);
$memberFieldset->setUseAsBaseFieldset(true);
$this->add($memberFieldset);
// submit elements
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'memberID',
'attributes' => array(
'type' => 'hidden',
),
));
$this->add(array(
'name' => 'memberLastName',
'attributes' => array(
'id' => 'memberLastName',
'type' => 'text',
'class' => 'col-lg-9',
),
'options' => array(
'label' => 'Last Name',
'label_attributes' => array(
'class' => 'col-lg-2 control-col-label'
),
),
));
$this->add(array(
'name' => 'memberFirstName',
'attributes' => array(
'id' => 'memberFirstName',
'type' => 'text',
'class' => 'col-lg-9',
),
'options' => array(
'label' => 'First Name',
'label_attributes' => array(
'class' => 'col-lg-2 control-col-label'
),
),
));
$this->add(array(
'name' => 'spouse',
'attributes' => array(
'id' => 'spouse',
'type' => 'text',
'class' => 'col-lg-9',
),
'options' => array(
'label' => 'Spouse',
'label_attributes' => array(
'class' => 'col-lg-2 control-col-label'
),
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'id' => 'submit',
'type' => 'submit',
'value' => 'Go',
'id' => 'submitbutton',
),
));
// Optionally set your validation group here
}
}
MemberController:
<?php
namespace Members\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Members\Entity\Member;
use Members\Form\CreateMemberForm;
use Members\Form\UpdateMemberForm;
use Doctrine\ORM\EntityManager;
class MemberController extends AbstractActionController
{
/**
* @var Doctrine\ORM\EntityManager
*/
protected $em;
public function setEntityManager(EntityManager $em)
{
$this->em = $em;
}
public function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->em;
}
// ... //
public function createAction()
{
// Get ObjectManager from the ServiceManager
$objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
// Create the form and inject the ObjectManager
$form = new CreateMemberForm($objectManager);
// Create a new, empty entity and bind it to the form
$member = new Member();
$form->bind($member);
if ($this->request->isPost()) {
$form->setData($this->request->getPost());
if ($form->isValid()) {
$objectManager->persist($member);
$objectManager->flush();
}
}
return array('form' => $form);
}
public function updateAction()
{
$memberID = (int)$this->getEvent()->getRouteMatch()->getParam('memberID');
if (!$memberID) {
return $this->redirect()->toRoute('members', array('action'=>'create'));
}
$member = $this->getEntityManager()->find('Members\Entity\Member', $memberID);
// Get your ObjectManager from the ServiceManager
$objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
// Create the form and inject the ObjectManager
$form = new UpdateMemberForm($objectManager);
$form->setBindOnValidate(false);
$form->bind($member);
$form->get('submit')->setAttribute('label', 'Update');
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$form->bindValues();
$this->getEntityManager()->flush();
// Redirect to list of members
return $this->redirect()->toRoute('members');
}
}
return array(
'memberID' => $memberID,
'form' => $form,
);
}
// ... //
}
update.phtml:
<?php
$title = 'Update Member';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<?php
$form = $this->form;
$form->setAttribute('action',
$this->url('members', array('action' => 'update', 'memberID'=>$this->memberID)));
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formHidden($form->get('memberID'));
echo $this->formRow($form->get('memberFirstName')) . "<br clear='both'/>";
echo $this->formRow($form->get('memberLastName')) . "<br clear='both'/>";
echo $this->formRow($form->get('spouse')) . "<br clear='both'/>";
echo $this->formInput($form->get('submit'));
echo $this->form()->closeTag($form);
回答1:
The two tables have a relationship so the parent object is still member
I assume. With this in mind you still have to create two Fieldset
elements. One that matches the structure for a member
and the other one that matches the data for personal_info
. The later being added as a sub-fieldset onto the MemberFieldset
.
That's actually all there is to it, the DoctrineObject
(Hydrator) should be able to take care about the ID mapping if it's a new entry. If it's an edit-entry the ID data is there anyways. It's not really that different of a deal than the examples given by the DoctrineModule /docs.
来源:https://stackoverflow.com/questions/22648186/update-two-tables-from-one-form-with-zf2-and-doctrine