0

I have two entities Store and Category and each Store has it's own categories. I'd like that when a Store owner's try to add a new category and category_parent, just the categories related to current Store will be displayed.

Right now, all categories are displayed in the select-option. I'm using Tree Gedmo extension to manage Category entity and I use getChildrenQueryBuilder method to select categories.

How can I modify this method and add my specific constraint ?

$store which is the constraint is declared in the controller action down.

I'd like te set current Category option disabled when he try to add a category_parent, so category and category parent must be differnt.

I hope it's clear

CategoryType.php

<?php

namespace Project\StoreBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;

class CategoryType extends AbstractType
{
    /**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        //..........
        //..........
        ->add('category', 'entity', array(
        'required' => false,
        'label' => 'Category parent',
        'class' => 'ProjectStoreBundle:Category',
        'attr' => array('class' => 'col-sm-8'),
        'empty_value' => 'Select one category',
        'property' => 'indentedName',
        'multiple' => false,
        'expanded' => false ,
        'query_builder' => function (\Project\StoreBundle\Entity\CategoryRepository $r)
            {
                return $r->getChildrenQueryBuilder(null, null, 'root', 'asc', false);
            }
        ))
    ;
}

/**
 * @param OptionsResolverInterface $resolver
 */
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Project\StoreBundle\Entity\Category'
    ));
}

/**
 * @return string
 */
public function getName()
{
    return 'project_storebundle_category';
}
}

Entity/Category.php

<?php

namespace Project\StoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;


use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;


/**
* Category
* @Gedmo\Tree(type="nested")
* @ORM\Table()
* @ORM\Entity(repositoryClass="Project\StoreBundle\Entity\CategoryRepository")
* @ORM\HasLifeCycleCallbacks()
*/
class Category
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=255)
 *
 *@Assert\NotBlank(message="Please enter the name of categorie.")
 */
private $name;

/**
 * @Gedmo\slug(fields={"name"}, unique_base="uniqueBase")
 * @ORM\Column(name="slug",length=255, unique=false)
 */
private $slug ;

/**
 * @ORM\Column(name="uniqueBase", type="integer")
 */
private $uniqueBase ;

/**
 * @ORM\Column(name="description", type="text", nullable=true)
 */
private $description;

/**
 * @ORM\Column(name="metaDescription", type="string", length=255, nullable=true)
 *
 * @Assert\Length(
 *     max=255,
 *     maxMessage="message"
 *  )
 */
private $metaDescription;

/**
 * @ORM\Column(name="metaKeywords", type="string", length=255, nullable=true)
 *
 * @Assert\Length(
 *     max=255,
 *     maxMessage="message"
 *  )
 */
private $metaKeywords;


/**
 * @ORM\Column(name="enabled", type="boolean", nullable=false)
 */
private $enabled;


/**
 * @Gedmo\TreeLeft
 * @ORM\Column(name="lft", type="integer")
 */
private $lft;

/**
 * @Gedmo\TreeLevel
 * @ORM\Column(name="lvl", type="integer")
 */
private $lvl;

/**
 * @Gedmo\TreeRight
 * @ORM\Column(name="rgt", type="integer")
 */
private $rgt;

/**
 * @Gedmo\TreeRoot
 * @ORM\Column(name="root", type="integer", nullable=true)
 */
private $root;

/**
 * @Gedmo\TreeParent
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
 * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
 */
private $parent;

/**
 * @ORM\OneToMany(targetEntity="Category", mappedBy="parent")
 * @ORM\OrderBy({"lft" = "ASC"})
 */
private $children;

/**
 *non mapped property 
 */
private $indentedName;

/**
 *non mapped property 
 */
private $category;

/**
 * @ORM\ManyToOne(targetEntity="Project\StoreBundle\Entity\Store", inversedBy="categories", cascade={"persist"})
 * @ORM\JoinColumn(nullable=false)
 */
private $store ;


/**
 * Constructor
 */
public function __construct()
{
    $this->children = new ArrayCollection();
}


/**
 * Get id
 *
 * @return integer 
 */
public function getId()
{
    return $this->id;
}

/**
 * Set name
 *
 * @param string $name
 * @return Category
 */
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 Category
 */
public function setSlug($slug)
{
    $this->slug = $slug;

    return $this;
}

/**
 * Get slug
 *
 * @return string 
 */
public function getSlug()
{
    return $this->slug;
}

/**
 * Set uniqueBase
 *
 * @param integer $uniqueBase
 * @return Category
 */
public function setUniqueBase($uniqueBase)
{
    $this->uniqueBase = $uniqueBase;

    return $this;
}

/**
 * Get uniqueBase
 *
 * @return integer 
 */
public function getUniqueBase()
{
    return $this->uniqueBase;
}

/**
 * Set description
 *
 * @param string $description
 * @return Category
 */
public function setDescription($description)
{
    $this->description = $description;

    return $this;
}

/**
 * Get description
 *
 * @return string 
 */
public function getDescription()
{
    return $this->description;
}

/**
 * Set metaDescription
 *
 * @param string $metaDescription
 * @return Category
 */
public function setMetaDescription($metaDescription)
{
    $this->metaDescription = $metaDescription;

    return $this;
}

/**
 * Get metaDescription
 *
 * @return string 
 */
public function getMetaDescription()
{
    return $this->metaDescription;
}

/**
 * Set metaKeywords
 *
 * @param string $metaKeywords
 * @return Category
 */
public function setMetaKeywords($metaKeywords)
{
    $this->metaKeywords = $metaKeywords;

    return $this;
}

/**
 * Get metaKeywords
 *
 * @return string 
 */
public function getMetaKeywords()
{
    return $this->metaKeywords;
}

/**
 * Set enabled
 *
 * @param boolean $enabled
 * @return Category
 */
public function setEnabled($enabled)
{
    $this->enabled = $enabled;

    return $this;
}

/**
 * Get enabled
 *
 * @return boolean 
 */
public function getEnabled()
{
    return $this->enabled;
}

/**
 * Set parent
 *
 * @param \Project\StoreBundle\Entity\Category $parent
 * @return Category
 */
public function setParent(\Project\StoreBundle\Entity\Category $parent = null)
{
    $this->parent = $parent;

    return $this;
}

/**
 * Get parent
 *
 * @return \Project\StoreBundle\Entity\Category 
 */
public function getParent()
{
    return $this->parent;
}

/**
 * Set lft
 *
 * @param integer $lft
 * @return Category
 */
public function setLft($lft)
{
    $this->lft = $lft;

    return $this;
}

/**
 * Get lft
 *
 * @return integer 
 */
public function getLft()
{
    return $this->lft;
}

/**
 * Set lvl
 *
 * @param integer $lvl
 * @return Category
 */
public function setLvl($lvl)
{
    $this->lvl = $lvl;

    return $this;
}

/**
 * Get lvl
 *
 * @return integer 
 */
public function getLvl()
{
    return $this->lvl;
}

/**
 * Set rgt
 *
 * @param integer $rgt
 * @return Category
 */
public function setRgt($rgt)
{
    $this->rgt = $rgt;

    return $this;
}

/**
 * Get rgt
 *
 * @return integer 
 */
public function getRgt()
{
    return $this->rgt;
}

/**
 * Set root
 *
 * @param integer $root
 * @return Category
 */
public function setRoot($root)
{
    $this->root = $root;

    return $this;
}

/**
 * Get root
 *
 * @return integer 
 */
public function getRoot()
{
    return $this->root;
}

/**
 * Add children
 *
 * @param \Project\StoreBundle\Entity\Category $children
 * @return Category
 */
public function addChild(\Project\StoreBundle\Entity\Category $children)
{
    $this->children[] = $children;

    return $this;
}

/**
 * Remove children
 *
 * @param \Project\StoreBundle\Entity\Category $children
 */
public function removeChild(\Project\StoreBundle\Entity\Category $children)
{
    $this->children->removeElement($children);
}

/**
 * Get children
 *
 * @return \Doctrine\Common\Collections\Collection 
 */
public function getChildren()
{
    return $this->children;
}

/**
 * Get IndentedName
 *
 */
public function getIndentedName()
{
    return str_repeat("-----", $this->lvl).$this->name;
}

/**
 * Get category
 *
 */
public function getCategory()
{
    return $this->category;
}

/**
 * Set store
 *
 * @param \Project\StoreBundle\Entity\Store $store
 * @return Category
 */
public function setStore(\Project\StoreBundle\Entity\Store $store = null)
{
    $this->store = $store;

    return $this;
}

/**
 * Get store
 *
 * @return \Project\StoreBundle\Entity\Store 
 */
public function getStore()
{
    return $this->store;
}

}

Controller

    /**
 * Create a new Category entity.
 *
 */
/**
* @ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function newAction(Store $store)
{
    // keep in mind, this will call all registered security voters
    if (false === $this->get('security.context')->isGranted('edit', $store)) {
        throw new AccessDeniedException('Unauthorised access!');
    }       

    $category = new Category();
    $category->setStore($store);
    $category->setUniqueBase($store->getId());

    $form = $this->createForm(new CategoryType(), $category);

    $request = $this->getRequest();

    if ($request->getMethod() == 'POST')
    {
        $form->bind($request);

        if ($form->isValid())
        {
            $em = $this->getDoctrine()->getManager();   
            $em->persist($category);
            $em->flush();

            $this->get('session')->getFlashBag()->add('message', 'Category enregistred');

            return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
        }
    }

    return $this->render('ProjectDashboardBundle:Category:new.html.twig',
    array(
        'form' => $form->createView() ,
        'store' =>$store,
        ));
}

I managed to pass the parameter $store to the form, but I don't know how to use it as a constraint in getChildrenQueryBuilder method. Should I create a new custom method? I prefer to use getChildrenQueryBuilder if it is possible.

Here is the new code

CategoryType.php

class CategoryType extends AbstractType
{

private $store;

public function __construct($store)
{
    $this->store = $store;
}
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $store = $this->store;

    $builder
        //...........
        //...........

        ->add('parent', 'entity', array(
        'required' => false,
        'label' => 'Category parent',
        'class' => 'ProjectStoreBundle:Category',
        'attr' => array('class' => 'col-sm-8'),
        'empty_value' => 'Select one category',
        'property' => 'indentedName',
        'multiple' => false,
        'expanded' => false ,
        'query_builder' => function (\Project\StoreBundle\Entity\CategoryRepository $r) use ($store)
            {
                return $r->getChildrenQueryBuilder(null, null, 'root', 'asc', false);
            }
        ))
    ;
}

/**
 * @param OptionsResolverInterface $resolver
 */
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Project\StoreBundle\Entity\Category'
    ));
}

/**
 * @return string
 */
public function getName()
{
    return 'project_storebundle_category';
}
}

Controller

/**
* @ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function newAction(Store $store)
{
    // keep in mind, this will call all registered security voters
    if (false === $this->get('security.context')->isGranted('edit', $store)) {
        throw new AccessDeniedException('Unauthorised access!');
    }       

    $category = new Category();
    $category->setStore($store);
    $category->setUniqueBase($store->getId());

    $form = $this->createForm(new CategoryType($store), $category);

    $request = $this->getRequest();

    if ($request->getMethod() == 'POST')
    {
        $form->bind($request);

        if ($form->isValid())
        {
            $em = $this->getDoctrine()->getManager();   
            $em->persist($category);
            $em->flush();

            $this->get('session')->getFlashBag()->add('message', 'Category  enregistred');

            return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
        }
    }

    return $this->render('ProjectDashboardBundle:Category:new.html.twig',
    array(
        'form' => $form->createView() ,
        'store' =>$store,
        ));
}
hous
  • 2,577
  • 2
  • 27
  • 66

1 Answers1

0

You're not saying which Symfony2 version you're using, but generically speaking, you just need to find a way to pass the current store to your form builder and use it in the method that filters the possible categories.

Take a look at this one, or this one, basically you just need to inject the $store into your form builder, and then you (almost) have it :-)

Community
  • 1
  • 1
mTorres
  • 3,590
  • 2
  • 25
  • 36
  • Thank you, I managed to pass the parameter $store to the form, but I don't know how to use it as a constraint in getChildrenQueryBuilder method. Should I create a new custom method? I prefer to use getChildrenQueryBuilder if it is possible. – hous Aug 04 '14 at 12:26
  • Now I would pass the ```$store``` (or $store ID, up to you) to your ```getChildrenQueryBuilder``` and then filter your SQL with this value on the same method (as you said this is your preferred method), be careful though, if you need this method elsewhere maybe you're not passing this new parameter so you have to handle it as an optional parameter - or create a new method :-) – mTorres Aug 04 '14 at 12:34
  • @ **mTorres** : hello, I have created a new method and I managed to pass it the new parameter **$store** and only the categories related to current store are selected but I have a problem with the arrangment of list categories. Can you help me please ? this is the link of the new question http://stackoverflow.com/questions/25144223/error-arrangement-of-option-list-using-nested-tree-extension-after-update – hous Aug 05 '14 at 20:34