This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,241 @@
<?php
namespace Gedmo\References;
use Doctrine\Common\Collections\Collection;
/**
* Lazy collection for loading reference many associations.
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class LazyCollection implements Collection
{
private $results;
private $callback;
public function __construct($callback)
{
$this->callback = $callback;
}
public function add($element)
{
$this->initialize();
return $this->results->add($element);
}
public function clear()
{
$this->initialize();
return $this->results->clear();
}
public function contains($element)
{
$this->initialize();
return $this->results->contains($element);
}
public function containsKey($key)
{
$this->initialize();
return $this->results->containsKey($key);
}
public function current()
{
$this->initialize();
return $this->results->current();
}
public function exists(\Closure $p)
{
$this->initialize();
return $this->results->exists($p);
}
public function filter(\Closure $p)
{
$this->initialize();
return $this->results->filter($p);
}
public function first()
{
$this->initialize();
return $this->results->first();
}
public function forAll(\Closure $p)
{
$this->initialize();
return $this->results->forAll($p);
}
public function get($key)
{
$this->initialize();
return $this->results->get($key);
}
public function getKeys()
{
$this->initialize();
return $this->results->getKeys();
}
public function getValues()
{
$this->initialize();
return $this->results->getValues();
}
public function indexOf($element)
{
$this->initialize();
return $this->results->indexOf($element);
}
public function isEmpty()
{
$this->initialize();
return $this->results->isEmpty();
}
public function key()
{
$this->initialize();
return $this->results->key();
}
public function last()
{
$this->initialize();
return $this->results->last();
}
public function map(\Closure $func)
{
$this->initialize();
return $this->results->map($func);
}
public function next()
{
$this->initialize();
return $this->results->next();
}
public function partition(\Closure $p)
{
$this->initialize();
return $this->results->partition($p);
}
public function remove($key)
{
$this->initialize();
return $this->results->remove($key);
}
public function removeElement($element)
{
$this->initialize();
return $this->results->removeElement($element);
}
public function set($key, $value)
{
$this->initialize();
return $this->results->set($key, $value);
}
public function slice($offset, $length = null)
{
$this->initialize();
return $this->results->slice($offset, $length);
}
public function toArray()
{
$this->initialize();
return $this->results->toArray();
}
public function offsetExists($offset)
{
$this->initialize();
return $this->results->offsetExists($offset);
}
public function offsetGet($offset)
{
$this->initialize();
return $this->results->offsetGet($offset);
}
public function offsetSet($offset, $value)
{
$this->initialize();
return $this->results->offsetSet($offset, $value);
}
public function offsetUnset($offset)
{
$this->initialize();
return $this->results->offsetUnset($offset);
}
public function getIterator()
{
$this->initialize();
return $this->results->getIterator();
}
public function count()
{
$this->initialize();
return $this->results->count();
}
private function initialize()
{
if (null === $this->results) {
$this->results = call_user_func($this->callback);
}
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Gedmo\References\Mapping\Driver;
use Gedmo\Mapping\Driver\AnnotationDriverInterface;
/**
* This is an annotation mapping driver for References
* behavioral extension.
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class Annotation implements AnnotationDriverInterface
{
/**
* Annotation to mark field as reference to one
*/
const REFERENCE_ONE = 'Gedmo\\Mapping\\Annotation\\ReferenceOne';
/**
* Annotation to mark field as reference to many
*/
const REFERENCE_MANY = 'Gedmo\\Mapping\\Annotation\\ReferenceMany';
/**
* Annotation to mark field as reference to many
*/
const REFERENCE_MANY_EMBED = 'Gedmo\\Mapping\\Annotation\\ReferenceManyEmbed';
private $annotations = array(
'referenceOne' => self::REFERENCE_ONE,
'referenceMany' => self::REFERENCE_MANY,
'referenceManyEmbed' => self::REFERENCE_MANY_EMBED,
);
/**
* Annotation reader instance
*
* @var object
*/
private $reader;
/**
* original driver if it is available
*/
protected $_originalDriver = null;
/**
* {@inheritDoc}
*/
public function setAnnotationReader($reader)
{
$this->reader = $reader;
}
/**
* {@inheritDoc}
*/
public function readExtendedMetadata($meta, array &$config)
{
$class = $meta->getReflectionClass();
foreach ($this->annotations as $key => $annotation) {
$config[$key] = array();
foreach ($class->getProperties() as $property) {
if ($meta->isMappedSuperclass && !$property->isPrivate() ||
$meta->isInheritedField($property->name) ||
isset($meta->associationMappings[$property->name]['inherited'])
) {
continue;
}
if ($reference = $this->reader->getPropertyAnnotation($property, $annotation)) {
$config[$key][$property->getName()] = array(
'field' => $property->getName(),
'type' => $reference->type,
'class' => $reference->class,
'identifier' => $reference->identifier,
'mappedBy' => $reference->mappedBy,
'inversedBy' => $reference->inversedBy,
);
}
}
}
}
/**
* Passes in the mapping read by original driver
*
* @param $driver
* @return void
*/
public function setOriginalDriver($driver)
{
$this->_originalDriver = $driver;
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace Gedmo\References\Mapping\Driver;
use Gedmo\Mapping\Driver\Xml as BaseXml;
use Gedmo\Exception\InvalidMappingException;
/**
* This is a xml mapping driver for References
* behavioral extension. Used for extraction of extended
* metadata from xml specifically for References
* extension.
*
* @author Aram Alipoor <aram.alipoor@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class Xml extends BaseXml
{
/**
* @var array
*/
private $validTypes = array(
'document',
'entity'
);
/**
* @var array
*/
private $validReferences = array(
'referenceOne',
'referenceMany',
'referenceManyEmbed'
);
/**
* {@inheritDoc}
*/
public function readExtendedMetadata($meta, array &$config)
{
/**
* @var \SimpleXmlElement $xml
*/
$xml = $this->_getMapping($meta->name);
$xmlDoctrine = $xml;
$xml = $xml->children(self::GEDMO_NAMESPACE_URI);
if ($xmlDoctrine->getName() === 'entity' || $xmlDoctrine->getName() === 'document' || $xmlDoctrine->getName() === 'mapped-superclass') {
if (isset($xml->reference)) {
/**
* @var \SimpleXMLElement $element
*/
foreach ($xml->reference as $element) {
if (!$this->_isAttributeSet($element, 'type')) {
throw new InvalidMappingException("Reference type (document or entity) is not set in class - {$meta->name}");
}
$type = $this->_getAttribute($element, 'type');
if (!in_array($type, $this->validTypes)) {
throw new InvalidMappingException(
$type .
' is not a valid reference type, valid types are: ' .
implode(', ', $this->validTypes)
);
}
$reference = $this->_getAttribute($element, 'reference');
if (!in_array($reference, $this->validReferences)) {
throw new InvalidMappingException(
$reference .
' is not a valid reference, valid references are: ' .
implode(', ', $this->validReferences)
);
}
if (!$this->_isAttributeSet($element, 'field')) {
throw new InvalidMappingException("Reference field is not set in class - {$meta->name}");
}
$field = $this->_getAttribute($element, 'field');
if (!$this->_isAttributeSet($element, 'class')) {
throw new InvalidMappingException("Reference field is not set in class - {$meta->name}");
}
$class = $this->_getAttribute($element, 'class');
if (!$this->_isAttributeSet($element, 'identifier')) {
throw new InvalidMappingException("Reference identifier is not set in class - {$meta->name}");
}
$identifier = $this->_getAttribute($element, 'identifier');
$config[$reference][$field] = array(
'field' => $field,
'type' => $type,
'class' => $class,
'identifier' => $identifier
);
if (!$this->_isAttributeSet($element, 'mappedBy')) {
$config[$reference][$field]['mappedBy'] = $this->_getAttribute($element, 'mappedBy');
}
if (!$this->_isAttributeSet($element, 'inversedBy')) {
$config[$reference][$field]['inversedBy'] = $this->_getAttribute($element, 'inversedBy');
}
}
}
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Gedmo\References\Mapping\Driver;
use Gedmo\Mapping\Driver\File;
use Gedmo\Mapping\Driver;
use Gedmo\Exception\InvalidMappingException;
/**
* @author Gonzalo Vilaseca <gonzalo.vilaseca@reiss.com>
*/
class Yaml extends File implements Driver
{
/**
* File extension
* @var string
*/
protected $_extension = '.dcm.yml';
private $validReferences = array(
'referenceOne' => array(),
'referenceMany' => array(),
'referenceManyEmbed' => array(),
);
/**
* {@inheritDoc}
*/
public function readExtendedMetadata($meta, array &$config)
{
$mapping = $this->_getMapping($meta->name);
if (isset($mapping['gedmo']) && isset($mapping['gedmo']['reference'])) {
foreach ($mapping['gedmo']['reference'] as $field => $fieldMapping) {
$reference = $fieldMapping['reference'];
if (!in_array($reference, array_keys($this->validReferences))) {
throw new InvalidMappingException(
$reference .
' is not a valid reference, valid references are: ' .
implode(', ', array_keys($this->validReferences))
);
}
$config[$reference][$field] = array(
'field' => $field,
'type' => $fieldMapping['type'],
'class' => $fieldMapping['class'],
);
if (array_key_exists('mappedBy', $fieldMapping)) {
$config[$reference][$field]['mappedBy'] = $fieldMapping['mappedBy'];
}
if (array_key_exists('identifier', $fieldMapping)) {
$config[$reference][$field]['identifier'] = $fieldMapping['identifier'];
}
if (array_key_exists('inversedBy', $fieldMapping)) {
$config[$reference][$field]['inversedBy'] = $fieldMapping['inversedBy'];
}
}
}
$config = array_merge($this->validReferences, $config);
}
/**
* {@inheritDoc}
*/
protected function _loadMappingFile($file)
{
return \Symfony\Component\Yaml\Yaml::parse($file);
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Gedmo\References\Mapping\Event\Adapter;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Proxy\Proxy as MongoDBProxy;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Proxy\Proxy as ORMProxy;
use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
use Gedmo\References\Mapping\Event\ReferencesAdapter;
/**
* Doctrine event adapter for ODM references behavior
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
final class ODM extends BaseAdapterODM implements ReferencesAdapter
{
/**
* @inheritDoc
*/
public function getIdentifier($om, $object, $single = true)
{
if ($om instanceof DocumentManager) {
return $this->extractIdentifier($om, $object, $single);
}
if ($om instanceof EntityManagerInterface) {
if ($object instanceof ORMProxy) {
$id = $om->getUnitOfWork()->getEntityIdentifier($object);
} else {
$meta = $om->getClassMetadata(get_class($object));
$id = array();
foreach ($meta->identifier as $name) {
$id[$name] = $meta->getReflectionProperty($name)->getValue($object);
// return null if one of identifiers is missing
if (!$id[$name]) {
return null;
}
}
}
if ($single) {
$id = current($id);
}
return $id;
}
}
/**
* @inheritDoc
*/
public function getSingleReference($om, $class, $identifier)
{
$this->throwIfNotEntityManager($om);
$meta = $om->getClassMetadata($class);
if (!$meta->isInheritanceTypeNone()) {
return $om->find($class, $identifier);
}
return $om->getReference($class, $identifier);
}
/**
* @inheritDoc
*/
public function extractIdentifier($om, $object, $single = true)
{
$meta = $om->getClassMetadata(get_class($object));
if ($object instanceof MongoDBProxy) {
$id = $om->getUnitOfWork()->getDocumentIdentifier($object);
} else {
$id = $meta->getReflectionProperty($meta->identifier)->getValue($object);
}
if ($single || !$id) {
return $id;
} else {
return array($meta->identifier => $id);
}
}
/**
* Override so we don't get an exception. We want to allow this.
*/
private function throwIfNotEntityManager(EntityManagerInterface $em)
{
}
}

View File

@@ -0,0 +1,119 @@
<?php
namespace Gedmo\References\Mapping\Event\Adapter;
use Doctrine\ODM\MongoDB\DocumentManager as MongoDocumentManager;
use Doctrine\ODM\MongoDB\Proxy\Proxy as MongoDBProxy;
use Doctrine\ODM\PHPCR\DocumentManager as PhpcrDocumentManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Proxy\Proxy as ORMProxy;
use Gedmo\Exception\InvalidArgumentException;
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
use Gedmo\References\Mapping\Event\ReferencesAdapter;
/**
* Doctrine event adapter for ORM references behavior
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
final class ORM extends BaseAdapterORM implements ReferencesAdapter
{
/**
* @inheritDoc
*/
public function getIdentifier($om, $object, $single = true)
{
if ($om instanceof EntityManagerInterface) {
return $this->extractIdentifier($om, $object, $single);
}
if ($om instanceof MongoDocumentManager) {
$meta = $om->getClassMetadata(get_class($object));
if ($object instanceof MongoDBProxy) {
$id = $om->getUnitOfWork()->getDocumentIdentifier($object);
} else {
$id = $meta->getReflectionProperty($meta->identifier)->getValue($object);
}
if ($single || !$id) {
return $id;
}
return array($meta->identifier => $id);
}
if ($om instanceof PhpcrDocumentManager) {
$meta = $om->getClassMetadata(get_class($object));
$id = $meta->getReflectionProperty($meta->identifier)->getValue($object);
if ($single || !$id) {
return $id;
}
return array($meta->identifier => $id);
}
}
/**
* @inheritDoc
*/
public function getSingleReference($om, $class, $identifier)
{
$this->throwIfNotDocumentManager($om);
$meta = $om->getClassMetadata($class);
if ($om instanceof MongoDocumentManager) {
if (!$meta->isInheritanceTypeNone()) {
return $om->find($class, $identifier);
}
}
return $om->getReference($class, $identifier);
}
/**
* @inheritDoc
*/
public function extractIdentifier($om, $object, $single = true)
{
if ($object instanceof ORMProxy) {
$id = $om->getUnitOfWork()->getEntityIdentifier($object);
} else {
$meta = $om->getClassMetadata(get_class($object));
$id = array();
foreach ($meta->identifier as $name) {
$id[$name] = $meta->getReflectionProperty($name)->getValue($object);
// return null if one of identifiers is missing
if (!$id[$name]) {
return null;
}
}
}
if ($single) {
$id = current($id);
}
return $id;
}
/**
* Override so we don't get an exception. We want to allow this.
*/
private function throwIfNotDocumentManager($dm)
{
if (!($dm instanceof MongoDocumentManager) && !($dm instanceof PhpcrDocumentManager)) {
throw new InvalidArgumentException(
sprintf(
'Expected a %s or %s instance but got "%s"',
'Doctrine\ODM\MongoDB\DocumentManager',
'Doctrine\ODM\PHPCR\DocumentManager',
is_object($dm) ? get_class($dm) : gettype($dm)
)
);
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Gedmo\References\Mapping\Event;
use Doctrine\Common\Persistence\ObjectManager;
use Gedmo\Mapping\Event\AdapterInterface;
/**
* Doctrine event adapter interface for References behavior
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
interface ReferencesAdapter extends AdapterInterface
{
/**
* Gets the identifier of the given object using the passed ObjectManager.
*
* @param ObjectManager $om
* @param object $object
* @param bool $single
*
* @return array|string|int $id - array or single identifier
*/
public function getIdentifier($om, $object, $single = true);
/**
* Gets a single reference for the given ObjectManager, class and identifier.
*
* @param ObjectManager $om
* @param string $class
* @param array|string|int $identifier
**/
public function getSingleReference($om, $class, $identifier);
/**
* Extracts identifiers from object or proxy.
*
* @param ObjectManager $om
* @param object $object
* @param bool $single
*
* @return array|string|int - array or single identifier
*/
public function extractIdentifier($om, $object, $single = true);
}

View File

@@ -0,0 +1,211 @@
<?php
namespace Gedmo\References;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\EventArgs;
use Doctrine\Common\Persistence\ObjectManager;
use Gedmo\Mapping\MappedEventSubscriber;
/**
* Listener for loading and persisting cross database references.
*
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Jonathan H. Wage <jonwage@gmail.com>
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class ReferencesListener extends MappedEventSubscriber
{
private $managers;
public function __construct(array $managers = array())
{
parent::__construct();
$this->managers = $managers;
}
public function loadClassMetadata(EventArgs $eventArgs)
{
$ea = $this->getEventAdapter($eventArgs);
$this->loadMetadataForObjectClass(
$ea->getObjectManager(), $eventArgs->getClassMetadata()
);
}
public function postLoad(EventArgs $eventArgs)
{
$ea = $this->getEventAdapter($eventArgs);
$om = $ea->getObjectManager();
$object = $ea->getObject();
$meta = $om->getClassMetadata(get_class($object));
$config = $this->getConfiguration($om, $meta->name);
if (isset($config['referenceOne'])) {
foreach ($config['referenceOne'] as $mapping) {
$property = $meta->reflClass->getProperty($mapping['field']);
$property->setAccessible(true);
if (isset($mapping['identifier'])) {
$referencedObjectId = $meta->getFieldValue($object, $mapping['identifier']);
if (null !== $referencedObjectId) {
$property->setValue(
$object,
$ea->getSingleReference(
$this->getManager($mapping['type']),
$mapping['class'],
$referencedObjectId
)
);
}
}
}
}
if (isset($config['referenceMany'])) {
foreach ($config['referenceMany'] as $mapping) {
$property = $meta->reflClass->getProperty($mapping['field']);
$property->setAccessible(true);
if (isset($mapping['mappedBy'])) {
$id = $ea->extractIdentifier($om, $object);
$manager = $this->getManager($mapping['type']);
$class = $mapping['class'];
$refMeta = $manager->getClassMetadata($class);
$refConfig = $this->getConfiguration($manager, $refMeta->name);
if (isset($refConfig['referenceOne'][$mapping['mappedBy']])) {
$refMapping = $refConfig['referenceOne'][$mapping['mappedBy']];
$identifier = $refMapping['identifier'];
$property->setValue(
$object,
new LazyCollection(
function () use ($id, &$manager, $class, $identifier) {
$results = $manager
->getRepository($class)
->findBy(array(
$identifier => $id,
));
return new ArrayCollection((is_array($results) ? $results : $results->toArray()));
}
)
);
}
}
}
}
$this->updateManyEmbedReferences($eventArgs);
}
public function prePersist(EventArgs $eventArgs)
{
$this->updateReferences($eventArgs);
}
public function preUpdate(EventArgs $eventArgs)
{
$this->updateReferences($eventArgs);
}
public function getSubscribedEvents()
{
return array(
'postLoad',
'loadClassMetadata',
'prePersist',
'preUpdate',
);
}
public function registerManager($type, $manager)
{
$this->managers[$type] = $manager;
}
/**
* @param string $type
*
* @return ObjectManager
*/
public function getManager($type)
{
return $this->managers[$type];
}
protected function getNamespace()
{
return __NAMESPACE__;
}
private function updateReferences(EventArgs $eventArgs)
{
$ea = $this->getEventAdapter($eventArgs);
$om = $ea->getObjectManager();
$object = $ea->getObject();
$meta = $om->getClassMetadata(get_class($object));
$config = $this->getConfiguration($om, $meta->name);
if (isset($config['referenceOne'])) {
foreach ($config['referenceOne'] as $mapping) {
if (isset($mapping['identifier'])) {
$property = $meta->reflClass->getProperty($mapping['field']);
$property->setAccessible(true);
$referencedObject = $property->getValue($object);
if (is_object($referencedObject)) {
$manager = $this->getManager($mapping['type']);
$identifier = $ea->getIdentifier($manager, $referencedObject);
$meta->setFieldValue(
$object,
$mapping['identifier'],
$identifier
);
}
}
}
}
$this->updateManyEmbedReferences($eventArgs);
}
public function updateManyEmbedReferences(EventArgs $eventArgs)
{
$ea = $this->getEventAdapter($eventArgs);
$om = $ea->getObjectManager();
$object = $ea->getObject();
$meta = $om->getClassMetadata(get_class($object));
$config = $this->getConfiguration($om, $meta->name);
if (isset($config['referenceManyEmbed'])) {
foreach ($config['referenceManyEmbed'] as $mapping) {
$property = $meta->reflClass->getProperty($mapping['field']);
$property->setAccessible(true);
$id = $ea->extractIdentifier($om, $object);
$manager = $this->getManager('document');
$class = $mapping['class'];
$refMeta = $manager->getClassMetadata($class);
// Trigger the loading of the configuration to validate the mapping
$this->getConfiguration($manager, $refMeta->name);
$identifier = $mapping['identifier'];
$property->setValue(
$object,
new LazyCollection(
function () use ($id, &$manager, $class, $identifier) {
$results = $manager
->getRepository($class)
->findBy(array(
$identifier => $id,
));
return new ArrayCollection((is_array($results) ? $results : $results->toArray()));
}
)
);
}
}
}
}