How to disable Blameable-behaviour programmatically in Symfony2

2.4k views Asked by At

I'm trying to run a console command in symfony2 in which some properties of a certain class are being updated. One of the properties has got a corresponding reviewedBy-property which is being set by the blameable-behaviour like so:

/**
 * @var bool
 * @ORM\Column(name="public_cmt", type="boolean", nullable=true)
 */
private $publicCmt;

/**
 * @var User $publicCmtReviewedBy
 *
 * @Gedmo\Blameable(on="change", field="public_cmt")
 * @ORM\ManyToOne(targetEntity="My\Bundle\EntityBundle\Entity\User")
 * @ORM\JoinColumn(name="public_cmt_reviewed_by", referencedColumnName="id", nullable=true)
 */
private $publicCmtReviewedBy;

When i run the task there's no user which can be 'blamed' so I get the following exception:

[Doctrine\ORM\ORMInvalidArgumentException]                                   
EntityManager#persist() expects parameter 1 to be an entity object, NULL given.

However I can also not disable blameable because it's not registered as a filter by the time i start the task and programmatically trying to set the user through:

// create the authentication token
        $token = new UsernamePasswordToken(
            $user,
            null,
            'main',
            $user->getRoles());
        // give it to the security context
        $this->getService('security.context')->setToken($token); 

doesn't work. Anyone got an idea?

3

There are 3 answers

1
Jason Roman On BEST ANSWER

First of all, I'm not sure if 'field' cares if you use the database column or the property, but you might need to change it to field="publicCmt".

What you should do is override the Blameable Listener. I'm going to assume you are using the StofDoctrineExtensionsBundle. First override in your config:

# app/config/config.yml
stof_doctrine_extensions:
    class:
        blameable: MyBundle\BlameableListener

Now just extend the existing listener. You have a couple options - either you want to allow for NULL values (no blame), or, you want to have a default user. Say for example you want to just skip the persist and allow a null, you would override as such:

namespace MyBundle\EventListener;

use Gedmo\Blameable\BlameableListener;

class MyBlameableListener extends BlameableListener
{
    public function getUserValue($meta, $field)
    {
        try {
            $user = parent::getUserValue($meta, $field);
        }
        catch (\Exception $e) {
            $user = null;

        return $user;
    }

    protected function updateField($object, $ea, $meta, $field)
    {
        if (!$user) {
            return;
        }

        parent::updateField($object, $ea, $meta, $field);
    }
}

So it tries to use the parent getUserValue() function first to grab the user, and if not it returns null. We must put in a try/catch because it throws an Exception if there is no current user. Now in our updateField() function, we simply don't do anything if there is no user.

Disclaimer - there may be parts of that updateField() function that you still need...I haven't tested this.

This is just an example. Another idea would be to have a default database user. You could put that in your config file with a particular username. Then instead of returning null if there is no user from the security token, you could instead grab the default user from the database and use that (naturally you'd have to inject the entity manager in the service as well).

2
Harry the Holtzman On

Slight modification of the above answer with identical config.yml-entry: we can check if a user is set and if not: since we have access to the object-manager in the updateField-method, get a default-user, set it and then execute the parent-method.

namespace MyBundle\EventListener;

use Gedmo\Blameable\BlameableListener;

class MyBlameableListener extends BlameableListener
{
    protected function updateField($object, $ea, $meta, $field)
    {
        // If we don't have a user, we are in a task and set a default-user
        if (null === $this->getUserValue($meta, $field)) {
            /*  @var $ur UserRepository */
            $ur       = $ea->getObjectManager()->getRepository('MyBundle:User');
            $taskUser = $ur->findOneBy(array('name' => 'task-user'));
            $this->setUserValue($taskUser);
        }
        parent::updateField($object, $ea, $meta, $field);
    }
}
0
Pierre On

If you use the StofDoctrineExtensionsBundle you can simply do :

$this->container->get('stof_doctrine_extensions.listener.blameable')
            ->setUserValue('task-user');

see : https://github.com/stof/StofDoctrineExtensionsBundle/issues/197