Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions features/relation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand All @@ -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:
"""
Expand Down
69 changes: 58 additions & 11 deletions src/Bridge/Symfony/Routing/IriConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not fond of this, does anyone have a better idea in mind?

}

/**
* 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;
}

/**
Expand Down
89 changes: 89 additions & 0 deletions tests/Fixtures/TestBundle/Entity/DummyFriend.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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 <dunglas@gmail.com>
*
* @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;
}
}
26 changes: 26 additions & 0 deletions tests/Fixtures/TestBundle/Entity/RelatedDummy.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}
}
102 changes: 102 additions & 0 deletions tests/Fixtures/TestBundle/Entity/RelatedToDummyFriend.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* 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;
}
}