0

I'm trying to soft delete a complete Customer. A Customer extends User. Customer also has associated InvoiceAddress[] entities.

It however, does not work. If the Customer has @Gedmo\SoftDeleteable, it fails on the Foreign Key association with User. If I also make the User entity soft delete-able, then it fails on the association between Customer and InvoiceAddress.

If I make the relation between Customer and InvoiceAddress to cascade={"persist", "remove"} (added remove), then it hard deletes all entities related to the Customer.

I figure it might be something in the configuration, though having read multiple questions and (of course) the docs of the SoftDeleteable extension itself, I haven't figured out what/where I'm doing something wrong.

Below is my setup, I've removed stuff from the code unrelated to the question.

Customer.php

namespace Customer\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\SoftDeleteable\SoftDeleteable;
// moar

/**
 * @ORM\Table
 * @ORM\Entity
 *
 * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
 */
class Customer extends User implements SoftDeleteable
{
    use GedmoDeletedAtTrait;

    /**
     * @var ArrayCollection|InvoiceAddress[]
     * @ORM\OneToMany(targetEntity="Customer\Entity\InvoiceAddress", mappedBy="customer", cascade={"persist"}, fetch="EAGER")
     */
    protected $invoiceAddresses;

    // properties, __construct(){}, getters/setters...
}

User.php

namespace User\Entity;

use BjyAuthorize\Provider\Role\ProviderInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Mvc\Entity\AbstractEntity;
use ZfcUser\Entity\UserInterface;

/**
 * @ORM\Table
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 *
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 */
class User extends AbstractEntity implements UserInterface, ProviderInterface
{
    // properties, constructor, getters/setters... 
}

GedmoDeletedAtTrait.php

namespace Mvc\Traits;

use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;

trait GedmoDeletedAtTrait
{
    use SoftDeleteableEntity;

    /**
     * Note: overrides Annotation (column name) and type hint, else it's the same as the original
     *
     * @var \DateTime|null
     * @Doctrine\ORM\Mapping\Column(name="deleted_at", type="datetime", nullable=true)
     */
    protected $deletedAt;
}

doctrine module config for Customer module

'doctrine' => [
    'driver' => [
        __NAMESPACE__ . '_driver' => [
            'class' => 'Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver',
            'cache' => 'array',
            'paths' => [
                __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src'
                . DIRECTORY_SEPARATOR . 'Entity',
            ]
        ],
        'orm_default'             => [
            'drivers' => [
                __NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
            ],
        ],
    ],
    'eventmanager' => [
        'orm_default' => [
            'subscribers' => [
                SoftDeleteableListener::class,
            ],
        ],
    ],
],

Related question: the docs also mention "filters". How to implement them and use them throughout a module with the setup above?

rkeet
  • 3,406
  • 2
  • 23
  • 49

1 Answers1

0

Found the answer. I was missing a piece of configuration, not (yet) sure as to how it relates to the Listener and the LifecycleCallbacks that need to be executed to soft-delete an Entity, but the complete configuration is as follows:

use Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter;
use Gedmo\SoftDeleteable\SoftDeleteableListener;

[ ... ]

'doctrine' => [
    'driver' => [
        __NAMESPACE__ . '_driver' => [
            'class' => 'Doctrine\\ORM\\Mapping\\Driver\\AnnotationDriver',
            'cache' => 'array',
            'paths' => [
                __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'src'
                . DIRECTORY_SEPARATOR . 'Entity',
            ]
        ],
        'orm_default'             => [
            'drivers' => [
                __NAMESPACE__ . '\Entity' => __NAMESPACE__ . '_driver'
            ],
        ],
    ],
    'eventmanager' => [
        'orm_default' => [
            'subscribers' => [
                SoftDeleteableListener::class,
            ],
        ],
    ],
    // THIS IS THE PART THAT WAS MISSING
    'configuration' => [
        'orm_default' => [
            'filters' => [
                'soft-deletable' => SoftDeleteableFilter::class,
            ],
        ],
    ],
],

In the above snipped I've marked the missing piece with a comment. However, as that bit just sets a filter to use on an alias, I'm not sure how it relates to the configuration above, which defines a Listener.

If I figure it out later/in the future I might come back and update this answer. In the mean time, maybe someone else might place a comment with the info?

rkeet
  • 3,406
  • 2
  • 23
  • 49