From a535c8c9f8d2407706a9d9e7f33803325de53d2c Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Tue, 24 May 2016 18:14:29 +0200 Subject: [PATCH] wip --- features/relation.feature | 57 ++++++++++ src/Bridge/Symfony/Routing/IriConverter.php | 69 ++++++++++-- .../TestBundle/Entity/DummyFriend.php | 89 +++++++++++++++ .../TestBundle/Entity/RelatedDummy.php | 26 +++++ .../Entity/RelatedToDummyFriend.php | 102 ++++++++++++++++++ 5 files changed, 332 insertions(+), 11 deletions(-) create mode 100644 tests/Fixtures/TestBundle/Entity/DummyFriend.php create mode 100644 tests/Fixtures/TestBundle/Entity/RelatedToDummyFriend.php diff --git a/features/relation.feature b/features/relation.feature index 4822aec6c74..bf505635fc5 100644 --- a/features/relation.feature +++ b/features/relation.feature @@ -23,6 +23,24 @@ Feature: Relations support } """ + Scenario: Create a dummy friend + When I send a "POST" request to "/dummy_friends" with body: + """ + {"name": "Zoidberg"} + """ + Then the response status code should be 201 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json" + And the JSON should be equal to: + """ + { + "@context": "/contexts/DummyFriend", + "@id": "/dummy_friends/1", + "@type": "DummyFriend", + "name": "Zoidberg" + } + """ + Scenario: Create a related dummy When I send a "POST" request to "/related_dummies" with body: """ @@ -42,12 +60,51 @@ Feature: Relations support "name": null, "dummyDate": null, "thirdLevel": "/third_levels/1", + "relatedToDummyFriend": null, "dummyBoolean": null, "symfony": "symfony", "age": null } """ + Scenario: Create a friend relationship + When I send a "POST" request to "/related_to_dummy_friends" with body: + """ + { + "name": "Friends relation", + "dummyFriend": "/dummy_friends/1", + "relatedDummy": "/related_dummies/1" + } + """ + Then the response status code should be 201 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json" + And the JSON should be equal to: + """ + { + "@context": "/contexts/RelatedToDummyFriend", + "@id": "/related_to_dummy_friends/dummyFriend=1;relatedDummy=1", + "@type": "RelatedToDummyFriend", + "name": "Friends relation" + } + """ + + Scenario: Get the relationship + When I send a "GET" request to "/related_to_dummy_friends/dummyFriend=1;relatedDummy=1" + And the response status code should be 200 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json" + And the JSON should be equal to: + """ + { + "@context": "/contexts/RelatedToDummyFriend", + "@id": "/related_to_dummy_friends/dummyFriend=1;relatedDummy=1", + "@type": "RelatedToDummyFriend", + "name": "Friends relation" + } + """ + + Scenario: Create a dummy with relations When I send a "POST" request to "/dummies" with body: """ diff --git a/src/Bridge/Symfony/Routing/IriConverter.php b/src/Bridge/Symfony/Routing/IriConverter.php index d24c41584ca..4a7a14320a5 100644 --- a/src/Bridge/Symfony/Routing/IriConverter.php +++ b/src/Bridge/Symfony/Routing/IriConverter.php @@ -77,25 +77,72 @@ public function getIriFromItem($item, int $referenceType = UrlGeneratorInterface $resourceClass = $this->getObjectClass($item); $routeName = $this->getRouteName($resourceClass, false); + $identifiers = array_map([$this, 'generateIdentifiersUrl'], $this->getIdentifiersFromItem($item)); + + if (count($identifiers) > 1) { + $identifiers = array_map(function ($identifierName, $identifierValue) { + return sprintf('%s=%s', $identifierName, rawurlencode($identifierValue)); + }, array_keys($identifiers), $identifiers); + } + + return $this->router->generate($routeName, ['id' => implode(';', $identifiers)], $referenceType); + } + + /** + * Get the identifier url. + * + * @param mixed $identifiers + * + * @return string + */ + public function generateIdentifiersUrl($identifiers) : string + { + if (!is_array($identifiers)) { + return rawurlencode($identifiers); + } + + if (1 === count($identifiers)) { + return rawurlencode(array_values($identifiers)[0]); + } + + return array_map(function ($identifierName, $identifierValue) { + return sprintf('%s=%s', $identifierName, $this->generateIdentifiersUrl($identifierValue)); + }, array_keys($identifiers), $identifiers); + } + + /** + * Find identifiers from an Item (Object). + * + * @param object $item + * + * @throws RuntimeException + * + * @return array + */ + private function getIdentifiersFromItem($item) : array + { + $identifiers = []; + $resourceClass = $this->getObjectClass($item); + foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) { $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName); if ($propertyMetadata->isIdentifier()) { $identifiers[$propertyName] = $this->propertyAccessor->getValue($item, $propertyName); - } - } - if (1 === count($identifiers)) { - $identifiers = array_map(function ($identifierValue) { - return rawurlencode($identifierValue); - }, $identifiers); - } else { - $identifiers = array_map(function ($identifierName, $identifierValue) { - return sprintf('%s=%s', $identifierName, rawurlencode($identifierValue)); - }, array_keys($identifiers), $identifiers); + if (!is_object($identifiers[$propertyName])) { + continue; + } + + $identifiers[$propertyName] = $this->getIdentifiersFromItem($identifiers[$propertyName]); + + if (0 === count($identifiers[$propertyName])) { + throw new \RuntimeException(sprintf('%s identifiers can not be found', $resourceClass)); + } + } } - return $this->router->generate($routeName, ['id' => implode(';', $identifiers)], $referenceType); + return $identifiers; } /** diff --git a/tests/Fixtures/TestBundle/Entity/DummyFriend.php b/tests/Fixtures/TestBundle/Entity/DummyFriend.php new file mode 100644 index 00000000000..8b7e271eeb1 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/DummyFriend.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity; + +use ApiPlatform\Core\Annotation\ApiProperty; +use ApiPlatform\Core\Annotation\ApiResource; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * DummyFriend. + * + * @author Kévin Dunglas + * + * @ApiResource() + * @ORM\Entity + */ +class DummyFriend +{ + /** + * @var int The id. + * + * @ORM\Column(type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + * @Groups({"fakemanytomany"}) + */ + private $id; + + /** + * @var string The dummy name. + * + * @ORM\Column + * @Assert\NotBlank + * @ApiProperty(iri="http://schema.org/name") + * @Groups({"fakemanytomany"}) + */ + private $name; + + /** + * Get id. + * + * @return id. + */ + public function getId() + { + return $this->id; + } + + /** + * Set id. + * + * @param id the value to set. + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * Get name. + * + * @return name. + */ + public function getName() + { + return $this->name; + } + + /** + * Set name. + * + * @param name the value to set. + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/tests/Fixtures/TestBundle/Entity/RelatedDummy.php b/tests/Fixtures/TestBundle/Entity/RelatedDummy.php index e9c36118d0c..ff1789dd668 100644 --- a/tests/Fixtures/TestBundle/Entity/RelatedDummy.php +++ b/tests/Fixtures/TestBundle/Entity/RelatedDummy.php @@ -60,6 +60,12 @@ class RelatedDummy extends ParentDummy */ public $thirdLevel; + /** + * @ORM\OneToMany(targetEntity="RelatedToDummyFriend", cascade={"persist"}, fetch="EAGER", mappedBy="relatedDummy") + * @Groups({"fakemanytomany"}) + */ + public $relatedToDummyFriend; + /** * @var bool A dummy bool. * @@ -117,4 +123,24 @@ public function setDummyBoolean($dummyBoolean) { $this->dummyBoolean = $dummyBoolean; } + + /** + * Get relatedToDummyFriend. + * + * @return relatedToDummyFriend. + */ + public function getRelatedToDummyFriend() + { + return $this->relatedToDummyFriend; + } + + /** + * Set relatedToDummyFriend. + * + * @param relatedToDummyFriend the value to set. + */ + public function setRelatedToDummyFriend(RelatedToDummyFriend $relatedToDummyFriend) + { + $this->relatedToDummyFriend = $relatedToDummyFriend; + } } diff --git a/tests/Fixtures/TestBundle/Entity/RelatedToDummyFriend.php b/tests/Fixtures/TestBundle/Entity/RelatedToDummyFriend.php new file mode 100644 index 00000000000..b2b0d1b98af --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/RelatedToDummyFriend.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity; + +use ApiPlatform\Core\Annotation\ApiProperty; +use ApiPlatform\Core\Annotation\ApiResource; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * Related To Dummy Friend represent an association table for a manytomany relation. + * + * @ApiResource() + * @ORM\Entity + */ +class RelatedToDummyFriend +{ + /** + * @var string The dummy name. + * + * @ORM\Column + * @Assert\NotBlank + * @ApiProperty(iri="http://schema.org/name") + */ + private $name; + + /** + * @ORM\Id + * @ORM\ManyToOne(targetEntity="DummyFriend", fetch="EAGER") + * @ORM\JoinColumn(name="dummyfriend_id", referencedColumnName="id", nullable=false) + * @Groups({"fakemanytomany"}) + */ + private $dummyFriend; + + /** + * @ORM\Id + * @ORM\ManyToOne(targetEntity="RelatedDummy", inversedBy="relatedToDummyFriend") + * @ORM\JoinColumn(name="relateddummy_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") + * @Groups({"fakemanytomany"}) + */ + private $relatedDummy; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + /** + * Get dummyFriend. + * + * @return dummyFriend. + */ + public function getDummyFriend() + { + return $this->dummyFriend; + } + + /** + * Set dummyFriend. + * + * @param dummyFriend the value to set. + */ + public function setDummyFriend($dummyFriend) + { + $this->dummyFriend = $dummyFriend; + } + + /** + * Get relatedDummy. + * + * @return relatedDummy. + */ + public function getRelatedDummy() + { + return $this->relatedDummy; + } + + /** + * Set relatedDummy. + * + * @param relatedDummy the value to set. + */ + public function setRelatedDummy($relatedDummy) + { + $this->relatedDummy = $relatedDummy; + } +}