-
-
Save randm-ch/adcacf8b9390aa563b38 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace MyCompany\MyApplication\Utility; | |
use TYPO3\Flow\Annotations as Flow; | |
use MyCompany\MyApplication\Exception; | |
/** | |
* @Flow\Scope("singleton") | |
*/ | |
class DataClassReflection { | |
/** | |
* @Flow\Inject | |
* @var \TYPO3\Flow\Reflection\ReflectionService | |
*/ | |
protected $reflectionService; | |
/** | |
* @var \Doctrine\Common\Persistence\ObjectManager | |
* @Flow\Inject | |
*/ | |
protected $entityManager; | |
/** | |
* @Flow\Inject | |
* @var \TYPO3\Flow\Persistence\PersistenceManagerInterface | |
*/ | |
protected $persistenceManager; | |
/** | |
* @var array | |
*/ | |
protected $fetchedClasses = array(); | |
/** | |
* Returns an array of the configured properties of the provided class and all values stored in the Data objects. | |
* | |
* @param string $className | |
* @param integer $depth Provide if you want to limit the recursion depth | |
* @return array | |
*/ | |
public function getProperties($className, $depth = 0) { | |
if(strpos($className, '\\') === 0) $className = substr($className, 1); | |
return $this->getPropertiesRecursive($className, $depth); | |
} | |
/** | |
* Returns an array of the configured properties of the provided class and all values stored in the Data objects. | |
* | |
* @param string $className | |
* @param integer $depth Provide if you want to limit the recursion depth | |
* @param integer $currentDepth For internal use only | |
* @param string $propertyPath For internal use only | |
* @param boolean $isRecursive For internal use only | |
* @return array | |
* @throws \TYPO3\Flow\Object\Exception\InvalidClassException | |
*/ | |
protected function getPropertiesRecursive($className, $depth = 0, $currentDepth = 0, $propertyPath = NULL, $isRecursive = FALSE) { | |
if($depth !== 0 && $depth <= $currentDepth) return array(); | |
if($isRecursive === FALSE) $this->fetchedClasses = array(); | |
if(in_array($className, $this->fetchedClasses)) return array(); | |
$this->fetchedClasses[] = $className; | |
$properties = array(); | |
foreach($this->reflectionService->getClassPropertyNames($className) as $property) { | |
if(!in_array($property, $this->getForbiddenProperties())) { | |
$reflection = new \TYPO3\Flow\Reflection\PropertyReflection($className, $property); | |
if($reflection->isTaggedWith('onetoone') || $reflection->isTaggedWith('manytoone') || $reflection->isTaggedWith('onetomany') || $reflection->isTaggedWith('manytomany')) { | |
$varDefinition = $reflection->getTagValues('var'); | |
$currentPropertyPath = (is_string($propertyPath) && strlen($propertyPath) > 0) ? $propertyPath.$property.'.' : $property.'.'; | |
$properties = array_merge($this->getPropertiesRecursive($varDefinition[0], $depth, $currentDepth+1, $currentPropertyPath, TRUE), $properties); | |
} | |
else { | |
$properties[] = $propertyPath.$property; | |
} | |
} | |
} | |
if(in_array($className, $this->reflectionService->getAllSubClassNamesForClass('MyCompany\MyApplication\Domain\Model\AbstractDataModel'))) { | |
$dataQuery = $this->entityManager->createQuery('SELECT DISTINCT(d.identifier) FROM '.$className.' a JOIN a.data d WHERE d IS NOT NULL'); | |
foreach($dataQuery->execute() as $result) { | |
$properties[] = $propertyPath.$result['identifier']; | |
} | |
} | |
return $properties; | |
} | |
/** | |
* Checks if all of the properties provided in $propertyPath are real class properties. | |
* If a dynamic Data propery is found, this method returns FALSE. | |
* | |
* @param string $className | |
* @param string $propertyPath | |
* @return boolean | |
*/ | |
public function isRealPropertyOfClass($className, $propertyPath) { | |
$properties = explode('.', $propertyPath); | |
foreach($properties as $property) { | |
if(in_array($property, $this->reflectionService->getClassPropertyNames($className))) { | |
$reflection = new \TYPO3\Flow\Reflection\PropertyReflection($className, $property); | |
if($reflection->isTaggedWith('onetoone') || $reflection->isTaggedWith('manytoone') || $reflection->isTaggedWith('onetomany') || $reflection->isTaggedWith('manytomany')) { | |
$varDefinition = $reflection->getTagValues('var'); | |
$className = $varDefinition[0]; | |
} | |
else return TRUE; | |
} | |
else return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
* Returns an array of the configured properties of the provided object. | |
* | |
* @param \MyCompany\MyApplication\Domain\Model\AbstractDataModel $object | |
* @param array $configuration | |
* @return array | |
*/ | |
public function getPropertiesAsArray($object, $configuration = array()) { | |
$properties = $this->getProperties(self::getClassNameFromProxyClassName(get_class($object))); | |
foreach($properties as $i => $property) { | |
$properties[$property] = $this->traverseObject($object, $property); | |
unset($properties[$i]); | |
} | |
if(count($configuration) > 0) { | |
$array = array(); | |
foreach($configuration as $property) { | |
if(isset($properties[$property])) $array[$property] = $properties[$property]; | |
else $array[$property] = NULL; | |
} | |
return $array; | |
} | |
return $properties; | |
} | |
/** | |
* Traverses the given object along the path, which is divided by full stops. | |
* Returns the value of the property at the end of the path or NULL if path | |
* destination was not found. | |
* | |
* @param \MyCompany\MyApplication\Domain\Model\AbstractDataModel $object | |
* @param string $propertyPath | |
* @param string $format If provided, \DateTime objects will automatically be returned as formatted strings | |
* @return mixed | |
*/ | |
public function traverseObject($object, $propertyPath, $format = 'd.m.Y') { | |
$properties = explode('.', $propertyPath); | |
foreach($properties as $property) { | |
if(is_object($object)) { | |
try { | |
$method = 'get'.ucfirst($property); | |
$object = $object->$method(); | |
} | |
catch(Exception\PropertyNotFoundException $e) { | |
return NULL; | |
} | |
} | |
else { | |
return NULL; | |
} | |
} | |
if(is_string($format) && strlen($format) > 0 && $object instanceof \DateTime) { | |
$object = $object->format($format); | |
} | |
return $object; | |
} | |
/** | |
* Returns a lowercase version of the class name, without namespace | |
* | |
* @param string $className | |
* @return string | |
*/ | |
static public function getPropertyNameFromClassname($className) { | |
return strtolower(substr($className, strrpos($className, '\\')+1)); | |
} | |
/** | |
* Gets the class name from a collection name with generic type definition like Collection<\The\Class\Name> | |
* | |
* @param $collectionName | |
* @return string | |
*/ | |
static public function getGenericTypeOfCollection($collectionName) { | |
if(strpos($collectionName, '<') !== FALSE) { | |
return substr($collectionName, strpos($collectionName, '<')+1, strpos($collectionName, '>')-strpos($collectionName, '<')-1); | |
} | |
return $collectionName; | |
} | |
/** | |
* @param string $proxyClassName | |
* @return string | |
*/ | |
static public function getClassNameFromProxyClassName($proxyClassName) { | |
return str_replace('TYPO3\Flow\Persistence\Doctrine\Proxies\__CG__', '', $proxyClassName); | |
} | |
/** | |
* @return array | |
*/ | |
static public function getForbiddenProperties() { | |
return array( | |
'settings', | |
'data', | |
'dataRepository' | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment