我尝试为目录创建树结构。
首先,用户使用基本信息(名称,代码,有效期,类型等)创建目录。然后,我以另一种形式重定向用户以创建树结构。
目录可以有多个级别。 这些级别可以包含多个产品(在此处命名为“Model”)。
所以,在我的树形态中,我有一系列关卡。 在水平集合表格中,我有一系列模型。
换句话说,我有一个收集到另一个集合。
我无法将我的几个实体保存在数据库中。当我在控制器中执行$em->flush();
时,它丢失了模型值(第二个集合)。我对CatalogModel实体的组合键(由级别ID和目录代码组成)有完整性约束违规。
但是,Catalog,CatalogLevel和CatalogModel很好地保留了。两者都填充了正确的数据。
所以我不明白我做错了什么......
目录实体:请注意,我不使用自动生成的ID。代码是id,用户在创建目录时提供。
/**
* @ORM\Table(name="catalogue")
* @ORM\Entity(repositoryClass="AppBundle\Entity\CatalogRepository")
* @UniqueEntity(fields="code", message="Catalog code already exists")
*/
class Catalog
{
/**
* @ORM\Column(name="Catalogue_Code", type="string", length=15)
* @ORM\Id
* @Assert\NotBlank()
* @Assert\Length(max=15, maxMessage="The code is too long ({{ limit }} characters max)")
*/
private $code;
/**
* @ORM\OneToMany(targetEntity="CatalogLevel", mappedBy="catalog", cascade={"persist", "remove"})
* @Assert\Valid
*/
private $levels;
/**
* Constructor
*/
public function __construct()
{
$this->levels = new ArrayCollection();
}
/**
* Set code
*
* @param string $code
*
* @return Catalog
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* Get levels
*
* @return ArrayCollection
*/
public function getLevels()
{
return $this->levels;
}
/**
* Add level
*
* @param \AppBundle\Entity\CatalogLevel $level
*
* @return Catalog
*/
public function addLevel(\AppBundle\Entity\CatalogLevel $level)
{
$level->setCatalogCode($this->getCode());
$level->setCatalog($this);
if (!$this->getLevels()->contains($level)) {
$this->levels->add($level);
}
return $this;
}
/**
* Remove level
*
* @param \AppBundle\Entity\CatalogLevel $level
*/
public function removeLevel(\AppBundle\Entity\CatalogLevel $level)
{
$this->levels->removeElement($level);
}
}
CatalogLevel实体:我也不使用自动生成的ID。 Level作为由级别id和目录代码组成的复合键。当我使用表单中的添加按钮创建一个级别时,在JS中生成id。请记住,关卡是一种收集形式。
/**
* @ORM\Table(name="catalogue_niveau")
* @ORM\Entity(repositoryClass="AppBundle\Entity\CatalogLevelRepository")
*/
class CatalogLevel
{
/**
* @ORM\Column(name="Niveau_ID", type="string", length=15)
* @ORM\Id
*/
private $id;
/**
* @ORM\Column(name="Catalogue_Code", type="string", length=15)
* @ORM\Id
*/
private $catalogCode;
/**
* @ORM\Column(name="Niveau_Nom", type="string", length=40)
* @Assert\NotBlank()
*/
private $name;
/**
* @ORM\Column(name="Parent_ID", type="string", length=15, nullable=true)
* @ORM\ManyToOne(targetEntity="CatalogLevel")
* @ORM\JoinColumn(referencedColumnName="Niveau_ID", onDelete="CASCADE")
*/
private $parent;
/**
* @ORM\Column(name="Niveau_Ordre", type="integer")
*/
private $order;
/**
* @ORM\ManyToOne(targetEntity="Catalog", inversedBy="levels")
* @ORM\JoinColumn(name="Catalogue_Code", referencedColumnName="Catalogue_Code")
*/
private $catalog;
/**
* @ORM\OneToMany(targetEntity="CatalogModel", mappedBy="level", cascade={"persist", "remove"})
* @Assert\Valid
*/
private $models;
/**
* Constructor
*/
public function __construct()
{
$this->order = 0;
$this->models = new ArrayCollection();
}
/**
* Set id
*
* @param string $id
*
* @return CatalogLevel
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id
*
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* Set catalogCode
*
* @param string $catalogCode
*
* @return CatalogLevel
*/
public function setCatalogCode($catalogCode)
{
$this->catalogCode = $catalogCode;
return $this;
}
/**
* Get catalogCode
*
* @return string
*/
public function getCatalogCode()
{
return $this->catalogCode;
}
/**
* Set name
*
* @param string $name
*
* @return CatalogLevel
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set parent
*
* @param string $parent
*
* @return CatalogLevel
*/
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* @return string
*/
public function getParent()
{
return $this->parent;
}
/**
* Set order
*
* @param integer $order
*
* @return CatalogLevel
*/
public function setOrder($order)
{
$this->order = $order;
return $this;
}
/**
* Get order
*
* @return integer
*/
public function getOrder()
{
return $this->order;
}
/**
* Set catalog
*
* @param \AppBundle\Entity\Catalog $catalog
*
* @return CatalogLevel
*/
public function setCatalog(\AppBundle\Entity\Catalog $catalog = null)
{
$this->catalog = $catalog;
return $this;
}
/**
* Get catalog
*
* @return \AppBundle\Entity\Catalog
*/
public function getCatalog()
{
return $this->catalog;
}
/**
* Get models
*
* @return ArrayCollection
*/
public function getModels()
{
return $this->models;
}
/**
* Add model
*
* @param \AppBundle\Entity\CatalogModel $model
*
* @return CatalogLevel
*/
public function addModel(\AppBundle\Entity\CatalogModel $model)
{
$model->setLevel($this);
$model->setCatalog($this->getCatalog());
if (!$this->getModels()->contains($model)) {
$this->models->add($model);
}
return $this;
}
/**
* Remove model
*
* @param \AppBundle\Entity\CatalogModel $model
*/
public function removeModel(\AppBundle\Entity\CatalogModel $model)
{
$this->models->removeElement($model);
}
}
CatalogModel实体:model也有一个复合键,由代码(Model实体的代码),目录代码和级别id组成。
/**
* @ORM\Table(name="catalogue_rayon")
* @ORM\Entity(repositoryClass="AppBundle\Entity\CatalogModelRepository")
*/
class CatalogModel
{
/**
* @ORM\Column(name="Modele_Code", type="string", length=20)
* @ORM\Id
*/
private $code;
/**
* @ORM\Column(name="Catalogue_Code", type="string", length=15)
* @ORM\Id
*/
private $catalogCode;
/**
* @ORM\Column(name="Niveau_ID", type="string", length=15)
* @ORM\Id
*/
private $levelId;
/**
* @ORM\Column(name="Modele_Ordre", type="integer")
*/
private $order;
/**
* @ORM\ManyToOne(targetEntity="CatalogLevel", inversedBy="models")
* @ORM\JoinColumn(name="Niveau_ID", referencedColumnName="Niveau_ID")
*/
private $level;
/**
* @ORM\ManyToOne(targetEntity="Catalog")
* @ORM\JoinColumn(name="Catalogue_Code", referencedColumnName="Catalogue_Code")
*/
private $catalog;
/**
* @ORM\ManyToOne(targetEntity="Model")
* @ORM\JoinColumn(name="Modele_Code", referencedColumnName="Modele_Code")
*/
private $model;
/**
* @ORM\Column(name="Date_Debut", type="datetime", nullable=true)
* @Assert\Date()
*/
private $beginDate;
/**
* @ORM\Column(name="Date_Fin", type="datetime", nullable=true)
* @Assert\Date()
*/
private $endDate;
/**
* Constructor
*/
public function __construct()
{
$this->order = 0;
}
/**
* Set code
*
* @param string $code
*
* @return CatalogModel
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set catalogCode
*
* @param string $catalogCode
*
* @return CatalogModel
*/
public function setCatalogCode($catalogCode)
{
$this->catalogCode = $catalogCode;
return $this;
}
/**
* Get catalogCode
*
* @return string
*/
public function getCatalogCode()
{
return $this->catalogCode;
}
/**
* Set levelId
*
* @param integer $levelId
*
* @return CatalogModel
*/
public function setLevelId($levelId)
{
$this->levelId = $levelId;
return $this;
}
/**
* Get levelId
*
* @return integer
*/
public function getLevelId()
{
return $this->levelId;
}
/**
* Set order
*
* @param integer $order
*
* @return CatalogModel
*/
public function setOrder($order)
{
$this->order = $order;
return $this;
}
/**
* Get order
*
* @return integer
*/
public function getOrder()
{
return $this->order;
}
/**
* Set level
*
* @param \AppBundle\Entity\CatalogLevel $level
*
* @return CatalogModel
*/
public function setLevel(\AppBundle\Entity\CatalogLevel $level = null)
{
$this->level = $level;
return $this;
}
/**
* Get level
*
* @return \AppBundle\Entity\CatalogLevel
*/
public function getLevel()
{
return $this->level;
}
/**
* Set catalog
*
* @param \AppBundle\Entity\Catalog $catalog
*
* @return CatalogModel
*/
public function setCatalog(\AppBundle\Entity\Catalog $catalog = null)
{
$this->catalog = $catalog;
return $this;
}
/**
* Get catalog
*
* @return \AppBundle\Entity\Catalog
*/
public function getCatalog()
{
return $this->catalog;
}
/**
* Set beginDate
*
* @param \DateTime $beginDate
*
* @return CatalogModel
*/
public function setBeginDate($beginDate)
{
$this->beginDate = $beginDate;
return $this;
}
/**
* Get beginDate
*
* @return \DateTime
*/
public function getBeginDate()
{
return $this->beginDate;
}
/**
* Set endDate
*
* @param \DateTime $endDate
*
* @return CatalogModel
*/
public function setEndDate($endDate)
{
$this->endDate = $endDate;
return $this;
}
/**
* Get endDate
*
* @return \DateTime
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* Set model
*
* @param \AppBundle\Entity\Model $model
*
* @return CatalogModel
*/
public function setModel(\AppBundle\Entity\Model $model = null)
{
$this->model = $model;
return $this;
}
/**
* Get model
*
* @return \AppBundle\Entity\Model
*/
public function getModel()
{
return $this->model;
}
}
然后,形式:
CatalogTreeType
class CatalogTreeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('levels', CollectionType::class, array(
'entry_type' => CatalogLevelType::class,
'entry_options' => array('catalogCode' => $options['data']->getCode()),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'label' => false,
'by_reference' => false
))
->add('search', TextType::class, array(
'label' => false,
'mapped' => false,
'required' => false,
'attr' => array(
'class' => 'search',
'size' => 35,
'placeholder' => 'Search for a model...'
)
))
->add('save', SubmitType::class, array(
'attr' => array('class' => 'button-link save'),
'label' => 'Validate'
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Catalog',
// 'allow_extra_fields' => true
));
}
}
CatalogLevelType (收藏)
class CatalogLevelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'label' => false,
'attr' => array(
'class' => 'full level-name',
'size' => 35,
'placeholder' => 'Name of level'
)
))
->add('id', HiddenType::class, array(
'attr' => array('class' => 'level-id')
))
->add('parent', HiddenType::class, array(
'attr' => array('class' => 'level-parent'),
'required' => false
))
->add('models', CollectionType::class, array(
'entry_type' => CatalogModelType::class,
'entry_options' => array('catalogCode' => $options['catalogCode']),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__model_name__',
'label' => false,
'by_reference' => false
))
;
}
CatalogModelType (收藏)
class CatalogModelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('code', TextType::class, array(
'label' => false,
'attr' => array(
'class' => 'full model-field',
'readonly' => true
)
))
->add('beginDate', DateType::class, array(
'label' => 'Begins',
'widget' => 'single_text',
'html5' => false,
'attr' => array('class' => 'full'),
'format' => 'yyyy-MM-dd',
'data' => new \DateTime('0000-00-00'),
'required' => false
))
->add('endDate', DateType::class, array(
'label' => 'Ends',
'widget' => 'single_text',
'html5' => false,
'attr' => array('class' => 'full'),
'format' => 'yyyy-MM-dd',
'data' => new \DateTime('0000-00-00'),
'required' => false
))
->add('catalogCode', HiddenType::class, array(
'data' => $options['catalogCode']
))
->add('levelId', HiddenType::class, array(
'attr' => array('class' => 'model-level-id')
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\CatalogModel',
'catalogCode' => null
));
}
}
CatalogController
/**
* @Route("/catalogs/{code}/tree-structure", name="catalog_tree")
*/
public function treeAction($code, Request $request) {
// get catalog from database
$em = $this->getDoctrine()->getManager();
$catalog = $em->getRepository('AppBundle:Catalog')->find($code);
// catalog doesn't exist
if (!$catalog) {
throw $this->createNotFoundException('No catalog found for code '. $code);
}
// build the form
$form = $this->createForm(CatalogTreeType::class, $catalog);
// Form posté et valide
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($catalog); // save the catalog
// save the levels
foreach($catalog->getLevels() as $level) {
// var_dump($level);
$em->persist($level);
// save the models
foreach($level->getModels() as $model) {
// var_dump($model);
$em->persist($model);
}
}
// update database
$em->flush();
// success message
$this->addFlash('notice', 'Catalog tree structure has been created successfully !');
# redirection
// return $this->redirectToRoute('catalog_tree_edit', array('code' => $code));
}
// show page
return $this->render('catalogs/tree.html.twig', array(
'h1_title' => 'Tree structure of the catalog : ' . $catalog->getName() . ' (' . $catalog->getCode() . ')',
'form' => $form->createView(),
'code' => $catalog->getCode()
));
}
var_dump的结果:
// save the levels
foreach($catalog->getLevels() as $level) {
// var_dump($level);
$em->persist($level);
// save the models
foreach($level->getModels() as $model) {
// var_dump($model);
$em->persist($model);
}
}
等级:
object(AppBundle\Entity\CatalogLevel)[2610]
private 'id' => string 'NIV_1' (length=5)
private 'catalogCode' => string 'CATEST' (length=6)
private 'name' => string 'genou' (length=5)
private 'parent' => null
private 'order' => int 0
private 'catalog' =>
object(AppBundle\Entity\Catalog)[2493]
private 'code' => string 'CATEST' (length=6)
private 'name' => string 'Catalogue de test' (length=17)
private 'type' => string 'Normal' (length=6)
private 'promo' => null
private 'tariff' => string 'F0' (length=2)
private 'campaign' => string 'FTEST' (length=5)
private 'beginDate' =>
object(DateTime)[2495]
public 'date' => string '2016-06-16 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
private 'endDate' =>
object(DateTime)[2496]
public 'date' => string '2016-06-24 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
private 'catalogSalesForce' =>
object(Doctrine\ORM\PersistentCollection)[2473]
private 'snapshot' =>
array (size=2)
...
private 'owner' =>
&object(AppBundle\Entity\Catalog)[2493]
private 'association' =>
array (size=15)
...
private 'em' =>
object(Doctrine\ORM\EntityManager)[2086]
...
private 'backRefFieldName' => string 'catalog' (length=7)
private 'typeClass' =>
object(Doctrine\ORM\Mapping\ClassMetadata)[2489]
...
private 'isDirty' => boolean false
protected 'collection' =>
object(Doctrine\Common\Collections\ArrayCollection)[2484]
...
protected 'initialized' => boolean true
private 'forces' => null
private 'levels' =>
object(Doctrine\ORM\PersistentCollection)[2459]
private 'snapshot' =>
array (size=0)
...
private 'owner' =>
&object(AppBundle\Entity\Catalog)[2493]
private 'association' =>
array (size=15)
...
private 'em' =>
object(Doctrine\ORM\EntityManager)[2086]
...
private 'backRefFieldName' => string 'catalog' (length=7)
private 'typeClass' =>
object(Doctrine\ORM\Mapping\ClassMetadata)[2468]
...
private 'isDirty' => boolean true
protected 'collection' =>
object(Doctrine\Common\Collections\ArrayCollection)[2442]
...
protected 'initialized' => boolean true
private 'models' =>
object(Doctrine\Common\Collections\ArrayCollection)[2708]
private 'elements' =>
array (size=1)
0 =>
object(AppBundle\Entity\CatalogModel)[2735]
型号:
object(AppBundle\Entity\CatalogModel)[2735]
private 'code' => string 'BARACCBA' (length=8)
private 'catalogCode' => string 'CATEST' (length=6)
private 'levelId' => string 'NIV_1' (length=5)
private 'order' => int 0
private 'level' =>
object(AppBundle\Entity\CatalogLevel)[2610]
private 'id' => string 'NIV_1' (length=5)
private 'catalogCode' => string 'CATEST' (length=6)
private 'name' => string 'genou' (length=5)
private 'parent' => null
private 'order' => int 0
private 'catalog' =>
object(AppBundle\Entity\Catalog)[2493]
private 'code' => string 'CATEST' (length=6)
private 'name' => string 'Catalogue de test' (length=17)
private 'type' => string 'Normal' (length=6)
private 'promo' => null
private 'tariff' => string 'F0' (length=2)
private 'campaign' => string 'FTEST' (length=5)
private 'beginDate' =>
object(DateTime)[2495]
...
private 'endDate' =>
object(DateTime)[2496]
...
private 'catalogSalesForce' =>
object(Doctrine\ORM\PersistentCollection)[2473]
...
private 'forces' => null
private 'levels' =>
object(Doctrine\ORM\PersistentCollection)[2459]
...
private 'models' =>
object(Doctrine\Common\Collections\ArrayCollection)[2708]
private 'elements' =>
array (size=1)
...
private 'catalog' => null
private 'model' => null
private 'beginDate' =>
object(DateTime)[2722]
public 'date' => string '1905-05-05 00:00:00.000000' (length=26)
public 'timezone_type' => int 1
public 'timezone' => string '+00:00' (length=6)
private 'endDate' =>
object(DateTime)[2772]
public 'date' => string '1905-05-05 00:00:00.000000' (length=26)
public 'timezone_type' => int 1
public 'timezone' => string '+00:00' (length=6)
请求详细信息:
"START TRANSACTION"
Parameters: { }
INSERT INTO catalogue_niveau (Niveau_ID, Catalogue_Code, Niveau_Nom, Parent_ID, Niveau_Ordre) VALUES (?, ?, ?, ?, ?)
Parameters: { 1: NIV_1, 2: CATEST, 3: genou, 4: null, 5: 0 }
INSERT INTO catalogue_rayon (Modele_Code, Catalogue_Code, Niveau_ID, Modele_Ordre, Date_Debut, Date_Fin) VALUES (?, ?, ?, ?, ?, ?)
Parameters: { 1: null, 2: null, 3: NIV_1, 4: 0, 5: '1905-05-05 00:00:00', 6: '1905-05-05 00:00:00' }
我不明白为什么模型值丢失了......请帮忙!
PS:如果我在树中创建只有级别,没有模型,则会将级别保存在数据库中。没错。所以,我的模型有问题。