-
Notifications
You must be signed in to change notification settings - Fork 8
PHPCR-ODM enhancer #27
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
language: php | ||
|
||
php: | ||
- 5.5 | ||
- 5.6 | ||
- 7.0 | ||
- hhvm | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,21 +19,25 @@ final class Descriptor | |
{ | ||
/** | ||
* Alias for the resource type for example `app.page`. | ||
* Value should be a scalar string. | ||
*/ | ||
const TYPE_ALIAS = 'type.alias'; | ||
|
||
/** | ||
* Humanized representation of the resource type. | ||
* Value should be a scalar string. | ||
*/ | ||
const TYPE_TITLE = 'type.title'; | ||
|
||
/** | ||
* Title of the actual payload, e.g. "My Blog Post". | ||
* Value should be a scalar string. | ||
*/ | ||
const PAYLOAD_TITLE = 'title'; | ||
|
||
/** | ||
* Descriptors for HTML links. | ||
* Values should be either a valid URI. | ||
*/ | ||
const LINK_EDIT_HTML = 'link.edit.html'; | ||
const LINK_CREATE_HTML = 'link.create.html'; | ||
|
@@ -42,13 +46,34 @@ final class Descriptor | |
const LINK_SHOW_HTML = 'link.show.html'; | ||
const LINK_LIST_HTML = 'link.list.html'; | ||
|
||
/** | ||
* Array of links for creating child resources. | ||
*/ | ||
const LINKS_CREATE_CHILD_HTML = 'links.create_child.html'; | ||
|
||
/** | ||
* Descriptors for REST links. | ||
* Values should be either a valid URI. | ||
*/ | ||
const LINK_EDIT_REST = 'link.edit.rest'; | ||
const LINK_CREATE_REST = 'link.create.rest'; | ||
const LINK_UPDATE_REST = 'link.update.rest'; | ||
const LINK_REMOVE_REST = 'link.remove.rest'; | ||
const LINK_SHOW_REST = 'link.show.rest'; | ||
const LINK_LIST_REST = 'link.show.rest'; | ||
|
||
/** | ||
* Permitted children types for this resource. | ||
* Value should be a scalar array, e.g. [ `stdClass`, `FooClass` ]. | ||
* | ||
* NOTE: This should be an explicit list of types and should not include | ||
* interfaces are abstract class names. | ||
*/ | ||
const CHILDREN_TYPES = 'children.types'; | ||
|
||
/** | ||
* If children are allowed to be added to this resource. | ||
* Value should be a boolean. | ||
*/ | ||
const CHILDREN_ALLOW = 'children.allow'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See #25 for a possible alternative to this system of enhancement by using vendor-provided objects as descriptors. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony CMF package. | ||
* | ||
* (c) 2011-2015 Symfony CMF | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Cmf\Component\Resource\Description\Enhancer\Doctrine; | ||
|
||
use Symfony\Cmf\Component\Resource\Description\DescriptionEnhancerInterface; | ||
use Symfony\Cmf\Component\Resource\Description\Description; | ||
use Puli\Repository\Api\Resource\PuliResource; | ||
use Symfony\Cmf\Component\Resource\Repository\Resource\CmfResource; | ||
use Symfony\Cmf\Component\Resource\Description\Descriptor; | ||
use Doctrine\ODM\PHPCR\Mapping\ClassMetadataFactory; | ||
use Doctrine\Common\Util\ClassUtils; | ||
|
||
/** | ||
* Add descriptors from the Doctrine PHPCR ODM. | ||
* | ||
* @author Daniel Leech <[email protected]> | ||
*/ | ||
class PhpcrOdmEnhancer implements DescriptionEnhancerInterface | ||
{ | ||
private $metadataFactory; | ||
|
||
public function __construct(ClassMetadataFactory $metadataFactory) | ||
{ | ||
$this->metadataFactory = $metadataFactory; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function enhance(Description $description) | ||
{ | ||
$metadata = $this->metadataFactory->getMetadataFor($description->getResource()->getPayloadType()); | ||
$childClasses = $metadata->getChildClasses(); | ||
$childTypes = []; | ||
|
||
// explode the allowed types into concrete classes | ||
foreach ($this->metadataFactory->getAllMetadata() as $childMetadata) { | ||
foreach ($childClasses as $childClass) { | ||
if ($childClass == $childMetadata->name || $childMetadata->getReflectionClass()->isSubclassOf($childClass)) { | ||
$childTypes[] = $childMetadata->name; | ||
} | ||
} | ||
} | ||
|
||
$description->set(Descriptor::CHILDREN_ALLOW, !$metadata->isLeaf()); | ||
$description->set(Descriptor::CHILDREN_TYPES, $childTypes); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function supports(PuliResource $resource) | ||
{ | ||
if (false === $resource instanceof CmfResource) { | ||
return false; | ||
} | ||
|
||
return $this->metadataFactory->hasMetadataFor(ClassUtils::getRealClass($resource->getPayloadType())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,9 @@ | |
use Sylius\Bundle\ResourceBundle\Controller\RequestConfigurationFactory; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Cmf\Component\Resource\Description\Descriptor; | ||
use Symfony\Component\Routing\Exception\RouteNotFoundException; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Sylius\Component\Resource\Metadata\Metadata; | ||
|
||
/** | ||
* Add descriptors from the Sylius Resource component. | ||
|
@@ -81,13 +84,26 @@ public function enhance(Description $description) | |
]; | ||
|
||
foreach ($map as $descriptor => $action) { | ||
$url = $this->urlGenerator->generate( | ||
$configuration->getRouteName($action), | ||
[ | ||
'id' => $payload->getId(), | ||
] | ||
); | ||
$description->set($descriptor, $url); | ||
|
||
// note that some resources may not have routes | ||
// registered with sonata (f.e. folder resources) | ||
// so we ignore route-not-found exceptions. | ||
try { | ||
$url = $this->urlGenerator->generate( | ||
$configuration->getRouteName($action), | ||
[ | ||
'id' => $payload->getId(), | ||
] | ||
); | ||
$description->set($descriptor, $url); | ||
} catch (RouteNotFoundException $e) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some resources may not have routes (they may not be administrable, for example a folder resource). I wonder if there is a better way to handle this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if its that bad. it could hide issues though, if i misconfigured something and then i will just not get a link instead of an exception that makes me notice i mistyped a name somewhere. could configuration->getRouteName decide if there is a route or not, so that you could do an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sadly not -- its just concatenates some of the metadata string attributes and hopes for the best. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But this should be tested and I will have a closer look at it again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even if there were a "has route" the result would be the same: the descriptor would not be added. So I am OK with this solution. |
||
} | ||
} | ||
|
||
// if a previous enhancer has set the children types descriptor, then | ||
// we can generate the LINKS_CREATE_CHILD_HTML descriptor. | ||
if ($description->has(Descriptor::CHILDREN_TYPES)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add comment |
||
$this->processChildrenTypes($description, $metadata, $request, $payload); | ||
} | ||
} | ||
|
||
|
@@ -108,4 +124,28 @@ public function supports(PuliResource $resource) | |
|
||
return true; | ||
} | ||
|
||
private function processChildrenTypes(Description $description, Metadata $metadata, Request $request, $payload) | ||
{ | ||
$childClasses = $description->get(Descriptor::CHILDREN_TYPES); | ||
$childLinks = []; | ||
|
||
foreach ($childClasses as $childClass) { | ||
try { | ||
$metadata = $this->registry->getByClass($childClass); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a PR to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} catch (\InvalidArgumentException $e) { | ||
continue; | ||
} | ||
|
||
$configuration = $this->requestConfigurationFactory->create($metadata, $request); | ||
|
||
$url = $this->urlGenerator->generate( | ||
$configuration->getRouteName('create') | ||
); | ||
|
||
$childLinks[$metadata->getAlias()] = $url.'?parent='.$payload->getId(); | ||
} | ||
|
||
$description->set(Descriptor::LINKS_CREATE_CHILD_HTML, $childLinks); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony CMF package. | ||
* | ||
* (c) 2011-2015 Symfony CMF | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Cmf\Component\Resource\Tests\Unit\Description\Enhancer\Doctrine; | ||
|
||
use Symfony\Cmf\Component\Resource\Description\Description; | ||
use Symfony\Cmf\Component\Resource\Repository\Resource\CmfResource; | ||
use Symfony\Cmf\Component\Resource\Description\Descriptor; | ||
use Doctrine\ODM\PHPCR\Mapping\ClassMetadataFactory; | ||
use Symfony\Cmf\Component\Resource\Description\Enhancer\Doctrine\PhpcrOdmEnhancer; | ||
use Puli\Repository\Api\Resource\PuliResource; | ||
use Doctrine\ODM\PHPCR\Mapping\ClassMetadata; | ||
|
||
class PhpcrOdmEnhancerTest extends \PHPUnit_Framework_TestCAse | ||
{ | ||
private $metadataFactory; | ||
private $enhancer; | ||
private $cmfResource; | ||
private $puliResource; | ||
private $odmMetadata; | ||
private $description; | ||
|
||
public function setUp() | ||
{ | ||
$this->metadataFactory = $this->prophesize(ClassMetadataFactory::class); | ||
$this->enhancer = new PhpcrOdmEnhancer($this->metadataFactory->reveal()); | ||
|
||
$this->cmfResource = $this->prophesize(CmfResource::class); | ||
$this->puliResource = $this->prophesize(PuliResource::class); | ||
$this->odmMetadata = $this->prophesize(ClassMetadata::class); | ||
$this->description = $this->prophesize(Description::class); | ||
} | ||
|
||
/** | ||
* It should return true it supports a given resource. | ||
*/ | ||
public function testSupportsResource() | ||
{ | ||
$this->cmfResource->getPayloadType()->willReturn(\stdClass::class); | ||
$this->metadataFactory->hasMetadataFor(\stdClass::class)->willReturn(true); | ||
|
||
$result = $this->enhancer->supports($this->cmfResource->reveal()); | ||
$this->assertTrue($result); | ||
} | ||
|
||
/** | ||
* It should return false if the resource is not an instance of CmfResource. | ||
*/ | ||
public function testNotSupportsNonCmfResource() | ||
{ | ||
$this->assertFalse( | ||
$this->enhancer->supports($this->puliResource->reveal()) | ||
); | ||
} | ||
|
||
/** | ||
* It should return false if the resource is not known by the PHPCR-ODM metadata factory. | ||
*/ | ||
public function testNotSupportsNotSupportedByPhpcrOdm() | ||
{ | ||
$this->cmfResource->getPayloadType()->willReturn(\stdClass::class); | ||
$this->metadataFactory->hasMetadataFor(\stdClass::class)->willReturn(false); | ||
|
||
$this->assertFalse( | ||
$this->enhancer->supports($this->cmfResource->reveal()) | ||
); | ||
} | ||
|
||
/** | ||
* It should enhance the description with the child mapping information from the PHPCR-ODM metadata. | ||
*/ | ||
public function testEnhanceDescription() | ||
{ | ||
// object the implements an allowed interface | ||
$mappedObject1 = $this->prophesize(); | ||
$mappedObject1->willImplement(FooInterface::class); | ||
$metadata1 = $this->prophesize(ClassMetadata::class); | ||
$metadata1->name = get_class($mappedObject1->reveal()); | ||
$metadata1->getReflectionClass()->willReturn(new \ReflectionClass($metadata1->name)); | ||
|
||
// object the extends an allowed abstract class | ||
$mappedObject2 = $this->prophesize(); | ||
$mappedObject2->willExtend(AbstractFoo::class); | ||
$metadata2 = $this->prophesize(ClassMetadata::class); | ||
$metadata2->name = get_class($mappedObject2->reveal()); | ||
$metadata2->getReflectionClass()->willReturn(new \ReflectionClass($metadata2->name)); | ||
|
||
// object of exact type that is allowed | ||
$mappedObject3 = $this->prophesize(); | ||
$metadata3 = $this->prophesize(ClassMetadata::class); | ||
$metadata3->name = get_class($mappedObject3->reveal()); | ||
$metadata3->getReflectionClass()->willReturn(new \ReflectionClass($metadata3->reveal())); | ||
|
||
// object that is not permitted | ||
$metadata4 = $this->prophesize(ClassMetadata::class); | ||
$metadata4->name = NotAllowedFoo::class; | ||
$metadata4->getReflectionClass()->willReturn(new \ReflectionClass($metadata4->reveal())); | ||
|
||
$this->description->getResource()->willReturn($this->cmfResource->reveal()); | ||
$this->cmfResource->getPayloadType()->willReturn('payload_type'); | ||
$this->metadataFactory->getMetadataFor('payload_type')->willReturn($this->odmMetadata->reveal()); | ||
$this->metadataFactory->getAllMetadata()->willReturn([ | ||
$metadata1->reveal(), | ||
$metadata2->reveal(), | ||
$metadata3->reveal(), | ||
]); | ||
|
||
$this->odmMetadata->isLeaf()->willReturn(false); | ||
$this->odmMetadata->getChildClasses()->willReturn([ | ||
FooInterface::class, | ||
AbstractFoo::class, | ||
$metadata3->name, | ||
]); | ||
|
||
$this->description->set(Descriptor::CHILDREN_ALLOW, true)->shouldBeCalled(); | ||
$this->description->set(Descriptor::CHILDREN_TYPES, [ | ||
$metadata1->name, | ||
$metadata2->name, | ||
$metadata3->name, | ||
])->shouldBeCalled(); | ||
$this->enhancer->enhance($this->description->reveal()); | ||
} | ||
} | ||
|
||
interface FooInterface | ||
{ | ||
} | ||
|
||
abstract class AbstractFoo | ||
{ | ||
} | ||
|
||
class NotAllowedFoo | ||
{ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add cmment