Иногда встроенных в Doctrine стратегий не достаточно и нужно создать свою. Но делать просто функцию или метод не правильно с точки зрения архитектуры, тогда как у Doctrine есть встроенные стредства для этого.
Решение для версии 2.3.0 +
Начиная с версии 2.3.0 в Doctrine появилась стратегия CUSTOM и ее использование достаточно тривиально. Для начала можно заглянуть в файл Doctrine/ORM/Mapping/ClassMetadataInfo.php, чтобы увидеть список доступных стратегий, обозначенный константами.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
/* The Id generator types. */ /** * AUTO means the generator type will depend on what the used platform prefers. * Offers full portability. */ const GENERATOR_TYPE_AUTO = 1; /** * SEQUENCE means a separate sequence object will be used. Platforms that do * not have native sequence support may emulate it. Full portability is currently * not guaranteed. */ const GENERATOR_TYPE_SEQUENCE = 2; /** * TABLE means a separate table is used for id generation. * Offers full portability. */ const GENERATOR_TYPE_TABLE = 3; /** * IDENTITY means an identity column is used for id generation. The database * will fill in the id column on insertion. Platforms that do not support * native identity columns may emulate them. Full portability is currently * not guaranteed. */ const GENERATOR_TYPE_IDENTITY = 4; /** * NONE means the class does not have a generated id. That means the class * must have a natural, manually assigned id. */ const GENERATOR_TYPE_NONE = 5; /** * UUID means that a UUID/GUID expression is used for id generation. Full * portability is currently not guaranteed. */ const GENERATOR_TYPE_UUID = 6; /** * CUSTOM means that customer will use own ID generator that supposedly work */ const GENERATOR_TYPE_CUSTOM = 7; |
Виден весь перечень стратегий, ну а нужный CUSTOM используется так. Добавляется следующая аннотация:
1 2 3 4 5 6 7 8 9 |
/** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="CUSTOM") * @ORM\CustomIdGenerator(class="MyModule\Doctrine\RandomIdGenerator") */ protected $id; |
Указанный в аннотации класс должен наследоваться от Doctrine\ORM\Id\AbstractIdGenerator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
namespace MyModule\Doctrine; use Doctrine\ORM\Id\AbstractIdGenerator; class RandomIdGenerator extends AbstractIdGenerator { public function generate(\Doctrine\ORM\EntityManager $em, $entity) { $entity_name = $em->getClassMetadata(get_class($entity))->getName(); while (true) { $id = mt_rand(100000, 999999); $item = $em->find($entity_name, $id); if (!$item) { return $id; } } throw new \Exception('RandomIdGenerator worked hard, but could not generate unique ID :('); } } |
Все достаточно просто.
Решение для версии до 2.3.0
До версии 2.3 в Doctrine не существовало встроенной возможности использовать CUSTOM стратегию со своим генератором. Поэтому приходилось расширять класс ClassMetadataInfo.php для добавления свох констант стратегий. Если посмотреть на ClassMetadataInfo.php до версии 2.3 то видно, что нет UUID и CUSTOM.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const GENERATOR_TYPE_IDENTITY = 4; /** * NONE means the class does not have a generated id. That means the class * must have a natural, manually assigned id. */ const GENERATOR_TYPE_NONE = 5; /** * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time * by doing a property-by-property comparison with the original data. This will * be done for all entities that are in MANAGED state at commit-time. * * This is the default change tracking policy. */ const CHANGETRACKING_DEFERRED_IMPLICIT = 1; |
На примере UUID свою стратегию можно реализовать так. В классе ClassMetadataInfo (или в своем классе наследующемся от него, в этом случае надо еще переопределить один из Doctrine service) добавить константу:
1 2 3 |
const GENERATOR_TYPE_UUID = 6; |
Далее перейти в метод completeIdGeneratorMapping в этом же классе и добавить case случай:
1 2 3 4 5 |
case ClassMetadata::GENERATOR_TYPE_UUID: $class->setIdGenerator(new \Doctrine\ORM\Id\UUIDGenerator()); break; |
Путь к классу может быть в другом пространстве имен. Теперь стратегию можно использовать.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * @Entity */ class User{ /** * * @Id * @Column(type="string", length=36) * @GeneratedValue(strategy="UUID") */ private $id; } |
В завершение еще один не такой красивый, но рабочий способ, это использовать систему эвентов, а именно эвент prePersist.
1 2 3 4 5 6 7 8 |
/** * @ORM\PrePersist() */ public function preSave() { $this->id = uniqid(); } |