0

I'm using the Gedmo Doctrine Extensions to handle Categories as a nested set. I'm building a REST API and I have a route to create a Category. I want to be able to create root Categories or child Categories. Here is the entity

<?php
namespace AppBundle\Entity;

use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;

/**
 * @Gedmo\Tree(type="nested")
 * @ORM\Table(name="bo_categories")
 * use repository for handy tree functions
 * @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
 */
class Category
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", length=64)
     */
    private $name;

    /**
     * @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;

    public function getId()
    {
        return $this->id;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setParent(Category $parent = null)
    {
        $this->parent = $parent;
    }

    public function getParent()
    {
        return $this->parent;
    }
}

Here is the form

<?php

namespace AppBundle\Form\Type\Classification;

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

class CategoryFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name')
                ->add('parent', 'entity', array(
                        'class'    => 'AppBundle:Category',
                        'property' => 'id',
                    ))
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'      => 'AppBundle\Entity\Category',
            'csrf_protection' => false,
        ));
    }

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

And here is the controller

/**
     * @Route("/create")
     * @Security("has_role('ROLE_SUPER_ADMIN')")
     * @Rest\View
     */
    public function postCreateAction(Request $request)
    {
        $categoryManager = $this->get('app.manager.category');

        $category = $categoryManager->createNew();

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

        // $category->setParent(10);

        $form->handleRequest($request);

        if ($form->isValid()) {

            $categoryManager->save($category);

            return new Response('', 201);
        } else {
            return $this->view($form, 400);
        }

    }

If I want to create a child category, it works fine. But if I want to create a root category without removing the "parent" field in the form I get this error

An exception occurred while executing 'SELECT b0_.id AS id0, b0_.name AS name1, b0_.lft AS lft2, b0_.lvl AS lvl3, b0_.rgt AS rgt4, b0_.root AS root5, b0_.parent_id AS parent_id6 FROM bo_categories b0_ WHERE b0_.id IN (?)' with params [""]:\n\nSQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "", "class": "Doctrine\DBAL\DBALException

Why do I get this error ? Can't the "parent" value be empty/null in the form ?

Hakim
  • 1,084
  • 11
  • 28

1 Answers1

0

There is absolutely no problem parent being NULL. Your current mapping on the other hand, doesn't allow that.

First, you should modify your mapping for $parent

@ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")

should allow null values:

@ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE", nullable=true)

Don't forget to run app/console doctrine:schema:update --force when you change your mappings!

Next, your form looks okay, but it miss one property - empty_value. This won't force you to choose parent category.

->add('parent', 'entity', array(
      'class'    => 'AppBundle:Category',
      'property' => 'id',
))

should be like this:

->add('parent', 'entity', array(
      'class'    => 'AppBundle:Category',
      'property' => 'id',
      'empty_value' => '-- Select parent --'
))

This should add extra option with no value and should be selected by default (when creating new category). Since your field is of type entity you will see all of your categories as well (when you need to select parent).

This should do the trick, give it a try.

Artamiel
  • 3,652
  • 2
  • 19
  • 24
  • I added the `nullable=true` in the mapping but that didn't change anything. I still get the same error. `schema:update` told me that there was no change in the schema (which seems logical since a many to one can be null by default, since it's a collection). And `'empty_value' => '-- Select parent --'` only applies for HTML form, and I'm building an API, so my calls are made with POSTMAN. Thanks for your help anyway :) – Hakim Jul 03 '15 at 08:02
  • I missed the API part. On a side note M:1 represents a foreign key in your table,not a collection. If you're positive that `parent` accept NULL you should be able to create root category. I will look into it later because I am on my phone. – Artamiel Jul 03 '15 at 09:21