On the ApiDoc for our controller we have specified the output response object and now we see a list of all the parameters that get returned.
How do we provide values for the version and/or description fields on this list?
I have tried adding #ApiDoc(description="text") to the response object's parameters but that doesn't seem to be doing anything.
Thanks in advance.
This is a working API method from one of my projects:
/**
* Get an extended FB token given a normal access_token
*
* #ApiDoc(
* resource=true,
* requirements={
* {
* "name"="access_token",
* "dataType"="string",
* "description"="The FB access token",
* "version" = "1.0"
* }
* },
* views = { "facebook" }
* )
* #Get("/extend/token/{access_token}", name="get_extend_fb_token", options={ "method_prefix" = false }, defaults={"_format"="json"})
*/
public function getExtendTokenAction(Request $request, $access_token)
{
//...
}
All APIDoc parameters that get returned are grouped under "requirements".
I stepped through the ApiDocBundle today and see that Description comes from the comment on the model property or method with #VirtualProperty.
For example:
/**
* This text will be displayed as the response property's description
*
* #var \DateTime
* #JMS\Type("DateTime<'Y-m-d\TH:i:sO'>")
*/
protected $dateTimeProperty;
or
/**
* VirtualProperty comment
*
* #JMS\Type("integer")
* #JMS\VirtualProperty()
* #return integer
*/
public function getVirtualProperty()
{
return $this->someFunc();
}
The same applies to the all comments on the controller method.
I haven't used nelmioApiDoc but looking at the documentation for it, using description="text" in the annotation section seems correct. Have you tried clearing you cache:
php bin/console cache:clear --env=prod
Not sure if it is related.
This section describes how versioning objects is used, and looks like you have to use #Until("x.x.x") and #Since("x.x") in your JMSSerializerBundle classes. See this link.
Related
I am new to symfony and doctrine. And I am compeleting a code that someone else has started. I mainly have a form for which I wrote a validation function in my controller. In this form a BusReservation object along with its BusReservationDetails are created and saved to the db. so at the end of the form validation function, after the entities are saved in DB, I call a BusReservation Manager method which is transformBusReservationDetailIntoBusTicket which aim is to take each BusReservationDetail in the BusReservation oject and create a a new entity BusTicket based on it.
so I created this loop (please let me know if there is something wrong in my code so that i can write in a good syntax). I tried to put the 3 persist that you see at the end of the code but I got : Notice: Undefined index: 0000000..
I tried to merge (the last 3 lines in code ) I got the following :
A new entity was found through the relationship 'MyBundle\Entity\CustomInfo#busTicket' that was not configured to cascade persist operations for entity: . To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
I got this same error when i commented all theh 6 lines of merge and flush.
PS: I am not expecting the flush to fully work. There are some properties that are nullable=false so I assume that I must set them as well so that the entities can be saved to DB. But the error i got is by far different than this.
PS : I noticed that there is a onFlush where the customInfo is updated and persisted again and other things happen, but i am trying to debug step by step. I tried to detach this event but still got the same errors. so I want to fix my code and make sure that the code part that i wrote in the manager is correct and if that's the case then I can move to debugging the event Listener. so please I would like to know if the following code is correct and why the flush is not working.
/**
* #param $idBusReservation
* #return bool
* #throws \Doctrine\ORM\NonUniqueResultException
*/
public function transformBusReservationIntoBusTicket($idBusReservation): bool
{ $result = "into the function";
/** #var BusReservation $busReservation */
$busReservation = $this->em->getRepository('MyBundle:BusReservation')->find($idBusReservation);
if ($busReservation !== null) {
/** #var BusReservationDetail $busReservationDetail */
foreach ($busReservation->getBusReservationDetails() as $busReservationDetail) {
$busTicket = new BusTicket($busReservationDetail->getBusModel(), $busReservation->getPassenger());
$busReservationDetail->setBusTicket($busTicket);
$busTicket->setBusReservationDetail($busReservationDetail);
$busTicket->setOwner($busreservation->getPassenger()->getName());
if ($busReservationDetail->getBusModel()->getCode() === 'VIPbus') {
// perform some logic .. later on
} else {
$customInfo = new CustomInfo();
$customInfo->setNumber(1551998);
// $customInfo->setCurrentMode(
// $this->em->getRepository('MyBundle:Mode')
// ->find(['code' => 'Working'])
// );
$customInfo->setBusTicket($busTicket);
// Bus ticket :
$busTicket->addCustomInfo($customInfo);
$busTicket->setComment($busReservation->getComment());
}
/** #var Mode $currentMode */
$currentMode = $this->em->getRepository('MyBundle:Mode')
->findOneBy(['code' => 'Working']);
$busTicket->setCurrentMode($currentMode);
// $this->em->merge($customInfo);
// $this->em->merge($busReservationDetail);
// $this->em->merge($busTicket);
// $this->em->persist($customInfo);
// $this->em->persist($busReservationDetail);
// $this->em->persist($busTicket);
}
$this->em->flush();
// $this->em->clear();
}
return $result;
}
// *************** In BusReservation.php ********************
/**
* #ORM\OneToMany(targetEntity="MyBundle\Entity\BusReservationDetail", mappedBy="busReservation")
*/
private $busReservationDetails;
/**
* Get busReservationDetails
*
*#return Collection
*/
public function getBusReservationDetails()
{
return $this->busReservationDetails;
}
// ---------------------------------------------------------------------
// *************** In BusReservationDetail.php ********************
/**
* #ORM\ManyToOne(targetEntity="MyBundle\Entity\BusReservation", inversedBy="busReservationDetails")
* #ORM\JoinColumn(name="id_bus_reservation", referencedColumnName="id_bus_reservation", nullable=false)
*/
private $busReservation;
/**
* #ORM\ManyToOne(targetEntity="MyBundle\Entity\BusModel")
* #ORM\JoinColumn(name="bus_model_code", referencedColumnName="bus_model_code", nullable=false)
*/
private $busModel;
/**
* #ORM\OneToOne(targetEntity="MyBundle\Entity\BusTicket", inversedBy="busReservationDetail", cascade={"merge","remove","persist"})
* #ORM\JoinColumn(name="id_bus_ticket", referencedColumnName="id_bus_ticket")
*/
private $busTicket;
/**
* #return BusModel
*/
public function getBusModel()
{
return $this->busModel;
}
//-------------------------------------------------------------------------
// ************ IN BusTicket.php *****************************
/**
* #ORM\OneToMany(targetEntity="MyBundle\Entity\CustomInfo", mappedBy="busTicket")
*/
private $customInfos;
/**
*
* #param CustomInfo $customInfo
*
* #return BusTicket
*/
public function addCustomInfot(CustomInfo $customInfo)
{
if (!$this->customInfos->contains($customInfo)) {
$this->customInfos[] = $customInfo;
}
return $this;
}
/**
* #ORM\OneToOne(targetEntity="MyBundle\Entity\busReservationDetail", mappedBy="busTicket")
*/
private $busReservationDetail;
// --------------------------------------------------------------------
// CUSTOMINFO ENTITY
/**
* #ORM\ManyToOne(targetEntity="MyBundle\Entity\BusTicket", inversedBy="customInfos")
* #ORM\JoinColumn(name="id_bus_ticket", referencedColumnName="id_bus_ticket", nullable=false)
*/
private $busTicket;
The answer is in your error message. You either have to add cascade={"persist"} to your entity annotation, or explicitly call persist. I don't believe you need em->merge() in this situation as you're never taking the entities out of context.
Where you have all your persist lines commented out, just try putting this in
$this->em->persist($busTicket);
$this->em->persist($busReservationDetail);
$this->em->persist($customInfo);
and if you're looping through a ton of entities, you could try adding the flush inside the loop at the end instead of a huge flush at the end.
I have an Doctrine Entity Class identified by an iso_code attribute :
/**
* #ApiResource(
* graphql={
* "item_query",
* "collection_query"
* }
* )
*
* #ORM\Entity(repositoryClass="App\Repository\LanguageRepository")
* #ORM\HasLifecycleCallbacks
*/
class Language extends BaseEntity
{
/**
* #var string
* #ApiProperty(identifier=true)
*
* #ORM\Id()
* #ORM\Column(type="string", length=2, unique=true)
*/
private $iso_code;
public function getId(): string
{
return $this->iso_code;
}
public function getIsoCode(): string
{
return $this->iso_code;
}
}
When I try to access a list through a GET requests /api/v2/languages?pages=1, I got "No identifiers defined for resource of type \"App\\Entity\\Language\"" error.
When I try to access an item through a GET requests /api/v2/languages/en, I got Invalid identifier value or configuration
Configuration:
- api platform v2.5.6
- php 7.4
- symfony 4.4
You have to add
App\Entity\Language
properties:
id:
identifier: false
iso_code:
identifier: true
Solved similar issue by setting identifier #ApiProperty(identifier=true) on top of the
getter function
Changing the name of the identifier to $id did the trick.
But I can't get why it's not possible to use $iso_code
I'm using API platform in my Symfony4 app to expose my resources.
It's a great framework but it force you by default to have all your Business logic in the front-end side, because it expose all your Entities and not a Business Object.
I don't like that and I prefer to have my business logic in the back-end side.
I need to create users, but there are different type of users.
So I have create a UserFactory in the back-end-side. So the front just need to push a Business object and the back-end take care of everything.
The front front can never persist a User Object directly in the DB. It is the role of the back-end
Following this tutorial to use DTO for Reading:
https://api-platform.com/docs/core/dto/#how-to-use-a-dto-for-reading
I'm trying to do the same for posting. And it works. Here is my Controller code:
/**
* #Route(
* path="/create/model",
* name="create-model",
* methods={"POST"},
* defaults={
* "_api_respond"=true,
* "_api_normalization_context"={"api_sub_level"=true},
* "_api_swagger_context"={
* "tags"={"User"},
* "summary"="Create a user Model",
* "parameters"={
*
* },
* "responses"={
* "201"={
* "description"="User Model created",
* "schema"={
* "type"="object",
* "properties"={
* "firstName"={"type"="string"},
* "lastName"={"type"="string"},
* "email"={"type"="string"},
* }
* }
* }
* }
* }
* }
* )
* #param Request $request
* #return \App\Entity\User
* #throws \App\Exception\ClassNotFoundException
* #throws \App\Exception\InvalidUserException
*/
public function createModel(Request $request)
{
$model = $this->serializer->deserialize($request->getContent(), Model::class, 'json');
$user = $this->userFactory->create($model);
$this->userRepository->save($user);
return $user;
}
It works great, but I would love my new resource to work in the Swagger UI, so I can Create via POST method new resources directly in the web interface.
For that I think I need to complete the parameter section in my _api_swagger_context. But I don't fin any documentation about that.
How can I do that?
Found the answer here: https://github.com/api-platform/docs/issues/666
You can fill parameters like this :
"parameters" = {
{
"name" = "data",
"in" = "body",
"required" = "true",
"schema" = {
"type" = "object",
"properties" = {
"firstName"={"type"="string"},
"lastName"={"type"="string"},
"email" = {"type" = "string" }
}
},
},
},
More docs about parameters for swagger here : https://swagger.io/docs/specification/2-0/describing-parameters/
Can anyone help me explaining what I am doing wrong? I try to set an entity in relation to an file. Its about Supplier and Stock.
My Stock entity looks like
/**
* #ORM\ManyToOne(targetEntity="PrClientBundle\Entity\Lieferanten")
* #ORM\JoinColumn(name="liefer_id", referencedColumnName="id")
* #var lieferant
*/
private $lieferant;
I also using getter and setter like following
/**
* Set lieferant
*
* #param \PrClientBundle\Entity\Lieferanten $lieferant
* #return Leadbase
*/
public function setLieferant(\PrClientBundle\Entity\Lieferanten $lieferant = null)
{
$this->lieferant = $lieferant;
return $this;
}
/**
* Get lieferant
*
* #return \PrClientBundle\Entity\Lieferanten
*/
public function getLieferant()
{
return $this->lieferant;
}
When I import new Stockitems like:
$lead->setLieferant($lieferant);
I get the following errormessage which I really don't understand :(
[Doctrine\ORM\ORMInvalidArgumentException]
A new entity was found through the relationship 'PrLeadBundle\Entity\Leadbase#lieferant' that was not configured to cascade persist operations for entity: PrClientBundle\Enti
ty\Lieferanten#000000002a45dae80000000002f826ff. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this
association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'PrClientBundle\Entity\Lieferante
n#__toString()' to get a clue.
It would be very great if you could help me understanding what am I doing wrong.
I have a delete route like so, using FOSRestBundle
/**
* #Route("/delete/{id}")
* #Security("has_role('ROLE_ADMIN')")
* #Rest\View
*/
public function deleteAction(Request $request, $id)
{
...
}
I want to make sure that the id parameter is numeric.
I tried :
#Route("/delete/{id}", requirements={"id" = "\d+"})
And
#QueryParam(name="id", requirements="\d+", description="User id")
And
#RequestParam(name="id", requirements="\d+", description="User id")
But none of these solutions work. Either the route is not found, or the constraint is not respected.
This works, but does not return a JSON encoded 404 response when trying to access a route like api/users/xx.json
#Rest\Delete("/{id}", requirements={"id" = "\d+"}, defaults={"id" = 1})