Last active
March 2, 2016 11:14
-
-
Save bwaidelich/5056310 to your computer and use it in GitHub Desktop.
A simple example showing how doctrine behaviours (in this chase (nested) tree and soft-delete) can be used within TYPO3 Flow.
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 Your\Package\Domain\Repository; | |
use TYPO3\Flow\Annotations as Flow; | |
/** | |
* @Flow\Scope("singleton") | |
*/ | |
abstract class AbstractTreeRepository extends \Gedmo\Tree\Entity\Repository\NestedTreeRepository implements \TYPO3\Flow\Persistence\RepositoryInterface { | |
/** | |
* @var \TYPO3\Flow\Persistence\PersistenceManagerInterface | |
*/ | |
protected $persistenceManager; | |
/** | |
* @var \Doctrine\ORM\EntityManager | |
*/ | |
protected $entityManager; | |
/** | |
* Warning: if you think you want to set this, | |
* look at RepositoryInterface::ENTITY_CLASSNAME first! | |
* | |
* @var string | |
*/ | |
protected $objectType; | |
/** | |
* @var array | |
*/ | |
protected $defaultOrderings = array(); | |
/** | |
* Initializes a new Repository. | |
* | |
* @param \Doctrine\Common\Persistence\ObjectManager $entityManager The EntityManager to use. | |
* @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata The class descriptor. | |
*/ | |
public function __construct(\Doctrine\Common\Persistence\ObjectManager $entityManager, \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata = NULL) { | |
if ($classMetadata === NULL) { | |
if (static::ENTITY_CLASSNAME === NULL) { | |
$this->objectType = str_replace(array('\\Repository\\', 'Repository'), array('\\Model\\', ''), get_class($this)); | |
} else { | |
$this->objectType = static::ENTITY_CLASSNAME; | |
} | |
$classMetadata = $entityManager->getClassMetadata($this->objectType); | |
} | |
parent::__construct($entityManager, $classMetadata); | |
$this->entityManager = $this->_em; | |
} | |
/** | |
* Injects the persistence manager | |
* | |
* @param \TYPO3\Flow\Persistence\PersistenceManagerInterface $persistenceManager | |
* @return void | |
*/ | |
public function injectPersistenceManager(\TYPO3\Flow\Persistence\PersistenceManagerInterface $persistenceManager) { | |
$this->persistenceManager = $persistenceManager; | |
} | |
/** | |
* Returns the classname of the entities this repository is managing. | |
* | |
* @return string | |
* @api | |
*/ | |
public function getEntityClassName() { | |
return $this->objectType; | |
} | |
/** | |
* Adds an object to this repository. | |
* | |
* @param object $object The object to add | |
* @return void | |
* @api | |
*/ | |
public function add($object) { | |
$this->entityManager->persist($object); | |
} | |
/** | |
* Removes an object from this repository. | |
* | |
* @param object $object The object to remove | |
* @return void | |
* @api | |
*/ | |
public function remove($object) { | |
$this->entityManager->remove($object); | |
} | |
/** | |
* Finds all entities in the repository. | |
* | |
* @return \TYPO3\Flow\Persistence\QueryResultInterface The query result | |
* @api | |
*/ | |
public function findAll() { | |
return $this->createQuery()->execute(); | |
} | |
/** | |
* Finds an object matching the given identifier. | |
* | |
* @param mixed $identifier The identifier of the object to find | |
* @return object The matching object if found, otherwise NULL | |
* @api | |
*/ | |
public function findByIdentifier($identifier) { | |
return $this->entityManager->find($this->objectType, $identifier); | |
} | |
/** | |
* Returns a query for objects of this repository | |
* | |
* @return \TYPO3\Flow\Persistence\Doctrine\Query | |
* @api | |
*/ | |
public function createQuery() { | |
$query = new \TYPO3\Flow\Persistence\Doctrine\Query($this->objectType); | |
if ($this->defaultOrderings) { | |
$query->setOrderings($this->defaultOrderings); | |
} | |
return $query; | |
} | |
/** | |
* Counts all objects of this repository | |
* | |
* @return integer | |
* @api | |
*/ | |
public function countAll() { | |
return $this->createQuery()->count(); | |
} | |
/** | |
* Removes all objects of this repository as if remove() was called for | |
* all of them. | |
* | |
* @return void | |
* @api | |
* @todo maybe use DQL here, would be much more performant | |
*/ | |
public function removeAll() { | |
foreach ($this->findAll() AS $object) { | |
$this->remove($object); | |
} | |
} | |
/** | |
* Sets the property names to order results by. Expected like this: | |
* array( | |
* 'foo' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING, | |
* 'bar' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_DESCENDING | |
* ) | |
* | |
* @param array $defaultOrderings The property names to order by by default | |
* @return void | |
* @api | |
*/ | |
public function setDefaultOrderings(array $defaultOrderings) { | |
$this->defaultOrderings = $defaultOrderings; | |
} | |
/** | |
* Schedules a modified object for persistence. | |
* | |
* @param object $object The modified object | |
* @return void | |
* @throws \TYPO3\Flow\Persistence\Exception\IllegalObjectTypeException | |
* @api | |
*/ | |
public function update($object) { | |
if (!($object instanceof $this->objectType)) { | |
throw new \TYPO3\Flow\Persistence\Exception\IllegalObjectTypeException('The modified object given to update() was not of the type (' . $this->objectType . ') this repository manages.', 1249479625); | |
} | |
$this->persistenceManager->update($object); | |
} | |
} | |
?> |
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 Your\Package\Domain\Model; | |
use TYPO3\Flow\Annotations as Flow; | |
use Doctrine\ORM\Mapping as ORM; | |
use Gedmo\Mapping\Annotation as Gedmo; | |
/** | |
* A Category (Nested tree structure) | |
* | |
* @Flow\Entity | |
* @Gedmo\Tree(type="nested") | |
*/ | |
class Category { | |
/** | |
* @var string | |
*/ | |
protected $title; | |
/** | |
* @Gedmo\TreeLeft | |
* @var integer | |
*/ | |
protected $treeLeft; | |
/** | |
* @Gedmo\TreeLevel | |
* @var integer | |
*/ | |
protected $treeLevel; | |
/** | |
* @Gedmo\TreeRight | |
* @var integer | |
*/ | |
protected $treeRight; | |
/** | |
* @Gedmo\TreeRoot | |
* @ORM\Column(nullable=true) | |
* @var integer | |
*/ | |
protected $treeRoot; | |
/** | |
* @Gedmo\TreeParent | |
* @ORM\ManyToOne(inversedBy="treechildren") | |
* @ORM\Column(nullable=true) | |
* @var \Your\Package\Domain\Model\Category | |
*/ | |
protected $treeParent; | |
/** | |
* @ORM\OneToMany(mappedBy="treeparent") | |
* @ORM\OrderBy({"left" = "ASC"}) | |
* @var \Doctrine\Common\Collections\ArrayCollection<\Your\Package\Domain\Model\Category> | |
*/ | |
protected $treeChildren; | |
/** | |
* @param string $title | |
*/ | |
public function setTitle($title) { | |
$this->title = $title; | |
} | |
/** | |
* @return string | |
*/ | |
public function getTitle() { | |
return $this->title; | |
} | |
/** | |
* @param \Your\Package\Domain\Model\Category $treeParent | |
*/ | |
public function setTreeParent(\Your\Package\Category $treeParent) { | |
$this->treeParent = $treeParent; | |
} | |
/** | |
* @return \Your\Package\Domain\Model\Category | |
*/ | |
public function getTreeParent() { | |
return $this->treeParent; | |
} | |
} | |
?> |
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 Your\Package\Controller; | |
use TYPO3\Flow\Annotations as Flow; | |
use Your\Package\Domain\Model\Category; | |
/** | |
* Category controller | |
*/ | |
class CategoryController extends \TYPO3\Flow\Mvc\Controller\ActionController { | |
/** | |
* @var \Your\Package\Domain\Repository\CategoryRepository | |
* @Flow\Inject | |
*/ | |
protected $categoryRepository; | |
/** | |
* @return void | |
*/ | |
public function indexAction() { | |
$this->view->assign('categories', $this->categoryRepository->childrenHierarchy()); | |
} | |
/** | |
* @return void | |
*/ | |
public function addAction() { | |
$food = new Category(); | |
$food->setTitle('Food'); | |
$this->categoryRepository->add($food); | |
$fruits = new Category(); | |
$fruits->setTitle('Fruits'); | |
$fruits->setTreeParent($food); | |
$this->categoryRepository->add($fruits); | |
$vegetables = new Category(); | |
$vegetables->setTitle('Vegetables'); | |
$vegetables->setTreeParent($food); | |
$this->categoryRepository->add($vegetables); | |
$carrots = new Category(); | |
$carrots->setTitle('Carrots'); | |
$carrots->setTreeParent($vegetables); | |
$this->categoryRepository->add($carrots); | |
$this->redirect('index'); | |
} | |
} | |
?> |
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 Your\Package\Domain\Repository; | |
use TYPO3\Flow\Annotations as Flow; | |
/** | |
* @Flow\Scope("singleton") | |
*/ | |
class CategoryRepository extends \Your\Package\Domain\Repository\AbstractTreeRepository { | |
} | |
?> |
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
{ | |
... | |
"require": { | |
"typo3/flow": "2.0.*", | |
"gedmo/doctrine-extensions": "dev-master" | |
}, | |
... | |
} |
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 Your\Package\Aop; | |
use TYPO3\Flow\Annotations as Flow; | |
/** | |
* "Hooks into" creation of doctrine EntityManager in order to register Behaviours | |
* from the "gedmo/doctrine-extensions" package. | |
* Note: Currently only "soft-delete" and "tree" behaviours are supported | |
* | |
* @Flow\Aspect | |
*/ | |
class EntityManagerFactoryAspect { | |
/** | |
* @var \TYPO3\Flow\Reflection\ReflectionService | |
* @Flow\Inject | |
*/ | |
protected $reflectionService; | |
/** | |
* @Flow\Around("method(TYPO3\Flow\Persistence\Doctrine\EntityManagerFactory->create())") | |
* @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current join point | |
* @return \Doctrine\ORM\EntityManager | |
*/ | |
public function registerDoctrineBehaviors(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { | |
/** @var $entityManager \Doctrine\ORM\EntityManager */ | |
$entityManager = $joinPoint->getAdviceChain()->proceed($joinPoint); | |
/** @var $eventManager \Doctrine\Common\EventManager */ | |
$eventManager = $entityManager->getEventManager(); | |
$classNamesAnnotatedAsDeletable = $this->reflectionService->getClassNamesByAnnotation('Gedmo\Mapping\Annotation\SoftDeleteable'); | |
$classNamesAnnotatedAsTree = $this->reflectionService->getClassNamesByAnnotation('Gedmo\Mapping\Annotation\Tree'); | |
if (!is_array($classNamesAnnotatedAsDeletable) && !is_array($classNamesAnnotatedAsTree)) { | |
return $entityManager; | |
} | |
foreach ($classNamesAnnotatedAsDeletable as $index => $className) { | |
if (!$this->reflectionService->isClassAnnotatedWith($className, 'TYPO3\Flow\Annotations\Entity')) { | |
unset($classNamesAnnotatedAsDeletable[$index]); | |
} | |
} | |
foreach ($classNamesAnnotatedAsTree as $index => $className) { | |
if (!$this->reflectionService->isClassAnnotatedWith($className, 'TYPO3\Flow\Annotations\Entity')) { | |
unset($classNamesAnnotatedAsTree[$index]); | |
} | |
} | |
if ($classNamesAnnotatedAsDeletable !== array()) { | |
$doctrineConfiguration = $entityManager->getConfiguration(); | |
$doctrineConfiguration->addFilter('soft-deletable', 'Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter'); | |
$entityManager->getFilters()->enable('soft-deletable'); | |
$softDeletableListener = new \Gedmo\SoftDeleteable\SoftDeleteableListener(); | |
$eventManager->addEventSubscriber($softDeletableListener); | |
} | |
if ($classNamesAnnotatedAsTree !== array()) { | |
$treeListener = new \Gedmo\Tree\TreeListener(); | |
$eventManager->addEventSubscriber($treeListener); | |
} | |
return $entityManager; | |
} | |
} | |
?> |
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
<!DOCTYPE html> | |
<html> | |
<body> | |
<h1>Categories</h1> | |
<ul class="t3-content-navigation"> | |
<f:render section="categoryTree" arguments="{categories: categories}" /> | |
</ul> | |
<f:section name="categoryTree"> | |
<f:for each="{categories}" as="category"> | |
<li> | |
{category.title} ({category.treeLevel}) | |
<f:if condition="{category.__children}"> | |
<ul> | |
<f:render section="categoryTree" arguments="{categories: category.__children}" /> | |
</ul> | |
</f:if> | |
</li> | |
</f:for> | |
</f:section> | |
<f:link.action action="add">Add new tree</f:link.action> | |
</body> | |
</html> |
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
TYPO3: | |
Flow: | |
# disable reflection for non psr-0 compliant 3rd party packages | |
object: | |
excludeClasses: | |
'gedmo.doctrineextensions' : ['Gedmo\\.*'] |
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 Your\Package\Domain\Model; | |
use TYPO3\Flow\Annotations as Flow; | |
use Doctrine\ORM\Mapping as ORM; | |
use Gedmo\Mapping\Annotation as Gedmo; | |
/** | |
* An example entity with soft-delete behaviour | |
* | |
* @Flow\Entity | |
* @Gedmo\SoftDeleteable(fieldName="deletedAt") | |
*/ | |
class SoftDeletableEntity { | |
/** | |
* @var string | |
*/ | |
protected $title; | |
/** | |
* DateTime when this entity was deleted (used for "Soft-Delete behaviour") | |
* | |
* @var \DateTime | |
* @ORM\Column(nullable=true) | |
*/ | |
protected $deletedAt; | |
/** | |
* @return \DateTime | |
*/ | |
public function getDeletedAt() { | |
return $this->deletedAt; | |
} | |
/** | |
* @param \DateTime $deletedAt | |
* @return void | |
*/ | |
public function setDeletedAt(\DateTime $deletedAt) { | |
$this->deletedAt = $deletedAt; | |
} | |
/** | |
* @return string The Document's title | |
*/ | |
public function getTitle() { | |
return $this->title; | |
} | |
/** | |
* Sets this Document's title | |
* | |
* @param string $title The Document's title | |
* @return void | |
*/ | |
public function setTitle($title) { | |
$this->title = $title; | |
} | |
} | |
?> |
@svparijs sorry, only saw your comment now.. If this is still a problem please flush the caches (if that gives the same exception use ./flow typo3.flow:cache:flush --force
Those annotations should be ignored with the provided Settings.yaml
FYI: With the latest version of Flow (and a change that is currently still under review) this will get very easy: https://gist.github.com/bwaidelich/9617211
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice gist, i'm trying to implement it but i'm running into trouble during the compile step i get an error:
Uncaught Exception
Execution of subprocess failed with exit code 1 and output:
Uncaught Exception
[Semantical Error] The annotation "@gedmo\Blameable" in property
Gedmo\Blameable\Traits\BlameableDocument::$createdBy was never
imported.
Did you maybe forget to add a "use" statement for this annotation?
More Information
Exception code #0
File
/Users/Shared/www/Kernkaart/Packages/Libraries/doctrine/common/lib/Doctrine/Common/Annotations/AnnotationException.php
line 52
Any idea's?