Unverified Commit c6bb438e authored by Sebastian Castro's avatar Sebastian Castro
Browse files

Prevent circular reference between categories and options

parent 21b7df6d
...@@ -41,14 +41,20 @@ class CategoryAdmin extends AbstractAdmin ...@@ -41,14 +41,20 @@ class CategoryAdmin extends AbstractAdmin
protected function configureFormFields(FormMapper $formMapper) 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')) ->with('Paramètres principaux', array('class' => 'col-xs-12 col-md-6'))
->add('name', null, array('required' => true, 'label' => 'Nom du groupe')) ->add('name', null, array('required' => true, 'label' => 'Nom du groupe'))
->add('pickingOptionText', null, array('required' => true, 'label' => 'Texte à afficher dans le formulaire : Choisissez ....')) ->add('pickingOptionText', null, array('required' => true, 'label' => 'Texte à afficher dans le formulaire : Choisissez ....'))
->add('parent', 'sonata_type_model', array( ->add('parent', 'sonata_type_model', array(
'class'=> 'Biopen\GeoDirectoryBundle\Document\Option', 'class'=> 'Biopen\GeoDirectoryBundle\Document\Option',
'required' => false, 'required' => false,
'choices_as_values' => true, 'query' => $parentQuery,
'choices_as_values' => true,
'label' => 'Catégorie parente'), array('admin_code' => 'admin.option_hidden')) '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('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"])) ->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; ...@@ -21,7 +21,7 @@ use Knp\Menu\ItemInterface;
class OptionAdmin extends AbstractAdmin class OptionAdmin extends AbstractAdmin
{ {
protected $baseRouteName = 'admin_biopen_geodirectory_option'; protected $baseRouteName = 'admin_biopen_geodirectory_option';
protected $baseRoutePattern = 'admin_biopen_geodirectory_option'; protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
public function createQuery($context = 'list') public function createQuery($context = 'list')
{ {
...@@ -31,7 +31,7 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option'; ...@@ -31,7 +31,7 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
public function getTemplate($name) public function getTemplate($name)
{ {
switch ($name) { switch ($name) {
case 'edit': return '@BiopenAdmin/edit/edit_option_category.html.twig'; case 'edit': return '@BiopenAdmin/edit/edit_option_category.html.twig';
break; break;
default : return parent::getTemplate($name); default : return parent::getTemplate($name);
...@@ -40,8 +40,13 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option'; ...@@ -40,8 +40,13 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
} }
protected function configureFormFields(FormMapper $formMapper) 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') ->tab('Principal')
->with('Paramètres principaux', array('class' => 'col-xs-12 col-md-6')) ->with('Paramètres principaux', array('class' => 'col-xs-12 col-md-6'))
->add('name', null, array('required' => true, 'label' => 'Nom')) ->add('name', null, array('required' => true, 'label' => 'Nom'))
...@@ -50,6 +55,7 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option'; ...@@ -50,6 +55,7 @@ protected $baseRoutePattern = 'admin_biopen_geodirectory_option';
->add('parent', 'sonata_type_model', array( ->add('parent', 'sonata_type_model', array(
'class'=> 'Biopen\GeoDirectoryBundle\Document\Category', 'class'=> 'Biopen\GeoDirectoryBundle\Document\Category',
'required' => true, 'required' => true,
'query' => $parentQuery,
'choices_as_values' => true, 'choices_as_values' => true,
'label' => 'Groupe de Catégorie parent', 'label' => 'Groupe de Catégorie parent',
'mapped' => true), array('admin_code' => 'admin.categories.lite_hidden')) 'mapped' => true), array('admin_code' => 'admin.categories.lite_hidden'))
......
...@@ -8,7 +8,7 @@ use JMS\Serializer\Annotation\Accessor; ...@@ -8,7 +8,7 @@ use JMS\Serializer\Annotation\Accessor;
/** /**
* Category * Category
* @MongoDB\HasLifecycleCallbacks * @MongoDB\HasLifecycleCallbacks
* @MongoDB\Document(repositoryClass="Biopen\GeoDirectoryBundle\Repository\CategoryRepository") * @MongoDB\Document(repositoryClass="Biopen\GeoDirectoryBundle\Repository\CategoryRepository")
*/ */
class Category class Category
...@@ -16,7 +16,7 @@ class Category ...@@ -16,7 +16,7 @@ class Category
/** /**
* @var int * @var int
* @Exclude * @Exclude
* @MongoDB\Id(strategy="INCREMENT") * @MongoDB\Id(strategy="INCREMENT")
*/ */
private $id; private $id;
...@@ -32,7 +32,7 @@ class Category ...@@ -32,7 +32,7 @@ class Category
* @Exclude(if="object.getNameShort() == object.getName()") * @Exclude(if="object.getNameShort() == object.getName()")
* @MongoDB\Field(type="string") * @MongoDB\Field(type="string")
*/ */
private $nameShort; private $nameShort;
/** /**
* @MongoDB\ReferenceOne(targetDocument="Biopen\GeoDirectoryBundle\Document\Option", inversedBy="subcategories") * @MongoDB\ReferenceOne(targetDocument="Biopen\GeoDirectoryBundle\Document\Option", inversedBy="subcategories")
...@@ -150,7 +150,7 @@ class Category ...@@ -150,7 +150,7 @@ class Category
* @Exclude(if="object.getOptionsCount() == 0") * @Exclude(if="object.getOptionsCount() == 0")
* @MongoDB\ReferenceMany(targetDocument="Biopen\GeoDirectoryBundle\Document\Option", mappedBy="parent", cascade={"persist", "remove"}, sort={"index"="ASC"}) * @MongoDB\ReferenceMany(targetDocument="Biopen\GeoDirectoryBundle\Document\Option", mappedBy="parent", cascade={"persist", "remove"}, sort={"index"="ASC"})
*/ */
private $options; private $options;
public function __construct() public function __construct()
...@@ -158,7 +158,7 @@ class Category ...@@ -158,7 +158,7 @@ class Category
$this->options = new \Doctrine\Common\Collections\ArrayCollection(); $this->options = new \Doctrine\Common\Collections\ArrayCollection();
} }
public function __toString() public function __toString()
{ {
$parentName = $this->getParent() ? $this->getParent()->getName() . '/' : ''; $parentName = $this->getParent() ? $this->getParent()->getName() . '/' : '';
return "(Groupe) " . $parentName . $this->getName(); return "(Groupe) " . $parentName . $this->getName();
...@@ -183,7 +183,24 @@ class Category ...@@ -183,7 +183,24 @@ class Category
usort( $sortedOptions , function ($a, $b) { return $a->getIndex() - $b->getIndex(); }); usort( $sortedOptions , function ($a, $b) { return $a->getIndex() - $b->getIndex(); });
return $sortedOptions; 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 * Get id
* *
...@@ -334,7 +351,7 @@ class Category ...@@ -334,7 +351,7 @@ class Category
{ {
return $this->enableDescription; return $this->enableDescription;
} }
/** /**
* Set pickingOptionText * Set pickingOptionText
* *
...@@ -409,11 +426,15 @@ class Category ...@@ -409,11 +426,15 @@ class Category
*/ */
public function setParent($parent, $updateParent = true) public function setParent($parent, $updateParent = true)
{ {
// clearing old parent if ($parent && in_array($parent->getId(), $this->getAllOptionsIds())) {
if ($updateParent && $this->parent) $this->parent->removeSubcategory($this, false); // Circular reference
} else {
$this->parent = $parent; // clearing old parent
if ($updateParent && $parent) $parent->addSubcategory($this, false); if ($updateParent && $this->parent) $this->parent->removeSubcategory($this, false);
$this->parent = $parent;
if ($updateParent && $parent) $parent->addSubcategory($this, false);
}
return $this; return $this;
} }
......
...@@ -17,9 +17,9 @@ class Option ...@@ -17,9 +17,9 @@ class Option
{ {
/** /**
* @var int * @var int
* @Accessor(getter="getStringId",setter="setId") * @Accessor(getter="getStringId",setter="setId")
* @Groups({"semantic"}) * @Groups({"semantic"})
* @MongoDB\Id(strategy="INCREMENT") * @MongoDB\Id(strategy="INCREMENT")
*/ */
private $id; private $id;
...@@ -31,7 +31,7 @@ class Option ...@@ -31,7 +31,7 @@ class Option
private $customId; private $customId;
/** /**
* @var string * @var string
* @Groups({"semantic"}) * @Groups({"semantic"})
* @MongoDB\Field(type="string") * @MongoDB\Field(type="string")
*/ */
...@@ -44,7 +44,7 @@ class Option ...@@ -44,7 +44,7 @@ class Option
* @MongoDB\Field(type="string") * @MongoDB\Field(type="string")
*/ */
private $nameShort; private $nameShort;
/** /**
* @Accessor(getter="getParentOptionId") * @Accessor(getter="getParentOptionId")
* @Groups({"semantic"}) * @Groups({"semantic"})
...@@ -56,7 +56,7 @@ class Option ...@@ -56,7 +56,7 @@ class Option
/** /**
* @var int * @var int
* @Exclude * @Exclude
* @MongoDB\Field(type="int") * @MongoDB\Field(type="int")
*/ */
private $index; private $index;
...@@ -187,11 +187,18 @@ class Option ...@@ -187,11 +187,18 @@ class Option
$this->subcategories = new \Doctrine\Common\Collections\ArrayCollection(); $this->subcategories = new \Doctrine\Common\Collections\ArrayCollection();
} }
public function __toString() public function __toString()
{ {
return $this->getName(); return $this->getName();
} }
public function allSubcategories()
{
$result = [];
return $result;
}
public function getNameWithParent() public function getNameWithParent()
{ {
$result = ''; $result = '';
...@@ -221,7 +228,7 @@ class Option ...@@ -221,7 +228,7 @@ class Option
{ {
$result = []; $result = [];
$parentOption = $option->getParentOption(); $parentOption = $option->getParentOption();
if ($parentOption) if ($parentOption)
{ {
$result = $this->recursivelyAddParentOptionId($parentOption); $result = $this->recursivelyAddParentOptionId($parentOption);
} }
...@@ -240,7 +247,24 @@ class Option ...@@ -240,7 +247,24 @@ class Option
foreach ($option->getSubcategories() as $categorie) { foreach ($option->getSubcategories() as $categorie) {
foreach ($categorie->getOptions() as $childOption) { foreach ($categorie->getOptions() as $childOption) {
$result = array_merge($result, $this->recursivelyAddChilrenOptionIds($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; return $result;
} }
...@@ -257,7 +281,7 @@ class Option ...@@ -257,7 +281,7 @@ class Option
usort( $sortedCategories , function ($a, $b) { return $a->getIndex() - $b->getIndex(); }); usort( $sortedCategories , function ($a, $b) { return $a->getIndex() - $b->getIndex(); });
return $sortedCategories; return $sortedCategories;
} }
/** /**
* Get id * Get id
* *
...@@ -278,9 +302,9 @@ class Option ...@@ -278,9 +302,9 @@ class Option
return $this->customId ?: strval($this->id); return $this->customId ?: strval($this->id);
} }
public function setId() public function setId()
{ {
return $this; return $this;
} }
/** /**
...@@ -567,7 +591,12 @@ class Option ...@@ -567,7 +591,12 @@ class Option
*/ */
public function setParent(\Biopen\GeoDirectoryBundle\Document\Category $parent) 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; 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