Skip to content

Instantly share code, notes, and snippets.

@guilhermeblanco
Created November 6, 2019 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guilhermeblanco/66e1e3e5890418d9efcfd4cee7b75363 to your computer and use it in GitHub Desktop.
Save guilhermeblanco/66e1e3e5890418d9efcfd4cee7b75363 to your computer and use it in GitHub Desktop.
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
use Closure;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
class LazyDataType extends Type
{
/** @var Closure */
private $initializer;
/** @var Type */
private $wrapped;
public function __construct(Closure $initializer)
{
$this->initializer = $initializer;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return $this->unwrap()->getName();
}
/**
* {@inheritdoc}
*/
public function getBindingType()
{
return $this->unwrap()->getBindingType();
}
/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $this->unwrap()->getSQLDeclaration($fieldDeclaration, $platform);
}
/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return $this->unwrap()->convertToDatabaseValue($value, $platform);
}
/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $this->unwrap()->convertToPHPValue($value, $platform);
}
/**
* {@inheritdoc}
*/
public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)
{
return $this->unwrap()->convertToDatabaseValueSQL($sqlExpr, $platform);
}
/**
* {@inheritdoc}
*/
public function convertToPHPValueSQL($sqlExpr, $platform)
{
return $this->unwrap()->convertToPHPValueSQL($sqlExpr, $platform);
}
/**
* {@inheritdoc}
*/
public function canRequireSQLConversion()
{
return $this->unwrap()->canRequireSQLConversion();
}
/**
* {@inheritdoc}
*/
public function requiresSQLCommentHint(AbstractPlatform $platform)
{
return $this->unwrap()->requiresSQLCommentHint($platform);
}
private function unwrap() : Type
{
if ($this->wrapped === null) {
$this->wrapped = ($this->initializer)();
}
return $this->wrapped;
}
}
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Builder;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Annotation;
use Doctrine\ORM\Cache\Exception\NonCacheableEntityAssociation;
use Doctrine\ORM\Mapping;
use RuntimeException;
use function count;
use function in_array;
use function sprintf;
abstract class ToOneAssociationMetadataBuilder extends AssociationMetadataBuilder
{
/** @var JoinColumnMetadataBuilder */
protected $joinColumnMetadataBuilder;
/** @var Annotation\Id|null */
protected $idAnnotation;
/** @var Annotation\JoinColumn|null */
protected $joinColumnAnnotation;
/** @var Annotation\JoinColumns|null */
protected $joinColumnsAnnotation;
public function __construct(
Mapping\ClassMetadataBuildingContext $metadataBuildingContext,
?JoinColumnMetadataBuilder $joinColumnMetadataBuilder = null,
?CacheMetadataBuilder $cacheMetadataBuilder = null
) {
parent::__construct($metadataBuildingContext, $cacheMetadataBuilder);
$this->joinColumnMetadataBuilder = $joinColumnMetadataBuilder ?: new JoinColumnMetadataBuilder($metadataBuildingContext);
}
public function withComponentMetadata(Mapping\ClassMetadata $componentMetadata) : AssociationMetadataBuilder
{
parent::withComponentMetadata($componentMetadata);
$this->joinColumnMetadataBuilder->withComponentMetadata($componentMetadata);
return $this;
}
public function withFieldName(string $fieldName) : AssociationMetadataBuilder
{
parent::withFieldName($fieldName);
$this->joinColumnMetadataBuilder->withFieldName($fieldName);
return $this;
}
public function withIdAnnotation(?Annotation\Id $idAnnotation) : ToOneAssociationMetadataBuilder
{
$this->idAnnotation = $idAnnotation;
return $this;
}
public function withJoinColumnAnnotation(?Annotation\JoinColumn $joinColumnAnnotation) : ToOneAssociationMetadataBuilder
{
$this->joinColumnAnnotation = $joinColumnAnnotation;
return $this;
}
public function withJoinColumnsAnnotation(?Annotation\JoinColumns $joinColumnsAnnotation) : ToOneAssociationMetadataBuilder
{
$this->joinColumnsAnnotation = $joinColumnsAnnotation;
return $this;
}
protected function buildPrimaryKey(Mapping\AssociationMetadata $associationMetadata) : void
{
if ($this->idAnnotation !== null) {
if ($associationMetadata->isOrphanRemoval()) {
throw Mapping\MappingException::illegalOrphanRemovalOnIdentifierAssociation(
$this->componentMetadata->getClassName(),
$this->fieldName
);
}
if (! $associationMetadata->isOwningSide()) {
throw Mapping\MappingException::illegalInverseIdentifierAssociation(
$this->componentMetadata->getClassName(),
$this->fieldName
);
}
if ($this->componentMetadata->getCache() !== null && $associationMetadata->getCache() === null) {
throw NonCacheableEntityAssociation::fromEntityAndField(
$this->componentMetadata->getClassName(),
$this->fieldName
);
}
// @todo guilhermeblanco The below block of code modifies component metadata properties, and it should be moved
// to the component metadata builder instead of here.
if (! in_array($this->fieldName, $this->componentMetadata->identifier, true)) {
$this->componentMetadata->identifier[] = $this->fieldName;
}
$associationMetadata->setPrimaryKey(true);
}
}
protected function buildJoinColumns(Mapping\ToOneAssociationMetadata $associationMetadata) : void
{
switch (true) {
case $this->joinColumnsAnnotation !== null:
foreach ($this->joinColumnsAnnotation->value as $joinColumnAnnotation) {
$this->joinColumnMetadataBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
$joinColumnMetadata = $this->joinColumnMetadataBuilder->build();
$dataTypeResolver = $this->createLazyDataTypeResolver(
$this->metadataBuildingContext,
$associationMetadata,
$joinColumnMetadata
);
$joinColumnMetadata->setType($dataTypeResolver);
$associationMetadata->addJoinColumn($joinColumnMetadata);
}
// Prevent currently unsupported scenario: association with multiple columns and being marked as primary
if ($associationMetadata->isPrimaryKey() && count($associationMetadata->getJoinColumns()) > 1) {
throw Mapping\MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
$this->componentMetadata->getClassName(),
$associationMetadata->getTargetEntity(),
$this->fieldName
);
}
break;
case $this->joinColumnAnnotation !== null:
$this->joinColumnMetadataBuilder->withJoinColumnAnnotation($this->joinColumnAnnotation);
$joinColumnMetadata = $this->joinColumnMetadataBuilder->build();
$dataTypeResolver = $this->createLazyDataTypeResolver(
$this->metadataBuildingContext,
$associationMetadata,
$joinColumnMetadata
);
$joinColumnMetadata->setType($dataTypeResolver);
$associationMetadata->addJoinColumn($joinColumnMetadata);
break;
default:
$joinColumnMetadata = $this->joinColumnMetadataBuilder->build();
$dataTypeResolver = $this->createLazyDataTypeResolver(
$this->metadataBuildingContext,
$associationMetadata,
$joinColumnMetadata
);
$joinColumnMetadata->setType($dataTypeResolver);
$associationMetadata->addJoinColumn($joinColumnMetadata);
break;
}
}
private function createLazyDataTypeResolver(
Mapping\ClassMetadataBuildingContext $metadataBuildingContext,
Mapping\ToOneAssociationMetadata $associationMetadata,
Mapping\JoinColumnMetadata $joinColumnMetadata
) {
return new Mapping\LazyDataType(
static function () use ($metadataBuildingContext, $associationMetadata, $joinColumnMetadata) : Type {
$classMetadataFactory = $metadataBuildingContext->getClassMetadataFactory();
$targetClassMetadata = $classMetadataFactory->getMetadataFor($associationMetadata->getTargetEntity());
$targetColumnMetadata = $targetClassMetadata->getColumn($joinColumnMetadata->getReferencedColumnName());
if (! $targetColumnMetadata) {
throw new RuntimeException(sprintf(
'Could not resolve type of column "%s" of class "%s"',
$joinColumnMetadata->getReferencedColumnName(),
$associationMetadata->getDeclaringClass()->getClassName()
));
}
return $targetColumnMetadata->getType();
}
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment