質問

I have an entity that can have another entity of the same type as parent (self-referencing?).

So I have a container named Sweden, then another Container named Stockholm, and the $parent property of Stockholm is Sweden (these two are the same type of entity).

I have a validation constraint that makes sure no two entities can have the same name, but here arises a problem when I choose a parent for an entity, because the way the validations work seem to be that it then goes on to check not only the name of the entity Stockholm, but also goes to check the entire entity that I chose for parent, and obviously the name Stockholm is already in the DB (otherwise I couldn't have picked it for parent) there is a validation error, seems like a catch22 issue...

some code to illustrate... Any thoughts?

The entity

namespace BizTV\ContainerManagementBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

use BizTV\ContainerManagementBundle\Validator\Constraints as BizTVAssert;

use BizTV\UserBundle\Entity\User as user;
use BizTV\ContainerManagementBundle\Entity\Container as Container;

/**
 * BizTV\ContainerManagementBundle\Entity\Container
 * @BizTVAssert\ContainerExists
 * @ORM\Table(name="container")
 * @ORM\Entity
 */
class Container
{

    /**
     * @var integer $id
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string $name
     * @Assert\NotBlank(message = "Du måste ange ett namn") 
     * @ORM\Column(name="name", type="string", length=255, nullable=true)
     */
    private $name;

    /**
    * @var object BizTV\BackendBundle\Entity\company
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
    * @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
    */
    protected $company; 

    /**
    * @var object BizTV\ContainerManagementBundle\Entity\ContainerType
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\ContainerManagementBundle\Entity\ContainerType")
    * @ORM\JoinColumn(name="container_type", referencedColumnName="id", nullable=false)
    */
    protected $containerType;

    /**
    * @var object BizTV\ContainerManagementBundle\Entity\ContainerSize
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\ContainerManagementBundle\Entity\ContainerSize")
    * @ORM\JoinColumn(name="container_size", referencedColumnName="id", nullable=false)
    */
    protected $containerSize;

    /**
     * @ORM\OneToMany(targetEntity="Container", mappedBy="parent")
     */
    private $children;

    /**
     * @ORM\OneToMany(targetEntity="BizTV\ContentManagementBundle\Entity\Content", mappedBy="container")
     * @ORM\OrderBy({"sortOrder" = "ASC"})
     * above code does nothing, thought to use that instead of the current jQuery tinySort but aparently not...
     */
    private $content;

    /**
    * @var object BizTV\ContainerManagementBundle\Entity\Container
    *  
    * @ORM\ManyToOne(targetEntity="Container", inversedBy="children")
    * @ORM\JoinColumn(name="parent", referencedColumnName="id", nullable=true)
    */
    protected $parent;

    /**
    * @var object BizTV\LayoutManagementBundle\Entity\Layout
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\LayoutManagementBundle\Entity\Layout")
    * @ORM\JoinColumn(name="screen_layout", referencedColumnName="id", nullable=true)
    */
    protected $screen_layout;   

    /**
     * @ORM\Column(type="boolean", nullable=true)
     * 
     * This only applies to the monitor containerType, others will always have false here. 
     * The purpose of this bool is the option of never rendering this monitor with a layout (handy for ex. BrfTV servers with lower resolution)
     */
    protected $prohibitLayout;


    /**
     * @ORM\ManyToMany(targetEntity="BizTV\UserBundle\Entity\User", mappedBy="access")
     */
    private $users;

    public function __construct() {
        $this->users = new \Doctrine\Common\Collections\ArrayCollection();
        $this->children = new \Doctrine\Common\Collections\ArrayCollection();
        $this->content = new \Doctrine\Common\Collections\ArrayCollection();
    }


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

    /**
     * Set name
     *
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

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

    /**
     * Set company
     *
     * @param BizTV\BackendBundle\Entity\company $company
     */
    public function setCompany(\BizTV\BackendBundle\Entity\company $company)
    {
        $this->company = $company;
    }

    /**
     * Get company
     *
     * @return BizTV\BackendBundle\Entity\company 
     */
    public function getCompany()
    {
        return $this->company;
    }

    /**
     * Set containerType
     *
     * @param BizTV\ContainerManagementBundle\Entity\ContainerType $containerType
     */
    public function setContainerType(\BizTV\ContainerManagementBundle\Entity\ContainerType $containerType)
    {
        $this->containerType = $containerType;
    }

    /**
     * Get containerType
     *
     * @return BizTV\ContainerManagementBundle\Entity\ContainerType 
     */
    public function getContainerType()
    {
        return $this->containerType;
    }

    /**
     * Set parent
     *
     * @param BizTV\ContainerManagementBundle\Entity\Container $parent
     */
    public function setParent(\BizTV\ContainerManagementBundle\Entity\Container $parent = NULL)
    {
        $this->parent = $parent;
    }

    /**
     * Get parent
     *
     * @return BizTV\ContainerManagementBundle\Entity\Container 
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Set screen_layout
     *
     * @param BizTV\LayoutManagementBundle\Entity\Layout $screenLayout
     */
    public function setScreenLayout(\BizTV\LayoutManagementBundle\Entity\Layout $screenLayout = NULL)
    {
        $this->screen_layout = $screenLayout;
    }

    /**
     * Get screen_layout
     *
     * @return BizTV\LayoutManagementBundle\Entity\Layout 
     */
    public function getScreenLayout()
    {
        return $this->screen_layout;
    }

    public function getSelectLabel()
    {
        if (isset($this->parent)) {
            return $this->name . ' (' . $this->parent->getName() . ')';
        }
        else {
            return $this->name;
        }
    }

    /**
     * Add users
     *
     * @param BizTV\UserBundle\Entity\User $users
     */
    public function addUser(\BizTV\UserBundle\Entity\User $users)
    {
        $this->users[] = $users;
    }

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

    /**
     * Add children
     *
     * @param BizTV\ContainerManagementBundle\Entity\Container $children
     */
    public function addContainer(\BizTV\ContainerManagementBundle\Entity\Container $children)
    {
        $this->children[] = $children;
    }

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





    /**
     * Add content
     *
     * @param BizTV\ContentManagementBundle\Entity\Content $content
     */
    public function addContent(\BizTV\ContentManagementBundle\Entity\Content $content)
    {
        $this->content[] = $content;
    }

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

    /**
     * Set containerSize
     *
     * @param BizTV\ContainerManagementBundle\Entity\ContainerSize $containerSize
     */
    public function setContainerSize(\BizTV\ContainerManagementBundle\Entity\ContainerSize $containerSize)
    {
        $this->containerSize = $containerSize;
    }

    /**
     * Get containerSize
     *
     * @return BizTV\ContainerManagementBundle\Entity\ContainerSize 
     */
    public function getContainerSize()
    {
        return $this->containerSize;
    }    


    /**
     * Set prohibitLayout
     *
     * @param boolean $prohibitLayout
     */
    public function setProhibitLayout($prohibitLayout)
    {
        $this->prohibitLayout = $prohibitLayout;
    }

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

Form

namespace BizTV\ContainerManagementBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

use Doctrine\ORM\EntityRepository;


class ContainerBuildingType extends AbstractType
{

    function __construct(\BizTV\BackendBundle\Entity\company $company, \BizTV\ContainerManagementBundle\Entity\ContainerType $parentType) {
        $this->parentType = $parentType;    
        $this->company = $company;
    }


    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('name', 'text', array('label' => 'Namn på fastighet '));

        $parentType = $this->parentType;
        $company = $this->company;

        $builder->add('parent', 'entity', array(
        'label' => 'Välj ett geografiskt område för fastigheten ',
        'class' => 'BizTVContainerManagementBundle:Container','property'=>'name',
        'query_builder' => function(EntityRepository $er) use ($parentType, $company) {
            return $er->createQueryBuilder('u')
                ->where('u.containerType = :type', 'u.company = :company')
                ->setParameters( array('type' => $parentType, 'company' => $company) )
                ->orderBy('u.name', 'ASC');
            },
        ));

    }

    public function getName()
    {
        return 'biztv_containermanagementbundle_containerbuildingtype';
    }
}

Constraint

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class ContainerExists extends Constraint
{
    public $message = 'Namnet är upptaget, vänligen välj ett annat.';

    public function validatedBy()
    {
        return 'containerExists';
    }

    public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }

}

Validator

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityManager as EntityManager;

class ContainerExistsValidator extends ConstraintValidator
{

    private $container;
    private $em;

    public function __construct(Container $container, EntityManager $em) {
        $this->container = $container;
        $this->em = $em;
    }   

    public function isValid($object, Constraint $constraint)
    {

        $em = $this->em;
        $container = $this->container;

        $company = $this->container->get('security.context')->getToken()->getUser()->getCompany();

        $parent = $object->getParent();

        //Fetch entities with same name in the same container
        $repository = $em->getRepository('BizTVContainerManagementBundle:Container');
        $query = $repository->createQueryBuilder('c')
            ->where('c.company = :company and c.parent = :parent')
            ->setParameters(array('company' => $company, 'parent' => $parent))
            ->orderBy('c.name', 'ASC')
            ->getQuery();
        $containers = $query->getResult();      

        foreach ($containers as $g) {

            echo "testing ".$g->getName()." against ".$object->getName()."<br/>";

            if ( $g->getName() == $object->getName() ) {    

                $this->setMessage('Namnet '.$object->getName().' är upptaget, vänligen välj ett annat');
                return false;
            }
        }

        return true;
    }
}
役に立ちましたか?

解決 2

The answer to a similar question solved this issue as well:

symfony2 validation of child entity prevents editing of parent entity

他のヒント

I believe you have your relationships backwards;

Your parent should be OneToMany and your child should be ManyToOne

Unless you intended to have many parents only have 1 child and that child belong to many parents.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top