/UserNormalizer.php Secret
Last active
July 19, 2020 14:28
Example of is owner denormalizer
This file contains hidden or 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 App\Serializer\Normalizer; | |
use App\Entity\User; | |
use Symfony\Component\HttpFoundation\RequestStack; | |
use Symfony\Component\Security\Core\Security; | |
use Symfony\Component\Serializer\Exception\BadMethodCallException; | |
use Symfony\Component\Serializer\Exception\ExceptionInterface; | |
use Symfony\Component\Serializer\Exception\ExtraAttributesException; | |
use Symfony\Component\Serializer\Exception\InvalidArgumentException; | |
use Symfony\Component\Serializer\Exception\LogicException; | |
use Symfony\Component\Serializer\Exception\RuntimeException; | |
use Symfony\Component\Serializer\Exception\UnexpectedValueException; | |
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; | |
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface; | |
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; | |
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; | |
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; | |
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; | |
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; | |
class UserNormalizer implements ContextAwareNormalizerInterface, CacheableSupportsMethodInterface, NormalizerAwareInterface, ContextAwareDenormalizerInterface, DenormalizerAwareInterface | |
{ | |
use NormalizerAwareTrait; | |
use DenormalizerAwareTrait; | |
private const ALREADY_CALLED = 'USER_NORMALIZER_ALREADY_CALLED'; | |
private $security; | |
/** | |
* @var RequestStack | |
*/ | |
private $requestStack; | |
public function __construct(Security $security, RequestStack $requestStack) | |
{ | |
$this->security = $security; | |
$this->requestStack = $requestStack; | |
} | |
/** | |
* @param User $object | |
*/ | |
public function normalize($object, $format = null, array $context = array()): array | |
{ | |
if ($this->userIsOwner($object)) { | |
$context['groups'][] = 'owner:read'; | |
} | |
$context[self::ALREADY_CALLED] = true; | |
$data = $this->normalizer->normalize($object, $format, $context); | |
// Here: add, edit, or delete some data | |
return $data; | |
} | |
public function supportsNormalization($data, $format = null, array $context = []) | |
{ | |
// avoid recursion: only call once per object | |
if (isset($context[self::ALREADY_CALLED])) { | |
return false; | |
} | |
return $data instanceof User; | |
} | |
private function userIsOwner(User $user): bool | |
{ | |
/** @var User|null $authenticatedUser */ | |
$authenticatedUser = $this->security->getUser(); | |
if (!$authenticatedUser) { | |
return false; | |
} | |
return $authenticatedUser->getEmail() === $user->getEmail(); | |
} | |
public function hasCacheableSupportsMethod(): bool | |
{ | |
return false; | |
} | |
public function supportsDenormalization($data, $type, $format = null, array $context = []) | |
{ | |
// avoid recursion: only call once per object | |
if (isset($context[self::ALREADY_CALLED])) { | |
return false; | |
} | |
return $type === User::class; | |
} | |
public function denormalize($data, $class, $format = null, array $context = []) | |
{ | |
$context[self::ALREADY_CALLED] = true; | |
/** @var User $apiResource */ | |
$apiResource = $this->requestStack->getCurrentRequest() | |
->attributes->get('data'); | |
/* | |
* Add the owner:write group if: | |
* A) the object is being created (the null case) because, by | |
* definition, if you are creating the object, you are the owner! | |
* B) the object is being updated and the current user is the owner. | |
*/ | |
if ($apiResource === null || $this->userIsOwner($apiResource)) { | |
$context['groups'][] = 'owner:write'; | |
} | |
/* | |
* This is an alternate solution: the resource object is already available | |
* in the $context variable via the object_to_populate key. | |
* thanks to Hans Grinwi in the comments for this tip! | |
*/ | |
/* | |
if ( | |
!isset($context['object_to_populate']) || | |
$context['object_to_populate']->getOwner() === $this->security->getUser() | |
) { | |
$context['groups'][] = 'owner:write'; | |
} | |
*/ | |
$object = $this->denormalizer->denormalize($data, $class, $format, $context); | |
if (!$object instanceof User) { | |
throw new \Exception('This should be a User! Something went wrong!'); | |
} | |
return $object; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment