0

I am working on a legacy application and I now need to change an existing table (called categories) so that it has a parent_id field which is a foreign key to the primary key of it’s own table (the categories table)

The issue I am having is that I cannot get Doctrine to set the parent field in the database

I have the following entity orm:

ModelBundle\Entity\Category:
    type: entity
    table: category
    indexes:
        fki_parent_to_id:
            columns:
                - parent_id
    uniqueConstraints:
        uniq_64c19c1989d9b62:
            columns:
                - slug
        uniq_name:
            columns:
                - name
    id:
        id:
            type: integer
            nullable: false
            options:
                unsigned: false
            id: true
            generator:
                strategy: SEQUENCE
    fields:
        name:
            type: string
            nullable: false
            length: 255
            options:
                fixed: false
        slug:
            type: string
            nullable: false
            length: 255
            options:
                fixed: false
        description:
            type: string
            nullable: true
            length: 255
            options:
                fixed: false
        isActive:
            type: boolean
            nullable: false
            options:
                default: false
            column: is_active
        displayOrder:
            type: integer
            nullable: true
            options:
                unsigned: false
            column: display_order
    oneToOne:
        parent:
            targetEntity: Category
            joinColumns:
                parent_id:
                    referencedColumnName: id

    lifecycleCallbacks: {  }

Here is the entity class:

<?php

namespace ModelBundle\Entity;

use Doctrine\Common\Collections\Collection;
use JMS\Serializer\Annotation\Expose;

use Symfony\Cmf\Bundle\SeoBundle\Extractor\TitleReadInterface;
use Symfony\Cmf\Bundle\SeoBundle\Extractor\DescriptionReadInterface;
use Symfony\Cmf\Bundle\SeoBundle\Extractor\ExtrasReadInterface;

class Category implements ExtrasReadInterface, TitleReadInterface, DescriptionReadInterface
{
    /** @var int */
    private $id;

    /** @var string */
    private $slug;

    /**
     * @Expose
     *
     * @var string
     */
    private $name;

    /** @var bool */
    protected $isActive;

    /** @var string */
    private $description;

    /** @var Collection */
    private $products;

    /** @var int */
    private $displayOrder;

    /**
     * @var Category
     */
    private $parent;


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

    public function setName(string $name): Category
    {
        $this->name = $name;
        return $this;
    }

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

    public function setIsActive(bool $isActive): Category
    {
        $this->isActive = $isActive;
        return $this;
    }

    public function getIsActive(): bool
    {
        return $this->isActive;
    }

    public function setDescription(string $description): Category
    {
        $this->description = $description;
        return $this;
    }

    public function getDescription(): string
    {
        return $this->description ?: '';
    }

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

    public function addProduct(Product $products): Category
    {
        $this->products[] = $products;
        return $this;
    }

    public function removeProduct(Product $products)
    {
        $this->products->removeElement($products);
    }

    public function getProducts(): Collection
    {
        return $this->products;
    }

    public function setSlug(string $slug): Category
    {
        $this->slug = $slug;
        return $this;
    }

    public function getSlug(): string
    {
        return $this->slug ?: '';
    }

    public function getSeoTitle(): string
    {
        return $this->getName();
    }

    public function getSeoDescription(): string
    {
        return $this->getDescription();
    }

    public function getSeoExtras(): array
    {
        return [
            'property' => [
                'og:title'       => $this->name,
                'og:description' => $this->description,
                'og:type'        => 'product:category'
            ],
        ];
    }

    function getDisplayOrder(): ?int
    {
        return $this->displayOrder;
    }

    public function setDisplayOrder($displayOrder): Category
    {
        $this->displayOrder = $displayOrder;
        return $this;
    }

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

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

}

This maps out what I have said above. This is similar to what is here in the docs: https://www.doctrine-project.org/projects/doctrine-orm/en/2.5/reference/association-mapping.html#one-to-one-self-referencing There is no yaml in the examples but I got this yaml from doctrine (using the generation commands).

No matter what I do I can never save what is set in the parent.

Example code:

$em = $this->get('doctrine')->getManager();
$repo = $em->getRepository(Category::class);

    $newCat = new Category();
    $newCat->setName('Test11');
    $newCat->setSlug('slug1');
    $newCat->setDescription('desc1');
    $newCat->setIsActive(true);
    $newCat->setDisplayOrder(1);
    $newCat->setParent($repo->find(11)); //<-- This does not get saved in the DB

    var_dump($newCat);

    $em->persist($newCat);
    $em->flush();

The $repo->find(11) returns a Category entity that has a parent (I set it manually in the database) but the parent field is NULL. (I have also tried with an entity that has no parent, same result) Here is the var_dump:

object(ModelBundle\Entity\Category)[421]
  private 'id' => null
  private 'slug' => string 'slug1' (length=42)
  private 'name' => string 'Teste11' (length=33)
  protected 'isActive' => boolean true
  private 'description' => string 'desc1' (length=5)
  private 'products' => 
    object(Doctrine\Common\Collections\ArrayCollection)[422]
      private 'elements' => 
        array (size=0)
          empty
  private 'displayOrder' => int 1
  private 'parent' => 
    object(ModelBundle\Entity\Category)[435]
      private 'id' => int 11
      private 'slug' => string 'entertaining' (length=12)
      private 'name' => string 'Browse Entertaining' (length=19)
      protected 'isActive' => boolean false
      private 'description' => string 'Selection of great food for any occasion.' (length=41)
      private 'products' => 
        object(Doctrine\ORM\PersistentCollection)[458]
          private 'snapshot' => 
            array (size=0)
          protected 'initialized' => boolean false
      private 'displayOrder' => null
      private 'parent' => null

This code creates a new category in the database but the parent field is set to NULL.

What am I doing wrong here? What do I need to do to get this to save the parent to the database. NOTE: I don’t want oneToMany I just want one category to have one parent and that is all.

Versions:

doctrine/annotations v1.3.1

doctrine/cache v1.6.1

doctrine/collections v1.4.0

doctrine/common v2.7.2

doctrine/data-fixtures v1.2.2

doctrine/dbal v2.5.12

doctrine/doctrine-bundle 1.6.7

doctrine/doctrine-cache-bundle 1.3.0

doctrine/doctrine-fixtures-bundle v2.4.0

doctrine/doctrine-migrations-bundle v1.2.1

doctrine/inflector v1.1.0

doctrine/instantiator 1.0.5

doctrine/lexer v1.0.1

doctrine/migrations v1.5.0

doctrine/orm v2.5.6

oro/doctrine-extensions 1.2.0

EDIT: Sorry guys, after 2 days of trying to get this to work, I noticed that doctrine had the following for caching (in symfony config):

orm:
    auto_generate_proxy_classes: "%kernel.debug%"
    entity_managers:
        default:
            naming_strategy: doctrine.orm.naming_strategy.underscore
            auto_mapping: true
            metadata_cache_driver: apcu
            query_cache_driver: apcu

As soon as I removed that cache, everything started to work. I was removing the caching directory manually but failed to see that doctrine was also being cached with apcu.

AntonioCS
  • 8,335
  • 18
  • 63
  • 92
  • 1
    There is too much information here for a follow up, could you try please to leave only what is relevant and remove the extra info? – ReynierPM Jun 27 '18 at 13:14
  • 1
    What happens if you remove the `indexes` and `joinColumns` parts from the YML and clear your cache? Usually,, you don’t need to declare them explicitely, and you may even run into problems with them. – lxg Jun 28 '18 at 11:02

0 Answers0