-
-
Notifications
You must be signed in to change notification settings - Fork 920
Doctrine filters: Allow nested properties (across relations) #242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
use Doctrine\Common\Persistence\ManagerRegistry; | ||
use Doctrine\Common\Persistence\Mapping\ClassMetadata; | ||
use Dunglas\ApiBundle\Api\ResourceInterface; | ||
use Dunglas\ApiBundle\Util\RequestParser; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
|
@@ -61,15 +62,102 @@ protected function getClassMetadata(ResourceInterface $resource) | |
} | ||
|
||
/** | ||
* Is the given property enabled? | ||
* Determines whether the given property is enabled. | ||
* | ||
* @param string $property | ||
* | ||
* @return bool | ||
*/ | ||
protected function isPropertyEnabled($property) | ||
{ | ||
return null === $this->properties || array_key_exists($property, $this->properties); | ||
if (null === $this->properties) { | ||
// to ensure sanity, nested properties must still be explicitly enabled | ||
return !$this->isPropertyNested($property); | ||
} | ||
|
||
return array_key_exists($property, $this->properties); | ||
} | ||
|
||
/** | ||
* Determines whether the given property is mapped. | ||
* | ||
* @param string $property | ||
* @param ResourceInterface $resource | ||
* @param bool $allowAssociation | ||
* | ||
* @return bool | ||
*/ | ||
protected function isPropertyMapped($property, ResourceInterface $resource, $allowAssociation = false) | ||
{ | ||
if ($this->isPropertyNested($property)) { | ||
$propertyParts = $this->splitPropertyParts($property); | ||
$metadata = $this->getNestedMetadata($resource, $propertyParts['associations']); | ||
$property = $propertyParts['field']; | ||
} else { | ||
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. Same idea here, please prevent usage of 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. IMO 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 agree that here the protected function isPropertyMapped($property, ResourceInterface $resource, $allowAssociation = false)
{
if ($this->isPropertyNested($property)) {
$propertyParts = $this->splitPropertyParts($property);
$metadata = $this->getNestedMetadata($resource, $propertyParts['associations']);
$property = $propertyParts['field'];
} else {
$metadata = $this->getClassMetadata($resource);
}
return $metadata->hasField($property) || ($allowAssociation && $metadata->hasAssociation($property));
} 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. Good catch! :) |
||
$metadata = $this->getClassMetadata($resource); | ||
} | ||
|
||
return $metadata->hasField($property) || ($allowAssociation && $metadata->hasAssociation($property)); | ||
} | ||
|
||
/** | ||
* Determines whether the given property is nested. | ||
* | ||
* @param string $property | ||
* | ||
* @return bool | ||
*/ | ||
protected function isPropertyNested($property) | ||
{ | ||
return false !== strpos($property, '.'); | ||
} | ||
|
||
/** | ||
* Gets nested class metadata for the given resource. | ||
* | ||
* @param ResourceInterface $resource | ||
* @param string[] $associations | ||
* | ||
* @return ClassMetadata | ||
*/ | ||
protected function getNestedMetadata(ResourceInterface $resource, array $associations) | ||
{ | ||
$metadata = $this->getClassMetadata($resource); | ||
|
||
foreach ($associations as $association) { | ||
if ($metadata->hasAssociation($association)) { | ||
$associationClass = $metadata->getAssociationTargetClass($association); | ||
|
||
$metadata = $this | ||
->managerRegistry | ||
->getManagerForClass($associationClass) | ||
->getClassMetadata($associationClass) | ||
; | ||
} | ||
} | ||
|
||
return $metadata; | ||
} | ||
|
||
/** | ||
* Splits the given property into parts. | ||
* | ||
* Returns an array with the following keys: | ||
* - associations: array of associations according to nesting order | ||
* - field: string holding the actual field (leaf node) | ||
* | ||
* @param string $property | ||
* | ||
* @return array | ||
*/ | ||
protected function splitPropertyParts($property) | ||
{ | ||
$parts = explode('.', $property); | ||
|
||
return [ | ||
'associations' => array_slice($parts, 0, -1), | ||
'field' => end($parts), | ||
]; | ||
} | ||
|
||
/** | ||
|
@@ -81,6 +169,20 @@ protected function isPropertyEnabled($property) | |
*/ | ||
protected function extractProperties(Request $request) | ||
{ | ||
$needsFixing = false; | ||
|
||
if (null !== $this->properties) { | ||
foreach ($this->properties as $property => $value) { | ||
if ($this->isPropertyNested($property) && $request->query->has(str_replace('.', '_', $property))) { | ||
$needsFixing = true; | ||
} | ||
} | ||
} | ||
|
||
if ($needsFixing) { | ||
$request = RequestParser::parseAndDuplicateRequest($request); | ||
} | ||
|
||
return $request->query->all(); | ||
} | ||
} |
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.
Ideally this should belong in Doctrine ORM?
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.
At least in an other place than here yep :)
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.
What is the problem to have it here?
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.
I just thought it could be useful in Doctrine ORM (of course, that's subject to the opinion of Doctrine maintainers).
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.
@dunglas I too don't get why this should be in another place, when it's specific to Doctrine pagination. But then again I'm not sure I can understand very well the other strong opinions of @sroze