phpsymfonydoctrinedoctrine-odm

Symfony MongoDb default sets empty array instead of not setting field


I've got a Document class with an EmbeddedDocument multiple self-inherited. It's working fine, but when i'm not sending subData it's saved in Mongo as empty array (subData: []).

I would expect that this field isn't saved at all if not sent.

I've tried with nullable=false and nullable=true.

Not setting ArrayCollection in __construct makes error Call to a member function add() on null

<?php

namespace App\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
 * @MongoDB\EmbeddedDocument()
 */
class Data
{
    /**
     * @MongoDB\Id()
     */
    private $id;

    /**
     * @MongoDB\Field(type="string")
     */
    private $value;

    /**
     * @MongoDB\EmbedMany(targetDocument="App\Document\Data", nullable=false)
     */
    public $subData = null;

    public function __construct()
    {
        $this->subData = new ArrayCollection();
    }

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

    public function getValue(): ?string
    {
        return $this->value;
    }

    public function setValue(string $value): self
    {
        $this->value = $value;

        return $this;
    }

    public function getSubData(): Collection
    {
        return $this->subData;
    }

    public function setSubData(array $subData): self
    {
        $this->subData = $subData;

        return $this;
    }

    public function addSubdata(Data $subData): self
    {
        $this->subData->add($subData);

        return $this;
    }
}

UPDATED

changing setter to this didn't help as suggest @Maniax it's still subData: []

    public function setSubData(array $subData): self
    {
        if(!empty($subData) && count($subData) > 0 && $subData !== null) {
            $this->subData = $subData;
        }

        return $this;
    }

Solution

  • If you don't want null in you database when the array is empty, you want to avoid initializing it by default, so avoid

    public function __construct()
    {
        $this->subData = new ArrayCollection();
    }
    

    The array still needs to be initialized, so in the add function you'll have to check if the array is not initialized before adding

    public function addSubData(Data $subData): self
    {
        // null and an empty array are falsey
        if (!$this->subData) {
            $this->subData = new ArrayCollection();
        }
        $this->subData->add($subData);
    }
    
    return $this;
    }
    

    And in the setter you check if you're not getting an empty array

    public function setSubData(array $subData): self
    {
        if(!empty($subData)) {
            $this->subData = $subData;
        }
    
        return $this;
    }
    

    This should prevent an empty array to end up in your database.