Last active
February 15, 2016 09:07
Revisions
-
chvonrohr revised this gist
Apr 24, 2015 . 2 changed files with 31 additions and 18 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -43,8 +43,8 @@ public function copy($object) { $properties = $this->reflectionService->getClassPropertyNames($className); foreach ($properties as $propertyName) { $propertyAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Frontal\Cockpit\Annotations\Copy'); if (!$propertyAnnotation) { // ignore if property has no "copy" annotation continue; } @@ -54,13 +54,16 @@ public function copy($object) { $setter = 'set' . ucfirst($propertyName); $originalValue = $object->$getter(); // copy as reference if ($copyMethod == 'reference') { $copiedValue = $this->copyAsReference($originalValue); // clone value itself (if its a model, it will copied again by annotations) } else { $copiedValue = $this->copyAsClone($originalValue); } if ($copiedValue != NULL) { $copy->$setter($copiedValue); } @@ -70,37 +73,44 @@ public function copy($object) { } protected function copyAsReference($value) { // collection if ($value instanceof \Doctrine\ORM\PersistentCollection) { $newStorage = new \Doctrine\Common\Collections\ArrayCollection(); foreach ($value as $item) { $newStorage->attach($item); } return $newStorage; // model } else if ($value instanceof Doctrine\ORM\ProxyInterface) { return $value; // other object } else if (is_object($value)) { // fallback case for class copying - value objects and such return $value; } else { // this case is very unlikely: means someone wished to copy hard type as a reference - so return a copy instead return $value; } } protected function copyAsClone($value) { // collection if ($value instanceof \Doctrine\ORM\PersistentCollection) { $newStorage = new \Doctrine\Common\Collections\ArrayCollection(); foreach ($value as $item) { $newItem = $this->copy($item); $newStorage->add($newItem); } return $newStorage; // model } else if ($value instanceof \Doctrine\ORM\ProxyInterface) { return $this->copy($value); // other object } else if (is_object($value)) { // fallback case for class copying - value objects and such return clone $value; This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -25,21 +25,24 @@ class Example { /** * @var \Foo\Bar\Domain\Model\ExampleChild * @ORM\OneOne(cascade={"persist", "remove"}) * @CP\Copy * => copy related object */ protected $child; /** * @var \Doctrine\Common\Collections\ArrayCollection<\Foo\Bar\Domain\Model\ExampleChild> * @ORM\OneToMany(mappedBy="project",cascade={"persist", "remove"}) * @CP\Copy * => copy related objects */ protected $childs; /** * Important: Set cascade=persist * @var \Doctrine\Common\Collections\ArrayCollection<\Foo\Bar\Domain\Model\MmChilds> * @ORM\ManyToMany(mappedBy="example",cascade={"persist", "remove"}) * @CP\Copy(type="reference") * => copy reference to existing objects */ -
chvonrohr revised this gist
Apr 23, 2015 . 1 changed file with 40 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,40 @@ <?php namespace Foo\Bar\Domain\Repository; use TYPO3\Flow\Annotations as Flow; use TYPO3\Flow\Persistence\Repository; /** * @Flow\Scope("singleton") */ class ExampleRepository extends Repository { /** * @var \TYPO3\Flow\Object\ObjectManagerInterface * @Flow\Inject */ protected $objectManager; /** * clone existing example * * @param \Foo\Bar\Domain\Model\Example $example * @return \Foo\Bar\Domain\Model\Example cloned example */ public function copy($example) { // clone project $copyService = $this->objectManager->get("Foo\Bar\Service\CopyService"); $clonedExample = $copyService->copy($example); // set title "xxx (copy)" $clonedExample->setTitle( $clonedExample->getTitle() . " (Copy)"); // persist $this->add($clonedExample); $this->persistenceManager->persistAll(); return $clonedExample; } } -
chvonrohr revised this gist
Apr 23, 2015 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -23,10 +23,10 @@ class CopyService { protected $reflectionService; /** * @var \TYPO3\Flow\Object\ObjectManagerInterface * @Flow\Inject */ protected $objectManager; /** -
chvonrohr revised this gist
Apr 23, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -8,7 +8,7 @@ final class Copy { /** * type of copy {empty, 'reference'} * @var string */ public $type; -
chvonrohr created this gist
Apr 23, 2015 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ <?php namespace Foo\Bar\Annotations; /** * @Annotation * @Target("PROPERTY") */ final class Copy { /** * Ember type * @var string */ public $type; } This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,115 @@ <?php namespace Foo\Bar\Service; use TYPO3\Flow\Annotations as Flow; /** * @Flow\Scope("singleton") */ class CopyService { /** * @var \Foo\Cockpit\Service\RecursionService * @Flow\Inject */ public $recursionService; /** * @var \TYPO3\Flow\Reflection\ReflectionService * @Flow\Inject */ protected $reflectionService; /** * @var \TYPO3\Flow\Object\ObjectManagerInterface * @Flow\Inject */ protected $objectManager; /** * Copy a single object based on field annotations about how to copy the object * * @return $copy */ public function copy($object) { $className = get_class($object); $this->recursionService->in(); $this->recursionService->check($className); $copy = $this->objectManager->get($className); $properties = $this->reflectionService->getClassPropertyNames($className); foreach ($properties as $propertyName) { //$tags = $this->reflectionService->getPropertyTagsValues($className, $propertyName); $propertyAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Frontal\Cockpit\Annotations\Copy'); if (!$propertyAnnotation) { // ignore if property has no "copy" annotation continue; } $copyMethod = $propertyAnnotation->type; $getter = 'get' . ucfirst($propertyName); $setter = 'set' . ucfirst($propertyName); $originalValue = $object->$getter(); if ($copyMethod == 'reference') { // copy as reference $copiedValue = $this->copyAsReference($originalValue); } else { // clone value itself (if its a model, it will copied again by annotations) $copiedValue = $this->copyAsClone($originalValue); } if ($copiedValue != NULL) { $copy->$setter($copiedValue); } } $this->recursionService->out(); return $copy; } protected function copyAsReference($value) { $objectManager = $this->objectManager; if ($value instanceof Doctrine\Common\Collections\ArrayCollection) { //TODO: more general class? traversable? // collection; copy collection and attach items to this new collection // if 1:n mapping is used, items are detached from their old collection - this is // a limitation of this type of reference $newStorage = $this->objectManager->get('Doctrine\Common\Collections\ArrayCollection'); foreach ($value as $item) { $newStorage->attach($item); } return $newStorage; //} else if ($value instanceof Tx_Locus_Domain_Model_AbstractDomainModelObject) { // 1:1 mapping as reference; return object itself // return $value; } else if (is_object($value)) { // fallback case for class copying - value objects and such return $value; } else { // this case is very unlikely: means someone wished to copy hard type as a reference - so return a copy instead return $value; } } protected function copyAsClone($value) { if ($value instanceof Doctrine\Common\Collections\ArrayCollection) { // collection; copy storage and copy items, return new storage $newStorage = $this->objectManager->get('Doctrine\Common\Collections\ArrayCollection'); foreach ($value as $item) { $newItem = $this->copy($item); $newStorage->attach($newItem); } return $newStorage; } else if (is_object($value)) { // fallback case for class copying - value objects and such return clone $value; } else { // value is probably a string return $value; } } } ?> This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,53 @@ <?php namespace Foo\Bar\Domain\Model; use TYPO3\Flow\Annotations as Flow; use Foo\Bar\Annotations as CP; /** * @Flow\Entity */ class Example { /** * @var string * @CP\Copy * => copy content */ protected $title; /** * @var \Foo\Bar\Domain\Model\ExampleCategory * @CP\Copy(type="reference") * => keep reference to relation in copy */ protected $category; /** * @var \Foo\Bar\Domain\Model\ExampleChild * @CP\Copy * => copy related object */ protected $child; /** * @var \Doctrine\Common\Collections\ArrayCollection<\Foo\Bar\Domain\Model\ExampleChild> * @CP\Copy * => copy related objects */ protected $childs; /** * @var \Doctrine\Common\Collections\ArrayCollection<\Foo\Bar\Domain\Model\MmChilds> * @ORM\ManyToMany * @CP\Copy(type="reference") * => copy reference to existing objects */ protected $mmChilds; // ... } ?>
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,212 @@ <?php namespace Foo\Bar\Service; use TYPO3\Flow\Annotations as Flow; /** * @Flow\Scope("singleton") */ class RecursionService { /** * @var string */ private $_exceptionMessage = 'Recursion problem occurred'; /** * @var int */ private $_level = 0; /** * @var int */ private $_maxLevel = 16; /** * @var int */ private $_maxEncounters = 1; /** * @var array */ private $_encountered = array(); /** * @var boolean */ private $_autoReset = FALSE; /** * Set the message used to prepend Exceptions * @param string $msg */ public function setExceptionMessage($msg) { $this->_exceptionMessage = $msg; } /** * Get the message used to prepend Exceptions * @return string */ public function getExceptionMessage() { return $this->_exceptionMessage; } /** * Set automatic resetting of encounters and level (TRUE/FALSE) * @param boolean $reset */ public function setAutoReset($reset) { $this->_autoReset = $reset; } /** * Set the maximum allowed number of times a particular identifier may be encountered before an Exception is thrown * @param unknown_type $max */ public function setMaxEncounters($max) { $this->_maxEncounters = $max; } /** * Get the maximum allowed number of encounters * @return int */ public function getMaxEncounters() { return $this->_maxEncounters; } /** * Set the maximum allowed recursion level * @param int $level */ public function setMaxLevel($level) { $this->_maxLevel = $level; } /** * Get the maximum allowed recursion level * @return int */ public function getMaxLevel() { return $this->_maxLevel; } /** * Get the current recursion level * @return int */ public function getLevel() { return $this->_level; } /** * Get the identifier last encountered * @return mixed */ public function getLastEncounter() { return array_pop($this->_encountered); } /** * Increase recursion level (start of implementer function) */ public function in() { $this->_level++; } /** * Decrease recursion level (end of implementer function) */ public function out() { $this->_level--; } /** * Encounter $data (usually a string), call this when new values are read in your recursive function * @param mixed $data */ public function encounter($data) { array_push($this->_encountered, $data); $this->check(); } /** * Check the current recursion level and encounter status. Call in each iteration of your function * @param string $exitMsg */ public function check($exitMsg='<no message>') { $level = $this->getLevel(); $maxEnc = $this->getMaxEncounters(); $message = $this->getExceptionMessage(); if ($this->failsOnLevel()) { $msg = "{$message} at level {$level} with message: {$exitMsg}"; throw new Exception($msg); } if ($this->failsOnMaxEncounters()) { $msg = "{$message} at encounter {$maxEnc} of {$maxEnc} allowed with message: {$exitMsg}"; $this->throwException($msg); } return TRUE; } /** * Reset all counters */ public function reset() { $this->_level = 0; $this->_encountered = array(); } /** * Throw an Exception - wrapper; check for auto-reset and reset if needed * @param string $message * @throws Exception */ private function throwException($message) { if ($this->_autoReset === TRUE) { $this->reset(); } throw new Exception($message); } /** * Check if the current iteration violates level restraints * @return boolean */ private function failsOnLevel() { $level = $this->getLevel(); $max = $this->getMaxLevel(); return (bool) ($level >= $max); } /** * Check if the current iteration violates encounter restraints * @return boolean */ private function failsOnMaxEncounters() { $lastEncounter = $this->getLastEncounter(); $occurrences = $this->countEncounters($lastEncounter); $max = $this->getMaxEncounters(); return (bool) ($occurrences > $max); } /** * Count number of times the identifier $encounter has been encountered * @param mixed $encounter * @return int */ private function countEncounters($encounter) { $num = 0; foreach ($this->_encountered as $encountered) { if ($encountered === $encounter) { $num++; } } return (int) $num; } } ?>