更新:我只是确切地说我是Symfony2的新手并且通常使用框架。我想澄清一下我的问题:我希望有一个包含许多字段的大表单,每个字段的标签来自第二个DB。在没有框架的情况下,在纯PHP中需要30分钟,但是如何使用Sf2呢?
我有一个问题要以同一个实体多次显示在一个表单中
以下是背景:
我有一个停车场,有多个停车位。为了最好地管理停车场,决定优先订购每个停车位
停车场的经理可以随时改变优先级,但必须有历史记录
停车位已经宣布,经理无法对其进行编辑
为此,我创建了实体:ParkingPlaces和Historical。作为ManyToOne ParkingPlaces的历史
我希望对于每个停车位标签,有一个字段以一种形式输入占用优先权
实体声明如下:
class Historical
{
/**
* @ORM\Column(name="date", type="datetime")
*/
private date;
/**
* @ORM\ManyToOne(targetEntity="ParkingPlace")
* @ORM\JoinColumn(name="parkingplace_id", referencedColumnName="id")
*/
private parkingPlace;
/**
* @ORM\Column(name="priority", type="integer")
*/
private priority;
}
class ParkingPlace
{
/**
* @ORM\Column(name="name", type="string", lenght=255)
*/
private name;
/**
* @ORM\OneToMany(targetEntity="Historical", mappedBy="parkingPlace")
*/
private historical;
}
这就是我希望的渲染:
<form>
<fieldset>
<legend>Parking A1</legend>
<input name="priority" type="number" />
</fieldset>
<fieldset>
<legend>Parking A2</legend>
<input name="priority" type="number" />
</fieldset>
<fieldset>
<legend>Parking A3</legend>
<input name="priority" type="number" />
</fieldset>
<fieldset>
<legend>Parking A4</legend>
<input name="priority" type="number" />
</fieldset>
...
</form>
我花了很多时间,但我没有任何解决方案。我在Stack上尝试了很多解决方案,但没有人解决我的问题。
答案 0 :(得分:2)
我设法回答了这个问题。我改变了数据库架构并在表单中使用了“收集”字段。
我在这里留下整个代码。我希望它会帮助其他人。
历史实体
//src/Xxx/YyyBundle/Entity/History.php
namespace Xxx\YyyBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* Xxx\YyyBundle\Entity\History
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Xxx\YyyBundle\Entity\HistoryRepository")
* @UniqueEntity(
* fields="name",
* message="Il existe déjà une table ayant ce nom."
* )
*/
class History
{
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string $name
*
* @ORM\Column(name="name", type="string", length=255, unique=true)
* @Assert\NotBlank(message="Le nom de la table a été oublié")
*/
private $name;
/**
* @var datetime $date
*
* @ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* @var Xxx\ZzzBundle\Entity\User $agent
*
* @ORM\ManyToOne(targetEntity="Ratp\AgentBundle\Entity\User")
* @ORM\JoinColumn(name="agent_id", referencedColumnName="id")
*/
private $agent;
/**
* @var Xxx\YyyBundle\Entity\ParkingPlacePriority $agent
*
* @ORM\OneToMany(targetEntity="ParkingPlacePriority", mappedBy="history", cascade={"persist"})
* @Assert\Valid(traverse=true)
*/
private $parkingPlacesPriorities;
/**
* Constructeur
*
* @return string
*/
public function __construct()
{
$this->parkingPlacesPriorities = new ArrayCollection();
}
/**
* Get History's name
*
* @return string
*/
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set date
*
* @param datetime $date
*/
public function setDate($date)
{
$this->date = $date;
}
/**
* Get date
*
* @return datetime
*/
public function getDate()
{
return $this->date;
}
/**
* Set agent
*
* @param Xxx\ZzzBundle\Entity\User $agent
*/
public function setAgent(\Xxx\ZzzBundle\Entity\User $agent)
{
$this->agent = $agent;
}
/**
* Get agent
*
* @return Xxx\ZzzBundle\Entity\User
*/
public function getAgent()
{
return $this->agent;
}
/**
* Set parkingPlacesPriorities
*
* @param Xxx\YyyBundle\Entity\ParkingPlacePriority $agent
*/
public function setParkingPlacesPriorities(ArrayCollection $parkingPlacesPriorities)
{
$this->parkingPlacesPriorities = $parkingPlacesPriorities;
foreach($parkingPlacesPriorities as $parkingPlacePriority)
{
$parkingPlacePriority->setHistory($this);
}
}
/**
* Get parkingPlacesPriorities
*
* @return Xxx\YyyBundle\Entity\ParkingPlacePriority
*/
public function addParkingPlacesPriorities(\Xxx\YyyBundle\Entity\ParkingPlacePriority $parkingPlacesPriorities)
{
return $this->parkingPlacesPriorities[] = $parkingPlacesPriorities;
foreach($parkingPlacesPriorities as $parkingPlacePriority)
{
$parkingPlacePriority->setHistory($this);
}
}
/**
* Get parkingPlacesPriorities
*
* @return Xxx\YyyBundle\Entity\ParkingPlacePriority
*/
public function getParkingPlacesPriorities()
{
return $this->parkingPlacesPriorities;
}
}
ParkingPlace实体
//src/Xxx/YyyBundle/Entity/ParkingPlace.php
namespace Xxx\YyyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Xxx\YyyBundle\Entity\ParkingPlace
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Xxx\YyyBundle\Entity\ParkingPlaceRepository")
*/
class ParkingPlace
{
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var integer $orderSequence
*
* @ORM\Column(name="orderSequence", type="integer")
* @Assert\Min(
* limit="1",
* message="L'ordre d'affichage ne peux être inférieure à 1",
* invalidMessage="L'ordre d'affichage doit être un chiffre"
* )
*/
private $orderSequence;
/**
* @var string $name
*
* @ORM\Column(name="name", type="string", length=255)
* @Assert\MinLength(
* limit=1,
* message="Le nom SAET doit comporter au minimum 1 caractère"
* )
* @Assert\MaxLength(
* limit=255,
* message="Le nom SAET doit comporter au maximum 255 caractères"
* )
* @Assert\Regex(
* pattern="/^[a-z0-9-]+$/i",
* message="Le nom d'exploitation contient des caractères invalides (seul les caractères alphanumériques et le tiret '-' sont acceptés)"
* )
*/
private $name;
/**
* @var string $saetName
*
* @ORM\Column(name="saetName", type="string", length=255, unique=true)
* @Assert\MinLength(
* limit=5,
* message="Le nom SAET doit comporter au minimum 5 caractères"
* )
* @Assert\MaxLength(
* limit=255,
* message="Le nom SAET doit comporter au maximum 255 caractères"
* )
* @Assert\Regex(
* pattern="/^prk_/i",
* message="Le nom SAET ne commence pas par 'PRK_'"
* )
* @Assert\Regex(
* pattern="/^[a-z0-9_]+$/i",
* message="Le nom SAET contient des caractères invalides (seul les caractères alphanumériques et le trait de soulignemet '_' sont acceptés)"
* )
*/
private $saetName;
/**
* @var string $rail
*
* @ORM\Column(name="rail", type="string", length=5)
* @Assert\NotBlank()
* @Assert\MinLength(
* limit=1,
* message="Le nom SAET doit comporter au minimum 1 caractères"
* )
* @Assert\MaxLength(
* limit=5,
* message="Le nom SAET doit comporter au maximum 5 caractères"
* )
*/
private $rail;
/**
* @var Xxx\YyyBundle\Entity\ParkingZone $parkingZone
*
* @ORM\ManyToOne(targetEntity="ParkingZone")
* @ORM\JoinColumn(name="parkingzone_id", referencedColumnName="id")
*/
private $parkingZone;
/**
* Get ParkingPlace's name
*
* @return string
*/
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set orderSequence
*
* @param integer $orderSequence
*/
public function setOrderSequence($orderSequence)
{
$this->orderSequence = $orderSequence;
}
/**
* Get orderSequence
*
* @return integer
*/
public function getOrderSequence()
{
return $this->orderSequence;
}
/**
* Set name
*
* @param string $name
*/
public function setName($name)
{
$this->name = strtoupper($name);
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set saetName
*
* @param string $saetName
*/
public function setSaetName($saetName)
{
$this->saetName = strtoupper($saetName);
}
/**
* Get saetName
*
* @return string
*/
public function getSaetName()
{
return $this->saetName;
}
/**
* Set rail
*
* @param string $rail
*/
public function setRail($rail)
{
$this->rail = strtoupper($rail);
}
/**
* Get rail
*
* @return string
*/
public function getRail()
{
return $this->rail;
}
/**
* Set parkingZone
*
* @param Xxx\YyyBundle\Entity\ParkingZone $parkingZone
*/
public function setParkingZone(\Xxx\YyyBundle\Entity\ParkingZone $parkingZone)
{
$this->parkingZone = $parkingZone;
}
/**
* Get parkingZone
*
* @return Xxx\YyyBundle\Entity\ParkingZone
*/
public function getParkingZone()
{
return $this->parkingZone;
}
}
ParkingPlacePriority实体
//src/Xxx/YyyBundle/Entity/ParkingPlacePriority.php
namespace Xxx\YyyBundle\Entity;
use Xxx\YyyBundle\RatpGarageL1Bundle;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* Xxx\YyyBundle\Entity\ParkingPlacePriority
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Xxx\YyyBundle\Entity\ParkingPlacePriorityRepository")
*/
class ParkingPlacePriority
{
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var Xxx\YyyBundle\Entity\ParkingPlace $parkingPlace
*
* @ORM\ManyToOne(targetEntity="ParkingPlace")
* @ORM\JoinColumn(name="parkingplace_id", referencedColumnName="id")
*/
private $parkingPlace;
/**
* @var Xxx\YyyBundle\Entity\History $history
*
* @ORM\ManyToOne(targetEntity="History", cascade={"persist"})
* @ORM\JoinColumn(name="history_id", referencedColumnName="id")
*/
private $history;
/**
* @var integer $priorityIn
*
* @ORM\Column(name="priorityIn", type="integer")
* @Assert\NotBlank(message="La priorité de garage n'as pas été donné")
* @Assert\Min(
* limit="1",
* message="La priorité de garage ne peux être inférieure à 1",
* invalidMessage="La priorité doit être un chiffre"
* )
*/
private $priorityIn;
/**
* @var integer $priorityOut
*
* @ORM\Column(name="priorityOut", type="integer")
* @Assert\NotBlank(message="La priorité de dégarage n'as pas été donné")
* @Assert\Min(
* limit="1",
* message="La priorité de dégarage ne peux être inférieure à 1",
* invalidMessage="La priorité doit être un chiffre"
* )
*/
private $priorityOut;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set priorityIn
*
* @param integer $priorityIn
*/
public function setPriorityIn($priorityIn)
{
$this->priorityIn = $priorityIn;
}
/**
* Get priorityIn
*
* @return integer
*/
public function getPriorityIn()
{
return $this->priorityIn;
}
/**
* Set priorityOut
*
* @param integer $priorityOut
*/
public function setPriorityOut($priorityOut)
{
$this->priorityOut = $priorityOut;
}
/**
* Get priorityOut
*
* @return integer
*/
public function getPriorityOut()
{
return $this->priorityOut;
}
/**
* Set parkingPlace
*
* @param Xxx\YyyBundle\Entity\ParkingPlace $parkingPlace
*/
public function setParkingPlace(\Xxx\YyyBundle\Entity\ParkingPlace $parkingPlace)
{
$this->parkingPlace = $parkingPlace;
}
/**
* Get parkingPlace
*
* @return Xxx\YyyBundle\Entity\ParkingPlace
*/
public function getParkingPlace()
{
return $this->parkingPlace;
}
/**
* Set history
*
* @param Xxx\YyyBundle\Entity\History $history
*/
public function setHistory(\Xxx\YyyBundle\Entity\History $history)
{
$this->history = $history;
}
/**
* Get history
*
* @return Xxx\YyyBundle\Entity\History
*/
public function getHistory()
{
return $this->history;
}
}
历史记录控制器
//src/Xxx/YyyBundle/Controller/HistoryController.php
namespace Xxx\YyyBundle\Controller;
use Xxx\YyyBundle\Entity\ParkingPlacePriority;
use Xxx\YyyBundle\Entity\ParkingPlace;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Xxx\YyyBundle\Entity\History;
use Xxx\YyyBundle\Form\HistoryType;
/**
* History controller.
*
* @Route("")
*/
class HistoryController extends Controller
{
/**
* Lists all History entities.
*
* @Route("/", name="history")
* @Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getEntityManager();
$entities = $em->getRepository('XxxYyyBundle:History')->findAll();
return array('entities' => $entities);
}
/**
* Displays a form to create a new History entity.
*
* @Route("/new", name="history_new")
* @Template()
*/
public function newAction()
{
$em = $this->getDoctrine()->getEntityManager();
$entities = $em->getRepository('XxxYyyBundle:ParkingPlace')->findAllOrdered();
$parkingZones = $em->getRepository('XxxYyyBundle:ParkingZone')->findAllOrdered();
$history = new History();
foreach ($entities as $entity)
{
$parkingPlacePriority = new ParkingPlacePriority();
$parkingPlacePriority->setParkingPlace($entity);
$history->addParkingPlacesPriorities($parkingPlacePriority);
}
$form = $this->createForm(new HistoryType(), $history);
return array(
'entity' => $history,
'parkingPlaces' => $entities,
'parkingZones' => $parkingZones,
'form' => $form->createView()
);
}
/**
* Creates a new History entity.
*
* @Route("/create", name="history_create")
* @Method("post")
* @Template("XxxYyyBundle:History:new.html.twig")
*/
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$entities = $em->getRepository('XxxYyyBundle:ParkingPlace')->findAllOrdered();
$parkingZones = $em->getRepository('XxxYyyBundle:ParkingZone')->findAllOrdered();
$history = new History();
foreach ($entities as $entity)
{
$parkingPlacePriority = new ParkingPlacePriority();
$parkingPlacePriority->setParkingPlace($entity);
$history->addParkingPlacesPriorities($parkingPlacePriority);
}
$history->setDate(new \DateTime());
$history->setAgent($this->container->get('security.context')->getToken()->getUser());
$request = $this->getRequest();
$form = $this->createForm(new HistoryType(), $history);
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($history);
$em->flush();
return $this->redirect($this->generateUrl('parkingplacepriority_export', array('id' => $history->getId())));
}
return array(
'entity' => $history,
'parkingPlaces' => $entities,
'parkingZones' => $parkingZones,
'form' => $form->createView()
);
}
/**
* Displays a form to edit an existing History entity.
*
* @Route("/{id}/edit", name="history_edit")
* @Template("XxxYyyBundle:History:new.html.twig")
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$parkingPlaces = $em->getRepository('XxxYyyBundle:ParkingPlace')->findAllOrdered();
$parkingZones = $em->getRepository('XxxYyyBundle:ParkingZone')->findAllOrdered();
$entity = $em->getRepository('XxxYyyBundle:History')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find History entity.');
}
$entity->setName('');
$editForm = $this->createForm(new HistoryType(), $entity);
return array(
'entity' => $entity,
'parkingPlaces' => $parkingPlaces,
'parkingZones' => $parkingZones,
'form' => $editForm->createView(),
);
}
/**
* Edits an existing History entity.
*
* @Route("/{id}/update", name="history_update")
* @Method("post")
* @Template("XxxYyyBundle:History:new.html.twig")
*/
public function updateAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('XxxYyyBundle:History')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find History entity.');
}
$editForm = $this->createForm(new HistoryType(), $entity);
$deleteForm = $this->createDeleteForm($id);
$request = $this->getRequest();
$editForm->bindRequest($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('parkingplacepriority_export', array('id' => $id)));
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
);
}
}
历史记录FormType
//src/Xxx/YyyBundle/Form/HistoryType.php
namespace Xxx\YyyBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class HistoryType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('name', 'text', array(
'max_length' => 255,
'trim' => true,
)
)
->add('parkingPlacesPriorities', 'collection', array(
'type' => new ParkingPlacePriorityType(),
'by_reference' => false,
)
)
;
}
public function getName()
{
return 'xxx_yyybundle_historytype';
}
}
ParkingPlacePriority FormType
//src/Xxx/YyyBundle/Form/ParkingPlacePriorityType.php
namespace Xxx\YyyBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class ParkingPlacePriorityType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('priorityIn', 'number', array(
'precision' => 0,
)
)
->add('priorityOut', 'number', array(
'precision' => 0,
)
)
->add('parkingPlace', 'entity', array(
'class' => 'RatpGarageL1Bundle:ParkingPlace',
'read_only' => true,
)
)
;
}
public function getName()
{
return 'xxx_yyybundle_parkingplaceprioritytype';
}
}
历史新实体表单模板
// src/Xxx/YyyBundle/Resources/views/History/new.html.twig
{% extends '::base.html.twig' %}
{% block title %}
{{ parent() }} | Historique | Nouvelle table
{% endblock %}
{% block zoneMenu %}
<li class="">
<a href="" id="save">Enregistrer</a>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Zone de garage
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
{% for parkingZone in parkingZones %}
<li class="">
<a href="#{{ parkingZone.id }}">{{ parkingZone.name }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endblock %}
{% block content %}
<h1>Nouvelle table de garage/dégarage</h1>
<form action="{{ path('history_create') }}" method="post" {{ form_enctype(form) }} class="form-horizontal">
<div class="row-fluid">
<div class="control-group offset7 {% if form_errors(form.name) | length > 0 %}error{% endif %}">
{{ form_label(form.name, 'Nom de la table', { 'attr': {'class': 'control-label'} } ) }}
<div class="controls error">
{{ form_widget(form.name, { 'attr': {'class': ''} } ) }}
<span class="help-inline">{{ form_errors(form.name) }}</span>
</div>
</div>
</div>
{% set voie = '0' %}
{% set zone = '0' %}
{% set i = 0 %}
{% for pkPl in form.parkingPlacesPriorities %}
{% if voie != parkingPlaces[i].rail %}
{% if voie != '0' %}
</fieldset>
{% endif %}
{% if zone != parkingPlaces[i].parkingZone %}
{% if zone != '0' %}
</div>
{% endif %}
<div id="{{ parkingPlaces[i].parkingZone.id }}" class="zone row-fluid">
<h3 class="">{{ parkingPlaces[i].parkingZone.name }}</h3>
<div class="synoptique">
<span>{{ asset('bundles/xxxyyy/images/' ~ parkingPlaces[i].parkingZone.image) }}</span>
</div>
{% endif %}
{% set zone = parkingPlaces[i].parkingZone %}
<fieldset class="voie">
{% set voie = parkingPlaces[i].rail %}
<legend class=""><strong>Voie {{ parkingPlaces[i].rail }}</strong></legend>
{% endif %}
<fieldset name="{{ parkingPlaces[i].name }}" class="place_garage span5">
<legend>{{ parkingPlaces[i].name }}</legend>
<div class="control-group {% if form_errors(pkPl.priorityIn) | length > 0 %}error{% endif %}">
{{ form_label(pkPl.priorityIn, 'Garage', { 'attr': {'class': 'control-label'} } ) }}
<div class="controls error">
{{ form_widget(pkPl.priorityIn, { 'attr': {'class': 'garage'} } ) }}
<span class="help-inline">{{ form_errors(pkPl.priorityIn) }}</span>
</div>
</div>
<div class="control-group {% if form_errors(pkPl.priorityOut) | length > 0 %}error{% endif %}">
{{ form_label(pkPl.priorityOut, 'Dégarage', { 'attr': {'class': 'control-label'} } ) }}
<div class="controls">
{{ form_widget(pkPl.priorityOut, { 'attr': {'class': 'degarage'} } ) }}
<span class="help-inline">{{ form_errors(pkPl.priorityOut) }}</span>
</div>
</div>
<div class="control-group">
{{ form_label(pkPl.parkingPlace, 'Place de garage', { 'attr': {'class': 'control-label'} } ) }}
<div class="controls error">
{{ form_widget(pkPl.parkingPlace, { 'attr': {'class': ''} } ) }}
<span class="help-inline">{{ form_errors(pkPl.parkingPlace) }}</span>
</div>
</div>
</fieldset>
{% set i = i + 1 %}
{% endfor %}
</fieldset>
</div>
{{ form_rest(form) }}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Enregistrer</button>
<a href="{{ path('history') }}" class="btn">Annuler</a>
</div>
</form>
{% endblock %}
{% block javascript %}
<script src="{{ asset('bundles/xxxyyy/js/script.js') }}"></script>
{% endblock %}