Prevent circular reference between categories and options

parent 21b7df6d
......@@ -41,14 +41,20 @@ class CategoryAdmin extends AbstractAdmin
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
// prevent circular reference, i.e setting a child as parent
$repo = $this->getConfigurationPool()->getContainer()->get('doctrine_mongodb')->getRepository('BiopenGeoDirectoryBundle:Option');
$parentQuery = $repo->createQueryBuilder()
->field('id')->notIn($this->subject->getAllOptionsIds());
$formMapper
->with('Paramètres principaux', array('class' => 'col-xs-12 col-md-6'))
->add('name', null, array('required' => true, 'label' => 'Nom du groupe'))
->add('pickingOptionText', null, array('required' => true, 'label' => 'Texte à afficher dans le formulaire : Choisissez ....'))
->add('parent', 'sonata_type_model', array(
'class'=> 'Biopen\GeoDirectoryBundle\Document\Option',
'required' => false,
'choices_as_values' => true,
'query' => $parentQuery,
'choices_as_values' => true,
'label' => 'Catégorie parente'), array('admin_code' => 'admin.option_hidden'))
->add('isMandatory', null, array('required' => false, 'label' => "Choix obligatoire", 'label_attr' => ['title'=>"Une catégorie de ce groupe doit être obligatoirement selectionnée"]))
->add('singleOption', null, array('required' => false, 'label' => 'Choix unique', 'label_attr' => ['title'=>"Une seule catégorie est selectionnable à la fois"]))
......
......@@ -21,7 +21,7 @@ use Knp\Menu\ItemInterface;
class OptionAdmin extends AbstractAdmin
{
protected $baseRouteName = 'admin_biopen_geodirectory_option';
protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
public function createQuery($context = 'list')
{
......@@ -31,7 +31,7 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
public function getTemplate($name)
{
switch ($name) {
switch ($name) {
case 'edit': return '@BiopenAdmin/edit/edit_option_category.html.twig';
break;
default : return parent::getTemplate($name);
......@@ -40,8 +40,13 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
}
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
{
// prevent circular reference, i.e setting a child as parent
$repo = $this->getConfigurationPool()->getContainer()->get('doctrine_mongodb')->getRepository('BiopenGeoDirectoryBundle:Category');
$parentQuery = $repo->createQueryBuilder()
->field('id')->notIn($this->subject->getAllSubcategoriesIds());
$formMapper
->tab('Principal')
->with('Paramètres principaux', array('class' => 'col-xs-12 col-md-6'))
->add('name', null, array('required' => true, 'label' => 'Nom'))
......@@ -50,6 +55,7 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
->add('parent', 'sonata_type_model', array(
'class'=> 'Biopen\GeoDirectoryBundle\Document\Category',
'required' => true,
'query' => $parentQuery,
'choices_as_values' => true,
'label' => 'Groupe de Catégorie parent',
'mapped' => true), array('admin_code' => 'admin.categories.lite_hidden'))
......
......@@ -8,7 +8,7 @@ use JMS\Serializer\Annotation\Accessor;
/**
* Category
* @MongoDB\HasLifecycleCallbacks
* @MongoDB\HasLifecycleCallbacks
* @MongoDB\Document(repositoryClass="Biopen\GeoDirectoryBundle\Repository\CategoryRepository")
*/
class Category
......@@ -16,7 +16,7 @@ class Category
/**
* @var int
* @Exclude
* @MongoDB\Id(strategy="INCREMENT")
* @MongoDB\Id(strategy="INCREMENT")
*/
private $id;
......@@ -32,7 +32,7 @@ class Category
* @Exclude(if="object.getNameShort() == object.getName()")
* @MongoDB\Field(type="string")
*/
private $nameShort;
private $nameShort;
/**
* @MongoDB\ReferenceOne(targetDocument="Biopen\GeoDirectoryBundle\Document\Option", inversedBy="subcategories")
......@@ -150,7 +150,7 @@ class Category
* @Exclude(if="object.getOptionsCount() == 0")
* @MongoDB\ReferenceMany(targetDocument="Biopen\GeoDirectoryBundle\Document\Option", mappedBy="parent", cascade={"persist", "remove"}, sort={"index"="ASC"})
*/
private $options;
private $options;
public function __construct()
......@@ -158,7 +158,7 @@ class Category
$this->options = new \Doctrine\Common\Collections\ArrayCollection();
}
public function __toString()
public function __toString()
{
$parentName = $this->getParent() ? $this->getParent()->getName() . '/' : '';
return "(Groupe) " . $parentName . $this->getName();
......@@ -183,7 +183,24 @@ class Category
usort( $sortedOptions , function ($a, $b) { return $a->getIndex() - $b->getIndex(); });
return $sortedOptions;
}
public function getAllOptionsIds()
{
return $this->recursivelyGetOptionsIds($this);
}
private function recursivelyGetOptionsIds($category)
{
$result = [];
foreach ($category->getOptions() as $option) {
$result[] = $option->getId();
foreach ($option->getSubcategories() as $childCategory) {
$result = array_merge($result, $this->recursivelyGetOptionsIds($childCategory));
}
}
return $result;
}
/**
* Get id
*
......@@ -334,7 +351,7 @@ class Category
{
return $this->enableDescription;
}
/**
* Set pickingOptionText
*
......@@ -409,11 +426,15 @@ class Category
*/
public function setParent($parent, $updateParent = true)
{
// clearing old parent
if ($updateParent && $this->parent) $this->parent->removeSubcategory($this, false);
$this->parent = $parent;
if ($updateParent && $parent) $parent->addSubcategory($this, false);
if ($parent && in_array($parent->getId(), $this->getAllOptionsIds())) {
// Circular reference
} else {
// clearing old parent
if ($updateParent && $this->parent) $this->parent->removeSubcategory($this, false);
$this->parent = $parent;
if ($updateParent && $parent) $parent->addSubcategory($this, false);
}
return $this;
}
......
......@@ -17,9 +17,9 @@ class Option
{
/**
* @var int
* @Accessor(getter="getStringId",setter="setId")
* @Accessor(getter="getStringId",setter="setId")
* @Groups({"semantic"})
* @MongoDB\Id(strategy="INCREMENT")
* @MongoDB\Id(strategy="INCREMENT")
*/
private $id;
......@@ -31,7 +31,7 @@ class Option
private $customId;
/**
* @var string
* @var string
* @Groups({"semantic"})
* @MongoDB\Field(type="string")
*/
......@@ -44,7 +44,7 @@ class Option
* @MongoDB\Field(type="string")
*/
private $nameShort;
/**
* @Accessor(getter="getParentOptionId")
* @Groups({"semantic"})
......@@ -56,7 +56,7 @@ class Option
/**
* @var int
* @Exclude
* @MongoDB\Field(type="int")
* @MongoDB\Field(type="int")
*/
private $index;
......@@ -187,11 +187,18 @@ class Option
$this->subcategories = new \Doctrine\Common\Collections\ArrayCollection();
}
public function __toString()
public function __toString()
{
return $this->getName();
}
public function allSubcategories()
{
$result = [];
return $result;
}
public function getNameWithParent()
{
$result = '';
......@@ -221,7 +228,7 @@ class Option
{
$result = [];
$parentOption = $option->getParentOption();
if ($parentOption)
if ($parentOption)
{
$result = $this->recursivelyAddParentOptionId($parentOption);
}
......@@ -240,7 +247,24 @@ class Option
foreach ($option->getSubcategories() as $categorie) {
foreach ($categorie->getOptions() as $childOption) {
$result = array_merge($result, $this->recursivelyAddChilrenOptionIds($childOption));
}
}
}
return $result;
}
public function getAllSubcategoriesIds()
{
return $this->recursivelyGetSubcategoriesIds($this);
}
private function recursivelyGetSubcategoriesIds($option)
{
$result = [];
foreach ($option->getSubcategories() as $categorie) {
$result[] = $categorie->getId();
foreach ($categorie->getOptions() as $childOption) {
$result = array_merge($result, $this->recursivelyGetSubcategoriesIds($childOption));
}
}
return $result;
}
......@@ -257,7 +281,7 @@ class Option
usort( $sortedCategories , function ($a, $b) { return $a->getIndex() - $b->getIndex(); });
return $sortedCategories;
}
/**
* Get id
*
......@@ -278,9 +302,9 @@ class Option
return $this->customId ?: strval($this->id);
}
public function setId()
{
return $this;
public function setId()
{
return $this;
}
/**
......@@ -567,7 +591,12 @@ class Option
*/
public function setParent(\Biopen\GeoDirectoryBundle\Document\Category $parent)
{
$this->parent = $parent;
if ($parent && in_array($parent->getId(), $this->getAllSubcategoriesIds())) {
// Circular reference
} else {
$this->parent = $parent;
}
return $this;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment