我正在用来自FOS的Symfony和RestBundle编写Restful API。我也在使用mongodb和学说。 我创建了Document,FormType和RestRoute,当我通过Postman指向端点时,发生了一些魔术。
文档:
<?php
declare(strict_types=1);
namespace App\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Validator\Constraints as Assert;
class SomeObject
{
/**
* @MongoDB\Id()
*
* @var string $id
*/
private $id;
/**
* @MongoDB\Field(type="string", name="name")
* @MongoDB\UniqueIndex()
* @Assert\NotBlank(
* message="Field: name. This value should not be blank."
* )
* @Assert\Length(
* max="30"
* )
*
* @var string $name
*/
private $name;
/**
* @MongoDB\Field(type="integer", name="minimum")
* @Assert\NotBlank(
* message="Field: minimum. This value should not be blank."
* )
* @Assert\GreaterThanOrEqual(1)
*
* @var int $minimum
*/
private $minimum;
/**
* @MongoDB\Field(type="decimal", name="price")
* @Assert\NotNull(
* message="Field: price. This value should not be null."
* )
*
* @var float $price
*/
private $price;
/**
* @MongoDB\Field(type="float", name="percent")
* @Assert\NotNull(
* message="Field: percent. This value should not be null."
* )
*
* @var float $percent
*/
private $percent;
/**
* @MongoDB\Field(type="integer", name="level")
* @Assert\NotBlank(
* message="Field: level. This value should not be blank."
* )
* @Assert\GreaterThanOrEqual(1)
* @Assert\Range(
* min="1",
* max="4"
* )
*
* @var int $level
*/
private $level;
/**
* @MongoDB\Field(type="integer", name="active")
* @Assert\NotNull(
* message="Field: active. This value should not be null."
* )
*
* @var int $active
*/
private $active;
/**
* SomeObject constructor.
*/
public function __construct()
{
//nothing to do here...
}
/**
* @return string
*/
public function getName(): string
{
return (string)$this->name;
}
/**
* @param string $name
* @return SomeObject
*/
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* @return int
*/
public function getMinimum(): int
{
return $this->minimum ?? 1;
}
/**
* @param int $minimum
* @return SomeObject
*/
public function setMinimum(int $minimum): self
{
$minimum = abs($minimum);
if (empty($minimum)) {
$minimum = 1;
}
$this->minimum = $minimum;
return $this;
}
/**
* @return float
*/
public function getPrice(): float
{
return (float)$this->price;
}
/**
* @param float $price
* @return SomeObject
*/
public function setPrice(float $price): self
{
$this->price = abs($price);
return $this;
}
/**
* @return float
*/
public function getPercent(): float
{
return (float)$this->percent;
}
/**
* @param float $percent
* @return SomeObject
*/
public function setPercent(float $percent): self
{
$this->percent = abs($percent);
return $this;
}
/**
* @return int
*/
public function getLevel(): int
{
return $this->level ?? 1;
}
/**
* @param int $level
* @return SomeObject
*/
public function setLevel(int $level): self
{
$level = abs($level);
if (empty($level)) {
$level = 1;
}
if ($level > 4) {
$level = 4;
}
$this->level = $level;
return $this;
}
/**
* @return int
*/
public function getActive(): int
{
return $this->active ?? 1;
}
/**
* @param int $active
* @return SomeObject
*/
public function setActive(int $active): self
{
$this->active = (int)(bool)$active;
return $this;
}
}
FormType:
<?php
declare(strict_types=1);
namespace App\Form;
use App\Document\SomeObject;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class SomeObjectType
* @package App\Form
*/
class SomeObjectType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'name',
TextType::class
)
->add(
'minimum',
IntegerType::class
)
->add(
'price',
MoneyType::class,
[
'scale' => 8,
])
->add(
'percent',
NumberType::class,
[
'scale' => 2,
])
->add(
'level',
IntegerType::class
)
->add(
'active',
ChoiceType::class,
[
'choices' => [
'No' => 0,
'Yes' => 1,
],
]);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SomeObject::class,
'csrf_protection' => false,
]);
}
/**
* @return string
*/
public function getName()
{
return 'someObject';
}
}
Controller (BaseController扩展了AbstractFOSRestController):
<?php
declare(strict_types=1);
namespace App\Controller\Api;
use App\Document\SomeObject;
use App\Document\User;
use App\Form\SomeObjectType;
use App\Service\ApiResponse;
use App\Service\Manager\SomeObjectManager;
use App\Service\MongoHelper;
use Doctrine\ODM\MongoDB\DocumentManager;
use Exception;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Class SomeObjectController
* @package App\Controller\Api
*
* @Rest\Route("objects")
*/
final class SomeObjectController extends BaseController
{
/** @var SomeObjectManager $someObjectManager */
private $someObjectManager;
/**
* SomeObjectController constructor.
* @param DocumentManager $dm
* @param RequestStack $requestStack
* @param UrlGeneratorInterface $router
* @param ApiResponse $apiResponse
* @param MongoHelper $mongoHelper
*/
public function __construct(DocumentManager $dm, RequestStack $requestStack, UrlGeneratorInterface $router, ApiResponse $apiResponse, MongoHelper $mongoHelper, SomeObjectManager $someObjectManagerr)
{
parent::__construct($dm, $requestStack, $router, $apiResponse, $mongoHelper);
$this->someObjectManager = $someObjectManager;
}
/**
* @Rest\Post("/", name="post_someObject")
*
* @param Request $request
* @return View
*/
public function postSomeObjectAction(Request $request): View
{
/** @var User $admin */
$admin = $this->getUser();
//!todo: change to Symfony roles
if (!$admin->getIsAdmin()) {
$this->apiResponse->setMessage('User needs to have Admin privileges.');
$this->status = Response::HTTP_UNAUTHORIZED;
}
if ($this->apiResponse->getStatus()) {
/** @var SomeObject $someObject */
$someObject = new SomeObject();
/** @var Form $form */
$form = $this->createForm(SomeObjectType::class, $someObject);
$form->handleRequest($request);
dd($form->getData());
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
try {
$this->documentManager->persist($data);
$this->documentManager->flush();
$this->apiResponse->setMessage('Object created.', false);
$this->apiResponse->setDataCreated($someObject->getId());
$this->status = Response::HTTP_CREATED;
} catch (Exception $e) {
$this->apiResponse->setMessage($e->getMessage());
$this->status = Response::HTTP_CONFLICT;
}
} else {
$this->getErrors($form);
}
}
return $this->getResponse();
}
}
JSON:
{
"someObject": {
"name": "Test Name",
"minimum": 1,
"price": 1,
"percent": 1,
"level": 1,
"active": 1
}
}
每次都会得到这个
SomeObject {#831
-id: null
-name: "Test Name"
-minimum: null
-price: 1.0
-percent: 1.0
-level: null
-active: null
}
但是,如果我更改JSON例如。与此(不同于1):
{
"someObject": {
"name": "Test Name",
"minimum": 2,
"price": 1,
"percent": 1,
"level": 5,
"active": 0
}
}
我会得到:
SomeObject {#831
-id: null
-name: "Test Name"
-minimum: 2
-price: 1.0
-percent: 1.0
-level: 4
-active: 0
}
如果我将IntegerType更改为NumberType,则第一个指向测试将返回正确的值。但是ChoiceType呢?
为什么在我的示例中IntegerType和ChoiceType返回null?我在做什么错了?