<?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