Doctrine 2 @Gedmo\SoftDeleteable и уникальные поля
У меня проблема с @Gedmo\SoftDeleteable, когда у меня есть уникальные поля. Если я удаляю какую-то строку из таблицы и кожи, попробуйте поставить новую запись и под этим именем дать мне ошибку:
SQLSTATE [23000]: нарушение ограничения целостности: 1062 Повторяющаяся запись 'weqwewqe1' для ключа 'UNIQ_*************'
Как изменить это на сообщение формы проверки в правильном месте?
И должен прислать мне форму сообщения с уже существующей записью.
Моя сущность:
/**
* @ORM\Entity(repositoryClass = "Eteam\PageBundle\Entity\Repository\PageRepository")
* @ORM\Table(name = "page")
* @ORM\HasLifecycleCallbacks
* @Gedmo\SoftDeleteable(fieldName = "deletedDate", timeAware = false)
*
* @UniqueEntity(fields = {"name"})
* @UniqueEntity(fields = {"slug"})
*/
class Page
{
/**
* @ORM\Column(name = "id", type = "integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy = "AUTO")
*/
private $id;
/**
* @ORM\Column(name = "name", type = "string", length = 120, unique = true)
*
* @Assert\NotBlank
* @Assert\Length(
* min = 3,
* max = 120
* )
*/
private $name;
/**
* @ORM\Column(name = "slug", type = "string", length = 120, unique = true)
*
* @Assert\NotBlank
* @Assert\Length(
* min = 3,
* max = 120
* )
*/
private $slug;
/**
* @ORM\Column(name = "type", type = "string", length = 50)
*/
private $type;
/**
* @ORM\Column(name = "status", type = "string", length = 50)
*/
private $status;
/**
* @ORM\OneToMany(targetEntity = "PageContent", mappedBy = "pageId")
*/
private $content;
/**
* @ORM\Column(name = "parent", type = "string", length = 50, nullable = true)
*/
private $parent;
/**
* @ORM\ManyToOne(targetEntity = "PageTemplate", inversedBy = "page")
* @ORM\JoinColumn(name = "page_template_id", referencedColumnName = "id", onDelete = "SET NULL")
*/
private $pageTemplate;
/**
* @ORM\ManyToOne(targetEntity = "PageContentMap", inversedBy = "page")
* @ORM\JoinColumn(name = "page_content_map_id", referencedColumnName = "id", onDelete = "SET NULL")
*/
private $pageContentMapId;
/**
* @ORM\Column(name = "created_date", type = "datetime")
*/
private $createdDate;
/**
* @ORM\Column(name = "updated_date", type = "datetime", nullable = true)
*/
private $updatedDate;
/**
* @var \DateTime deletedDate
* @ORM\Column(name = "deleted_date", type = "datetime", nullable = true)
*/
private $deletedDate;
/**
* Constructor
*/
public function __construct()
{
$this->content = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Page
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set slug
*
* @param string $slug
*
* @return Page
*/
public function setSlug($slug)
{
$genSlug = new Slugify();
$this->slug = $genSlug->slugify($slug);
return $this;
}
/**
* Get slug
*
* @return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set type
*
* @param string $type
*
* @return Page
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Get type
*
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* Set status
*
* @param string $status
*
* @return Page
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* @return string
*/
public function getStatus()
{
return $this->status;
}
/**
* Set parent
*
* @param string $parent
*
* @return Page
*/
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* @return string
*/
public function getParent()
{
return $this->parent;
}
/**
* Set createdDate
*
* @param \DateTime $createdDate
*
* @return Page
*/
public function setCreatedDate($createdDate)
{
$this->createdDate = $createdDate;
return $this;
}
/**
* Get createdDate
*
* @return \DateTime
*/
public function getCreatedDate()
{
return $this->createdDate;
}
/**
* Set updatedDate
*
* @param \DateTime $updatedDate
*
* @return Page
*/
public function setUpdatedDate($updatedDate)
{
$this->updatedDate = $updatedDate;
return $this;
}
/**
* Get updatedDate
*
* @return \DateTime
*/
public function getUpdatedDate()
{
return $this->updatedDate;
}
/**
* Set deletedDate
*
* @param \DateTime $deletedDate
*
* @return Page
*/
public function setDeletedDate($deletedDate)
{
$this->deletedDate = $deletedDate;
return $this;
}
/**
* Get deletedDate
*
* @return \DateTime
*/
public function getDeletedDate()
{
return $this->deletedDate;
}
/**
* Add content
*
* @param \Eteam\PageBundle\Entity\PageContent $content
*
* @return Page
*/
public function addContent(\Eteam\PageBundle\Entity\PageContent $content)
{
$this->content[] = $content;
return $this;
}
/**
* Remove content
*
* @param \Eteam\PageBundle\Entity\PageContent $content
*/
public function removeContent(\Eteam\PageBundle\Entity\PageContent $content)
{
$this->content->removeElement($content);
}
/**
* Get content
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getContent()
{
return $this->content;
}
/**
* Set pageTemplate
*
* @param \Eteam\PageBundle\Entity\PageTemplate $pageTemplate
*
* @return Page
*/
public function setPageTemplate(\Eteam\PageBundle\Entity\PageTemplate $pageTemplate = null)
{
$this->pageTemplate = $pageTemplate;
return $this;
}
/**
* Get pageTemplate
*
* @return \Eteam\PageBundle\Entity\PageTemplate
*/
public function getPageTemplate()
{
return $this->pageTemplate;
}
/**
* Set pageContentMapId
*
* @param \Eteam\PageBundle\Entity\PageContentMap $pageContentMapId
*
* @return Page
*/
public function setPageContentMapId(\Eteam\PageBundle\Entity\PageContentMap $pageContentMapId = null)
{
$this->pageContentMapId = $pageContentMapId;
return $this;
}
/**
* Get pageContentMapId
*
* @return \Eteam\PageBundle\Entity\PageContentMap
*/
public function getPageContentMapId()
{
return $this->pageContentMapId;
}
/**
* @ORM\PrePersist
* @ORM\PreUpdate
*/
public function preSave()
{
if (null === $this->slug) {
$this->setSlug($this->getName());
}
if (null == $this->status) {
$this->setStatus('unpublish');
}
}
Стали я еще не исправил эту проблему. Мне нужно поймать это исключение и отправить в форму сообщение, это имя готово существовать. Пожалуйста помоги.
2 ответа
Вы знаете, что означает "мягкое удаление"? Это означает, что каждая операция удаления будет преобразована в SQL, который устанавливает только некоторые deleted
флаг для true
, И если вы вставите еще одну строку с тем же значением уникального поля, в котором была удалена мягкая строка, вы получите это сообщение.
У вас есть два способа решить эту проблему:
- Создайте свой уникальный индекс с двумя столбцами: исходное уникальное поле и
deleted
флаг. Тогда вы получите эту ошибку только при попытке добавить строку с существующими уникальными значениями полей только для несинхронно удаленных. - Избегайте этого нарушения: вы должны исключить возможность добавления строки, дублирующей другую, в уникальные поля.
Второй - лучший подход ИМХО.
Уникальные ограничения!
Вы должны использовать UniqueConstraints
не UniqueEntity
,
Например, для name
, создать UniqueConstraint
между name
а также deletedDate
,
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Entity(repositoryClass="Eteam\PageBundle\Entity\Repository\PageRepository")
* @ORM\Table(name="page",uniqueConstraints={@ORM\UniqueConstraint(name="name_unique", columns={"name", "deleted_date"})})
* @Gedmo\SoftDeleteable(fieldName="deletedDate", timeAware=false)
*/
class Page
{
//..
}
Хорошее решение Мика:
/** * @ORM\Table(name="page",uniqueConstraints={@ORM\UniqueConstraint(name="name_unique", columns={"name", "deleted_date"})}) */
вероятно, работает с postgresql, но не работает в некоторых версиях mysql ( поскольку NULL - неизвестное значение). Какsend to form message this name is ready exist.
? Например, мы можем изменить действие newAction и editAction в контроллере (когда CRUD создается в консоли):
public function newAction(Request $request)
{
$computer = new Computer();
$form = $this->createForm('AppBundle\Form\ComputerType', $computer);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$warning = $this->uniqueInventoryNr($computer->getId(),$computer->getInventoryNr()); // added
if ($warning == '') { // added
$em = $this->getDoctrine()->getManager();
$em->persist($computer);
$em->flush();
return $this->redirectToRoute('computer_index');
} else { // added
$form_error = new \Symfony\Component\Form\FormError($warning); // added
$form->addError($form_error); // added
} // added
}
return $this->render('computer/new.html.twig', array(
'computer' => $computer,
'form' => $form->createView(),
));
}
Изменить действие:
/**
* Displays a form to edit an existing computer entity.
*
* @Route("/{id}/edit", name="computer_edit")
* @Method({"GET", "POST"})
*/
public function editAction(Request $request, Computer $computer)
{
$deleteForm = $this->createDeleteForm($computer);
$editForm = $this->createForm('AppBundle\Form\ComputerType', $computer);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$warning = $this->uniqueInventoryNr($computer->getId(),$computer->getInventoryNr()); // added
if ( $warning == '') { // added
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('computer_edit', array('id' => $computer->getId()));
} else { // added
$form_error = new \Symfony\Component\Form\FormError($warning); // added
$editForm->addError($form_error); // added
} // added
}
return $this->render('computer/edit.html.twig', array(
'computer' => $computer,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
функция проверки:
private function uniqueInventoryNr($computerId,$inventoryNr)
{
$message = '';
$em = $this->getDoctrine()->getManager();
$computer_in = $em->getRepository('AppBundle\Entity\Computer')->findBy(array('inventoryNr' => $inventoryNr));
if (count($computer_in) > 0) {
if ($computer_in[0]->getId() != $computerId) // avoid when you edit
{
$message = 'Inventory number is duplicated. ';
}
}
return $message;
}