Skip to content

Instantly share code, notes, and snippets.

@allucardster
Last active April 21, 2021 22:10
Show Gist options
  • Save allucardster/86073426076bc49822a9f488d6cc26ff to your computer and use it in GitHub Desktop.
Save allucardster/86073426076bc49822a9f488d6cc26ff to your computer and use it in GitHub Desktop.
JMS Serializer Subscriber example with Symfony 4.4
<?php
namespace App\Controller\Api;
use App\Repository\ProductRepository;
use FOS\RestBundle\Controller\AbstractFOSRestController;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Response;
use FOS\RestBundle\Controller\Annotations as Rest;
/**
* @Rest\Route("/product")
*/
class ProductController extends AbstractFOSRestController
{
/**
* @var ProductRepository
*/
private ProductRepository $repository;
/**
* ProductController constructor.
* @param ProductRepository $repository
*/
public function __construct(ProductRepository $repository)
{
$this->repository = $repository;
}
/**
* @Rest\Get("/")
* @Rest\View(serializerGroups={"Default", "product_stock"})
*
* @return View
*/
public function getAction(): View
{
$products = $this->repository->findAll();
return View::create($products, Response ::HTTP_OK);
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
* @Serializer\ExclusionPolicy("all")
*/
class Product
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Serializer\Expose()
*/
private int $id;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Expose()
*/
private string $name;
/**
* @ORM\Column(type="string", length=255)
*/
private string $brand;
/**
* @ORM\Column(type="float", options={"default":0})
*/
private float $price;
public function getId(): int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getBrand(): string
{
return $this->brand;
}
public function setBrand(string $brand): self
{
$this->brand = $brand;
return $this;
}
public function getPrice(): float
{
return $this->price;
}
public function setPrice(float $price): self
{
$this->price = $price;
return $this;
}
}
<?php
namespace App\Serializer\Subscriber;
use App\Entity\Product;
use App\Util\BrandType;
use App\Util\OffPrice;
use JMS\Serializer\Context;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Metadata\StaticPropertyMetadata;
class ProductSubscriber implements EventSubscriberInterface
{
/**
* Returns the events to which this class has subscribed.
*
* Return format:
* array(
* array('event' => 'the-event-name', 'method' => 'onEventName', 'class' => 'some-class', 'format' => 'json'),
* array(...),
* )
*
* The class may be omitted if the class wants to subscribe to events of all classes.
* Same goes for the format key.
*
* @return array
*/
public static function getSubscribedEvents()
{
return [
[
'event' => 'serializer.post_serialize',
'method' => 'onPostSerialize',
'class' => Product::class,
'format' => 'json',
],
];
}
/**
* @param Context $context
* @param string $group
*
* @return bool
*/
private function isGroupActive(Context $context, string $group): bool
{
$groups = $context->hasAttribute('groups') ? $context->getAttribute('groups') : [];
return in_array($group, $groups);
}
/**
* @param JsonSerializationVisitor $visitor
* @param string $key
* @param $value
*/
private function addData(JsonSerializationVisitor $visitor, string $key, $value)
{
$visitor->visitProperty(new StaticPropertyMetadata('', $key, $value), $value);
}
/**
* @param ObjectEvent $event
* @throws \ReflectionException
*/
public function onPostSerialize(ObjectEvent $event)
{
/** @var Product $product */
$product = $event->getObject();
$context = $event->getContext();
/** @var JsonSerializationVisitor $visitor */
$visitor = $event->getVisitor();
// Add brand node
$this->addData($visitor, 'brand', [
'id' => $product->getBrand(),
'name' => BrandType::getName($product->getBrand()),
]);
// Add price node
$this->addData($visitor, 'price', OffPrice::getPrice($product->getBrand(), $product->getPrice()));
// Add stock node
if ($this->isGroupActive($context, 'product_stock')) {
$this->addData($visitor, 'stock', 100);
}
}
}
//GET /api/product/
[
{
"id": 1,
"name": "Product Name 1",
"brand": "Brand Name 1", //<-- node added by default from `ProductSubscriber`
"price": "99.99", //<-- node added by default from `ProductSubscriber`
"stock": 100 //<-- node added from `ProductSubscriber` if `product_stock` serialization group is used
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment