如何在 Symfony 5 中创建用于序列化 json 的自定义 Normilizer?
How to create a custom Normilizer for serializing json in Symfony 5?
我正在使用 symfony 创建 REST api,最终想要 return 自定义 json。
例如隐藏一些字段,从关系对象中获取特定字段(来自外键)等等(底部的例子)。
我有两个具有 ManyToOne
/OneToMany
关系的实体,产品和类别。
Product.php
:
<?php
namespace App\Entity;
use App\Repository\ProductRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=ProductRepository::class)
*/
class Product
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="text")
*/
private $description;
/**
* @ORM\Column(type="float")
*/
private $price;
/**
* @ORM\Column(type="boolean")
*/
private $available;
/**
* @ORM\ManyToOne(targetEntity=Category::class, inversedBy="products")
* @ORM\JoinColumn(nullable=false)
*/
private $category;
// ...
// Rest of getters & setters
// ...
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
Category.php
:
<?php
namespace App\Entity;
use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=CategoryRepository::class)
*/
class Category
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\OneToMany(targetEntity=Product::class, mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
// ...
// Rest of getters & setters
// ...
/**
* @return Collection|Product[]
*/
public function getProducts(): Collection
{
return $this->products;
}
public function addProduct(Product $product): self
{
if (!$this->products->contains($product)) {
$this->products[] = $product;
$product->setCategory($this);
}
return $this;
}
public function removeProduct(Product $product): self
{
if ($this->products->removeElement($product)) {
// set the owning side to null (unless already changed)
if ($product->getCategory() === $this) {
$product->setCategory(null);
}
}
return $this;
}
public function __toString()
{
return $this->getName();
}
}
这是我的控制器:
class ProductApiController extends AbstractController
{
/**
* @Route("/products", name = "api_product_list")
*/
public function getAll(SerializerInterface $serializer, ProductRepository $repo): Response
{
$products = $repo->findAll();
$jsonObject = $serializer->serialize($products, 'json', [
'circular_reference_handler' => function () {
return null;
}]
);
return new Response($jsonObject, 200, ['Content-Type' => 'application/json']);
}
}
这是我当前的输出:
{
"id": 1,
"name": "Gaming pc",
"description": "A nice computer",
"price": 9800,
"available": true,
"category": {
"id": 1,
"name": "Computers",
"products": [
null
],
"__initializer__": null,
"__cloner__": null,
"__isInitialized__": true
}
}
这是我想要得到的输出:
{
"id": 1,
"name": "Gaming pc",
"description": "A nice computer",
"price": 9800,
"available": true,
"category": "Computers" // Basically this is why I need a custom serializer
}
这是 documentation,但我不知道在我的情况下该怎么做。
在 src/Serializer/ProductNormalizer.php
下:(路径必须准确,否则你必须 register it manually)
<?php
namespace App\Serializer;
use App\Entity\Product;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class ProductNormalizer implements ContextAwareNormalizerInterface
{
private $normalizer;
public function __construct(ObjectNormalizer $normalizer)
{
$this->normalizer = $normalizer;
}
public function normalize($product, string $format = null, array $context = [])
{
$data = $this->normalizer->normalize($product, $format, $context);
$data = [
'id' => $product->getId(),
'name' => $product->getName(),
'description' => $product->getDescription(),
'price' => $product->getPrice(),
'available' => $product->getAvailable(),
'category' => $product->getCategory()->getName(), //Customize to your needs
];
return $data;
}
public function supportsNormalization($data, string $format = null, array $context = [])
{
return $data instanceof product;
}
}
我正在使用 symfony 创建 REST api,最终想要 return 自定义 json。 例如隐藏一些字段,从关系对象中获取特定字段(来自外键)等等(底部的例子)。
我有两个具有 ManyToOne
/OneToMany
关系的实体,产品和类别。
Product.php
:
<?php
namespace App\Entity;
use App\Repository\ProductRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=ProductRepository::class)
*/
class Product
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="text")
*/
private $description;
/**
* @ORM\Column(type="float")
*/
private $price;
/**
* @ORM\Column(type="boolean")
*/
private $available;
/**
* @ORM\ManyToOne(targetEntity=Category::class, inversedBy="products")
* @ORM\JoinColumn(nullable=false)
*/
private $category;
// ...
// Rest of getters & setters
// ...
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
Category.php
:
<?php
namespace App\Entity;
use App\Repository\CategoryRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=CategoryRepository::class)
*/
class Category
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\OneToMany(targetEntity=Product::class, mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
// ...
// Rest of getters & setters
// ...
/**
* @return Collection|Product[]
*/
public function getProducts(): Collection
{
return $this->products;
}
public function addProduct(Product $product): self
{
if (!$this->products->contains($product)) {
$this->products[] = $product;
$product->setCategory($this);
}
return $this;
}
public function removeProduct(Product $product): self
{
if ($this->products->removeElement($product)) {
// set the owning side to null (unless already changed)
if ($product->getCategory() === $this) {
$product->setCategory(null);
}
}
return $this;
}
public function __toString()
{
return $this->getName();
}
}
这是我的控制器:
class ProductApiController extends AbstractController
{
/**
* @Route("/products", name = "api_product_list")
*/
public function getAll(SerializerInterface $serializer, ProductRepository $repo): Response
{
$products = $repo->findAll();
$jsonObject = $serializer->serialize($products, 'json', [
'circular_reference_handler' => function () {
return null;
}]
);
return new Response($jsonObject, 200, ['Content-Type' => 'application/json']);
}
}
这是我当前的输出:
{
"id": 1,
"name": "Gaming pc",
"description": "A nice computer",
"price": 9800,
"available": true,
"category": {
"id": 1,
"name": "Computers",
"products": [
null
],
"__initializer__": null,
"__cloner__": null,
"__isInitialized__": true
}
}
这是我想要得到的输出:
{
"id": 1,
"name": "Gaming pc",
"description": "A nice computer",
"price": 9800,
"available": true,
"category": "Computers" // Basically this is why I need a custom serializer
}
这是 documentation,但我不知道在我的情况下该怎么做。
在 src/Serializer/ProductNormalizer.php
下:(路径必须准确,否则你必须 register it manually)
<?php
namespace App\Serializer;
use App\Entity\Product;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class ProductNormalizer implements ContextAwareNormalizerInterface
{
private $normalizer;
public function __construct(ObjectNormalizer $normalizer)
{
$this->normalizer = $normalizer;
}
public function normalize($product, string $format = null, array $context = [])
{
$data = $this->normalizer->normalize($product, $format, $context);
$data = [
'id' => $product->getId(),
'name' => $product->getName(),
'description' => $product->getDescription(),
'price' => $product->getPrice(),
'available' => $product->getAvailable(),
'category' => $product->getCategory()->getName(), //Customize to your needs
];
return $data;
}
public function supportsNormalization($data, string $format = null, array $context = [])
{
return $data instanceof product;
}
}