1

I have an DB table with a UNIQUE column that should contain a unique 8 character alphanumeric string.

I've (finally) making the move from my own MVC framework to symfony. Up until now I would have had a private method in the model that is called on CREATE. A loop in the method would generate a random hash, and perform a READ on the table to see if it is unique: if so, the hash would be returned and injected into the CREATE request.

The problem as I see it is that in symfony I have no access to the repository from within the entity class, so I can't use a lifecycle callback. I understand the reasoning behind this. On the other hand, the hash generation has nothing to do with the controller – for me it is internal logic that belongs in the model. If I later change the data structure, I need to edit the controller.

My question is: architecture-wise, where should I put the hash generation method?

Tim
  • 6,281
  • 3
  • 39
  • 49

3 Answers3

1

Answering my own question:

I created a custom repository, which has access to the doctrine entity manager.

The repository has a createNewHash method:

class HashRepository extends EntityRepository
{
    public function createNewHash()
    {
        $hash = new Hash();
        $hash->setHash($this->_getUniqueHash());
        $em = $this->getEntityManager();
        $em->persist($hash);
        $em->flush();
        return $hash;
    }

    private function _getUniqueHash()
    {
        $hash = null;
        $hashexists = true;
        while ($hashexists) {
            $hash = $this->_generateRandomAlphaNumericString();
            if (!$hashobject = $this->findOneByHash($hash)) {
                $hashexists = false;
            }
        }
        return $hash;
    }

    private function _generateRandomAlphaNumericString( $length=8 )
    {
        $bits = $length / 2;
        return bin2hex(openssl_random_pseudo_bytes($bits));
    }
}

The createNewHash() method can then be called from the Controller, and the Controller does not have to concern itself with hash creation.

EDIT: Listeners are another way of doing it.

Tim
  • 6,281
  • 3
  • 39
  • 49
1

You can use a listener. You were right that the lifecycle callbacks are not the correct solution since you need access to the repository. But you can define a Listener that listens to the same event as the lifecycle callback, but is an service and therefore can have the repository as dependency.

m0c
  • 2,180
  • 26
  • 45
0

In your entity constructor, i can add this:

<?php

namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * MyEntity
 *
 * @ORM\Table(name="my_entity")
 */
class MyEntity
{
    /**
     * @ORM\Column(type="string", length=8, unique=true, nullable=false)
     * @var string
     */
    private $uniqId;

    public function __construct()
    {
        $this->uniqId = hash('crc32b', uniqid());
    }

    // ...

}

Hope this helps

Picoss
  • 2,047
  • 13
  • 14
  • Thanks, but I know how to generate a hash – I need to perform a DB read in the entity to see if it really is unique. ( See this comment on uniqueness in generated hashes: http://stackoverflow.com/a/4070171/698511 ) – Tim Oct 16 '13 at 10:04
  • 1
    So you need to use event listeners : http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html – Picoss Oct 16 '13 at 10:12
  • Looked at them, but managed to do it with an EntityRepository (see my answer). There are probably several ways to do it. Thanks for the help! – Tim Oct 16 '13 at 10:17