welcome back to dyb-tech

This commit is contained in:
Daniel Guzman
2024-05-18 02:28:01 +02:00
parent 9513cdba09
commit 9f30bc98c7
6149 changed files with 668407 additions and 0 deletions
@@ -0,0 +1,110 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Form\ChoiceList;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\ChoiceList\Loader\AbstractChoiceLoader;
use Symfony\Component\Form\Exception\LogicException;
/**
* Loads choices using a Doctrine object manager.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class DoctrineChoiceLoader extends AbstractChoiceLoader
{
private ObjectManager $manager;
private string $class;
private ?IdReader $idReader;
private ?EntityLoaderInterface $objectLoader;
/**
* Creates a new choice loader.
*
* Optionally, an implementation of {@link EntityLoaderInterface} can be
* passed which optimizes the object loading for one of the Doctrine
* mapper implementations.
*
* @param string $class The class name of the loaded objects
*/
public function __construct(ObjectManager $manager, string $class, ?IdReader $idReader = null, ?EntityLoaderInterface $objectLoader = null)
{
$classMetadata = $manager->getClassMetadata($class);
if ($idReader && !$idReader->isSingleId()) {
throw new \InvalidArgumentException(sprintf('The "$idReader" argument of "%s" must be null when the query cannot be optimized because of composite id fields.', __METHOD__));
}
$this->manager = $manager;
$this->class = $classMetadata->getName();
$this->idReader = $idReader;
$this->objectLoader = $objectLoader;
}
protected function loadChoices(): iterable
{
return $this->objectLoader
? $this->objectLoader->getEntities()
: $this->manager->getRepository($this->class)->findAll();
}
protected function doLoadValuesForChoices(array $choices): array
{
// Optimize performance for single-field identifiers. We already
// know that the IDs are used as values
// Attention: This optimization does not check choices for existence
if ($this->idReader) {
throw new LogicException('Not defining the IdReader explicitly as a value callback when the query can be optimized is not supported.');
}
return parent::doLoadValuesForChoices($choices);
}
protected function doLoadChoicesForValues(array $values, ?callable $value): array
{
if ($this->idReader && null === $value) {
throw new LogicException('Not defining the IdReader explicitly as a value callback when the query can be optimized is not supported.');
}
$idReader = null;
if (\is_array($value) && $value[0] instanceof IdReader) {
$idReader = $value[0];
} elseif ($value instanceof \Closure && ($rThis = (new \ReflectionFunction($value))->getClosureThis()) instanceof IdReader) {
$idReader = $rThis;
}
// Optimize performance in case we have an object loader and
// a single-field identifier
if ($idReader && $this->objectLoader) {
$objects = [];
$objectsById = [];
// Maintain order and indices from the given $values
// An alternative approach to the following loop is to add the
// "INDEX BY" clause to the Doctrine query in the loader,
// but I'm not sure whether that's doable in a generic fashion.
foreach ($this->objectLoader->getEntitiesByIds($idReader->getIdField(), $values) as $object) {
$objectsById[$idReader->getIdValue($object)] = $object;
}
foreach ($values as $i => $id) {
if (isset($objectsById[$id])) {
$objects[$i] = $objectsById[$id];
}
}
return $objects;
}
return parent::doLoadChoicesForValues($values, $value);
}
}
@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Form\ChoiceList;
/**
* Custom loader for entities in the choice list.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
interface EntityLoaderInterface
{
/**
* Returns an array of entities that are valid choices in the corresponding choice list.
*/
public function getEntities(): array;
/**
* Returns an array of entities matching the given identifiers.
*/
public function getEntitiesByIds(string $identifier, array $values): array;
}
@@ -0,0 +1,107 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Form\ChoiceList;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\Exception\RuntimeException;
/**
* A utility for reading object IDs.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
class IdReader
{
private ObjectManager $om;
private ClassMetadata $classMetadata;
private bool $singleId;
private bool $intId;
private string $idField;
private ?self $associationIdReader = null;
public function __construct(ObjectManager $om, ClassMetadata $classMetadata)
{
$ids = $classMetadata->getIdentifierFieldNames();
$idType = $classMetadata->getTypeOfField(current($ids));
$this->om = $om;
$this->classMetadata = $classMetadata;
$this->singleId = 1 === \count($ids);
$this->intId = $this->singleId && \in_array($idType, ['integer', 'smallint', 'bigint']);
$this->idField = current($ids);
// single field association are resolved, since the schema column could be an int
if ($this->singleId && $classMetadata->hasAssociation($this->idField)) {
$this->associationIdReader = new self($om, $om->getClassMetadata(
$classMetadata->getAssociationTargetClass($this->idField)
));
$this->singleId = $this->associationIdReader->isSingleId();
$this->intId = $this->associationIdReader->isIntId();
}
}
/**
* Returns whether the class has a single-column ID.
*/
public function isSingleId(): bool
{
return $this->singleId;
}
/**
* Returns whether the class has a single-column integer ID.
*/
public function isIntId(): bool
{
return $this->intId;
}
/**
* Returns the ID value for an object.
*
* This method assumes that the object has a single-column ID.
*/
public function getIdValue(?object $object = null): string
{
if (!$object) {
return '';
}
if (!$this->om->contains($object)) {
throw new RuntimeException(sprintf('Entity of type "%s" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?', get_debug_type($object)));
}
$this->om->initializeObject($object);
$idValue = current($this->classMetadata->getIdentifierValues($object));
if ($this->associationIdReader) {
$idValue = $this->associationIdReader->getIdValue($idValue);
}
return (string) $idValue;
}
/**
* Returns the name of the ID field.
*
* This method assumes that the object has a single-column ID.
*/
public function getIdField(): string
{
return $this->idField;
}
}
@@ -0,0 +1,110 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Doctrine\Form\ChoiceList;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Exception\TransformationFailedException;
/**
* Loads entities using a {@link QueryBuilder} instance.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ORMQueryBuilderLoader implements EntityLoaderInterface
{
/**
* Contains the query builder that builds the query for fetching the
* entities.
*
* This property should only be accessed through queryBuilder.
*/
private QueryBuilder $queryBuilder;
public function __construct(QueryBuilder $queryBuilder)
{
$this->queryBuilder = $queryBuilder;
}
public function getEntities(): array
{
return $this->queryBuilder->getQuery()->execute();
}
public function getEntitiesByIds(string $identifier, array $values): array
{
if (null !== $this->queryBuilder->getMaxResults() || 0 < (int) $this->queryBuilder->getFirstResult()) {
// an offset or a limit would apply on results including the where clause with submitted id values
// that could make invalid choices valid
$choices = [];
$metadata = $this->queryBuilder->getEntityManager()->getClassMetadata(current($this->queryBuilder->getRootEntities()));
foreach ($this->getEntities() as $entity) {
if (\in_array((string) current($metadata->getIdentifierValues($entity)), $values, true)) {
$choices[] = $entity;
}
}
return $choices;
}
$qb = clone $this->queryBuilder;
$alias = current($qb->getRootAliases());
$parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier;
$parameter = str_replace('.', '_', $parameter);
$where = $qb->expr()->in($alias.'.'.$identifier, ':'.$parameter);
// Guess type
$entity = current($qb->getRootEntities());
$metadata = $qb->getEntityManager()->getClassMetadata($entity);
if (\in_array($type = $metadata->getTypeOfField($identifier), ['integer', 'bigint', 'smallint'])) {
$parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY;
// Filter out non-integer values (e.g. ""). If we don't, some
// databases such as PostgreSQL fail.
$values = array_values(array_filter($values, fn ($v) => (string) $v === (string) (int) $v || ctype_digit($v)));
} elseif (\in_array($type, ['ulid', 'uuid', 'guid'])) {
$parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY;
// Like above, but we just filter out empty strings.
$values = array_values(array_filter($values, fn ($v) => '' !== (string) $v));
// Convert values into right type
if (Type::hasType($type)) {
$doctrineType = Type::getType($type);
$platform = $qb->getEntityManager()->getConnection()->getDatabasePlatform();
foreach ($values as &$value) {
try {
$value = $doctrineType->convertToDatabaseValue($value, $platform);
} catch (ConversionException $e) {
throw new TransformationFailedException(sprintf('Failed to transform "%s" into "%s".', $value, $type), 0, $e);
}
}
unset($value);
}
} else {
$parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY;
}
if (!$values) {
return [];
}
return $qb->andWhere($where)
->getQuery()
->setParameter($parameter, $values, $parameterType)
->getResult();
}
}