Symfony 3.1.7 + FOSRestBundle latest version
<?php
namespace PM\ApiBundle\Controller;
...
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
class ArticlesController extends FOSRestController
{
/**
* #ApiDoc(
* section="articles",
* resource=true,
* description="Get articles published"
* )
* #Rest\View(serializerGroups={"article"})
* #Rest\Get("/articles")
*/
public function getArticlesAction(Request $request)
{
$articles = $this->getDoctrine()
->getManager()
->getRepository('PMPlatformBundle:Article')
->findAllDateDesc();
/* #var $articles Article[] */
return $articles;
}
Then in my Article entity I added this annotation #Groups({"article"}) with the right use statement.
Whit default serializer I get :
[
[],
[]
]
Whit JMS serializer (bundle) I get :
{
"0": {},
"1": {}
}
(I have two articles in db)
it seems like the "article" group is not recognized. When I use the default serializer whithout this annotations I get a circular error.
What's wrong ?
[EDIT] Same behavior with
/**
* #ApiDoc(
* section="articles",
* resource=true,
* description="Get articles published"
* )
* #Rest\View()
* #Rest\Get("/articles")
*/
public function getArticlesAction(Request $request)
{
$context = new Context();
$context->addGroup('article');
$articles = $this->getDoctrine()
->getManager()
->getRepository('PMPlatformBundle:Article')
->findAllDateDesc();
/* #var $articles Article[] */
$view = $this->view($articles, 200);
$view->setContext($context);
return $view;
}
The response still empty.
You can keep the default serializer of symfony. No need of JMSSerializer.
You may have forgotten to activate the annotations of serializer in config.yml (https://symfony.com/doc/current/serializer.html#using-serialization-groups-annotations)
#app/config/config.yml
framework:
....
serializer: { enable_annotations: true }
It is necessary to force the view_response_listener in config.yml (http://symfony.com/doc/master/bundles/FOSRestBundle/3-listener-support.html, http://symfony.com/doc/master/bundles/FOSRestBundle/view_response_listener.html)
#app/config/config.yml
fos_rest:
view:
view_response_listener: 'force'
That should work !
Ok I fixed it using JMS serializer like this :
use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerBuilder;
class ArticlesController extends FOSRestController
{
/**
* #ApiDoc(
* section="articles",
* resource=true,
* description="Get articles published"
* )
* #Rest\View()
* #Rest\Get("/articles")
*/
public function getArticlesAction(Request $request)
{
$serializer = SerializerBuilder::create()->build();
$articles = $this->getDoctrine()
->getManager()
->getRepository('PMPlatformBundle:Article')
->findAllDateDesc();
/* #var $articles Article[] */
return $serializer->serialize($articles, 'json', SerializationContext::create()->setGroups(array('article')));
}
Now the groups annotations works fine.
try php bin/console cache:clear
Using your suggested answer
$serializer = SerializerBuilder::create()->build();
return $serializer->serialize($collection, 'json', SerializationContext::create()->setGroups(['group']));
returns JSON as string to me. To solve your problem, you have to have the following in your config.yml file:
jms_serializer:
metadata:
auto_detection: true
this is how it recognises mappings that you specify with your annotations in Entity files. More info on it here: docs. Having that, you can use the #View(serializerGroups={"group"}) annotation as you wanted.
P.S. Symfony 3.2.2; JMSSerializer 1.1; FOSRestBundle 2.1
Related
I have jms serializer and in my class entity have annotation. I have case when I need apply jms serializer annotation only in some case. How to do this ? First I think need move annotation from entity class to uml maybe and create some handler where I can enable this annotation for this entity, in the rest of the time jms are not applicable to entity
my entity
/**
* #Annotation\ExclusionPolicy("all")
*/
class Application implements ContainsRecordedMessages
{
/**
* #var int
*
* #Annotation\Groups({
* "get_application"
* })
* #Annotation\Expose()
*/
private $id;
/**
* #var int
*
* #Annotation\Groups({
* "post_application"
* })
* #Annotation\SerializedName("company_id")
* #Annotation\Expose()
*/
private $companyId;
/**
* #var string
*
* #Annotation\Groups({
* "post_application"
* })
* #Annotation\SerializedName("channel_sale")
* #Annotation\Expose()
*/
private $channelSale;
and I have class manager for this entity where I want enable jms and the disable jms serializer annotation
class ApplicationManager
{
public function someFunction()
{
$this->enableJmsForEntity(Application::class);
//some logic
//
$this->disableJmsForEntity(Application::class);
}
}
my goal - that jms serializer don't work in serializer proccess for response. Only in my service, wehre I create entity with deserializer function, jms serializer annotation for entity enable. Because in this old project all respons look like this
return $this->json($application, Response::HTTP_CREATED);
vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php:114
protected function json($data, $status = 200, $headers = array(), $context = array())
{
if ($this->container->has('serializer')) {
$json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array(
'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS,
), $context));
return new JsonResponse($json, $status, $headers, true);
}
return new JsonResponse($data, $status, $headers);
}
and after adding jms annotation we have problem, response in changing ..
My composer.json (part of it):
{
"require": {
"symfony/symfony": "3.1.*",
"jms/serializer-bundle": "^1.1",
"friendsofsymfony/rest-bundle": "^2.1"
}
}
I have some entities which I'd like to return partial data for the list action and complete for the find action. For so, I have these files:
Product.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* #ORM\Entity
* #ORM\Table(name="represented")
* #JMS\ExclusionPolicy("ALL")
*/
class Product
{
/**
* #var integer
* #ORM\Column(type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string", nullable=false, length=48)
*/
protected $name;
/**
* #var Group
* #ORM\ManyToOne(targetEntity="Group", inversedBy="products")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
*/
protected $group;
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setGroup(Group $group)
{
$this->group = $group;
}
public function getGroup()
{
return $this->group;
}
}
ProductController.php
<?php
namespace AppBundle\Controller;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\FOSRestController;
use AppBundle\Entity\Product;
class ProductController extends FOSRestController
{
/**
* #Get("/product", name="list_products")
*/
public function listAction()
{
$products = $this->getDoctrine()
->getRepository('AppBundle:Product')
->findBy([], [ 'name' => 'ASC' ]);
$view = $this->view($products);
return $this->handleView($view);
}
/**
* #Get("/product/{id}", requirements={"id" = "\d+"}, name="get_product")
*/
public function getAction($id)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Product')
->find($id);
if ( ! $product) {
$error = [
'error' => 'Product not found'
];
$view = $this->view($error, 404);
} else {
$view = $this->view($product);
}
return $this->handleView($view);
}
}
I would like to be able to not show the group property on the list result. For this I have tried a few of approaches, mainly with groups.
Just use configure the group name for the properties I want to show
on my list with Groups({"List"}) and refer this group on the
controller with #View(serializerGroups={"List"}). But this had no
affect as all properties are visible.
Configure #ExclusionPolicy("all") for the entire entity didn't
work as well.
In addition to ExclusionPolicy, #Expose to all properties I wanted
to show in some or all groups, but that made all properties marked
to be shown.
I also tried some more variants of these, but nothing that change the results.
End of your controller action should not be only:
return $this->handleView($view);
But for getting a group or groups working you need to activate them. On top of the class you need to add FOSRest Context, not JMS context:
use FOS\RestBundle\Context\Context;
and in the controller actions before returning view:
$view = $this->view($products, 200);
$context = new Context();
$context->setGroups(['list']);
$view->setContext($context);
return $this->handleView($view);
This will work for Symfony 3.2 and FOSRest 2.1 and 2.0.
Link for upgrading documentation for FOSRest:
https://github.com/FriendsOfSymfony/FOSRestBundle/blob/master/UPGRADING-2.0.md
If you use #Exclude then this should work. Another option I have used is to make a small addition to config.yml:
fos_rest:
serializer:
groups: ['Default']
This requires that the entity properties be in the group Default in order to be serialized. If you have no #Groups annotation on a property then it will be in Default, but as soon as you add the #Groups annotation then it will no longer be in the Default group (unless you specifically add it.)
This allowed the default serialization process to include all the entity fields except for those with a #Groups annotation, and then elsewhere I could serialize with both Default and my other groups selected if I wanted everything to be included.
// Function in the Controller class
public function getAction(MyEntity $me) {
// Use automatic serializer, only things in 'Default' group are included
return $me;
}
I have entity and I use
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
and when I delete some entity in my database I have my entity and in field deletedAt I have time when I delete entity and this is ok. But Now I need find all deleteAt entity? I create QB
$qb = $this->getEntityManager()->createQueryBuilder('d');
$qb
->select('d')
->from('ArtelProfileBundle:Project', 'd')
->where('d.deletedAt IS NOT NULL');
$count = $qb->getQuery()->getResult();
$query = $qb->getQuery();
$results = $query->getResult();
return [$results, $count];
I have 0 entity, why and how find entity ?
UPDATE
In my controller
class ProjectController extends FOSRestController
{
public function getProjectsAction(ParamFetcher $paramFetcher)
{
$manager = $this->getDoctrine()->getManager();
if($paramFetcher->get('status'))
{
$manager->getFilters()->disable('soft-deleteable');
$queryBuilder = $manager->getRepository('ArtelProfileBundle:Project')->findForStatusProject($paramFetcher, $this->getUser());
}
and I have error
Filter 'soft-deleteable' is not enabled.
my entity
/**
* Project
*
* #ORM\Table(name="project")
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
* #ORM\Entity(repositoryClass="Artel\ProfileBundle\Entity\Repository\ProjectRepository")
* #ExclusionPolicy("all")
*/
class Project
{
/////
/**
* #var \DateTime $deletedAt
*
* #ORM\Column(name="deleted_at", type="datetime", nullable=true)
* #Type("DateTime")
* #Expose()
*/
protected $deletedAt;
help please
Solved
it is simply a mismatch between the name I use in my config and in my code
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
stof_doctrine_extensions:
default_locale: "%locale%"
orm:
default:
timestampable: true
sluggable: true
softdeleteable: true
and then in my action I do
$manager->getFilters()->disable('softdeleteable');
And have the entity which I deleted
It's been a while, debugging the FilterColletion Class I found a simpler solution
$manager->getFilters()->disable('soft_deleteable'); //(using dash instead of minus sing, the last one is parsed to a dash )
I'm using couchDb in symfony 2.7.2.
I have several doubts.
Now I installed this Bundle
And I create one entity for testing
<?php
namespace foo\GarageBundle\Document;
use Doctrine\ODM\CouchDB\Mapping\Annotations as CouchDB;
/**
* #CouchDB\Document
*/
class Utente
{
/** #CouchDB\Id */
private $id;
/** #CouchDB\Field(type="string") */
private $nome;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set nome
*
* #param string $nome
* #return Utente
*/
public function setNome($nome)
{
$this->nome = $nome;
return $this;
}
/**
* Get nome
*
* #return string
*/
public function getNome()
{
return $this->nome;
}
}
In my controller I added this Code
$dm = $this->container->get('doctrine_couchdb.client.default_connection');
$doc = $this->container->get('doctrine_couchdb.odm.default_document_manager');
try{
$dm->createDatabase($dm->getDatabase());
}catch(\Exception $e){
$msg = $e->getMessage();
}
$user = new Utente();
$user->setNome('foo');
$doc->persist($user);
$doc->flush();
my config.yml is
doctrine_couch_db:
client:
default_connection: default
connections:
default:
dbname: symfony2
odm:
default_document_manager: default
document_managers:
default:
auto_mapping: true
With controller I created Database but I can't insert the new Document, I got this error
The class 'foo\GarageBundle\Document\Utente' was not found in the chain configured namespaces
And I don't understand why it is useful to use a bundle as what I am using ( I know it could be a stupid question ), and why I have to use * #CouchDB\Document instead of #Document inside my entity ?
Seems a problem related the namespace of the entity class.
The automapping is registering the CouchDocument subnamespace of
your bundle, not Document (which is auto-mapped by
DoctrineMongoDBBundle)
So use a different namespace for the User class and the other Counch you use, as follow:
namespace foo\GarageBundle\CouchDocument;
In particular:
<?php
namespace foo\GarageBundle\CouchDocument;
use Doctrine\ODM\CouchDB\Mapping\Annotations as CouchDB;
/**
* #CouchDB\Document
*/
class Utente
{
Hope this help
See this discussion on github.
/**
* #CouchDB\Document
* #CouchDB\Index
*/
class Utente
{
I have been trying out Symfony 2.2, the FOSRest Bundle (using JMS Serializer), and Doctrine ODM using MongoDB.
After many hours of trying to figure out how to correctly setup the FOSRest Bundle I'm still having some trouble: I have a very simple route that returns a list of products and prices. Whenever I request for the HTML format I get the correct response, but if I request any other format (JSON, XML) I get an error:
[{"message": "Resources are not supported in serialized data. Path: Monolog\\Handler\\StreamHandler -> Symfony\\Bridge\\Monolog\\Logger -> Doctrine\\Bundle\\MongoDBBundle\\Logger\\Logger -> Doctrine\\Bundle\\MongoDBBundle\\Logger\\AggregateLogger -> Doctrine\\ODM\\MongoDB\\Configuration -> Doctrine\\MongoDB\\Connection -> Doctrine\\ODM\\MongoDB\\LoggableCursor",
"class": "JMS\\Serializer\\Exception\\RuntimeException",...
you can see the full error message here
My current setup is very simple: I have created a single route to a controller that returns a list of products and the price (I followed this example to create the product document).
This is the route:
rest_product:
type: rest
resource: Onema\RestApiBundle\Controller\ProductController
This is the controller:
<?php
namespace Onema\RestApiBundle\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\Rest\Util\Codes;
use JMS\Serializer\SerializationContext;
use Onema\RestApiBundle\Document\Product;
class ProductController extends FOSRestController implements ClassResourceInterface
{
public function getAction()
{
$dm = $this->get('doctrine_mongodb')->getManager();
$products = $dm->getRepository('RestApiBundle:Product')->findAll();
if(!$products)
{
throw $this->createNotFoundException('No product found.');
}
$data = array('documents' => $products);
$view = $this->view($data, 200);
$view->setTemplate("RestApiBundle:Product:get.html.twig");
return $this->handleView($view);
}
}
This is the view called from the controller Resources/Product/get.html.twig:
<ul>
{% for document in documents %}
<li>
{{ document.name }}<br />
{{ document.price }}
</li>
{% endfor %}
</ul>
Any ideas why this would work correctly for one format but not the others? Anything additional I'm supposed to setup?
UPDATE:
This is the config values I have been using.
At the end of app/config/config.yml I had this:
sensio_framework_extra:
view: { annotations: false }
router: { annotations: true }
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
formats:
json: true
failed_validation: HTTP_BAD_REQUEST
default_engine: twig
view_response_listener: 'force'
WORKAROUND:
Doing a bit more research I ran into another error which lead me to this questions and answer:
https://stackoverflow.com/a/14030646/155248
Once I got rid of the Doctrine\ODM\MongoDB\LoggableCursor by adding every result to an array like this:
$productsQ = $dm->getRepository('RestApiBundle:Product')->findAll();
foreach ($productsQ as $product) {
$products[] = $product;
}
return $products;
I started getting the results in the correct format. This is kind of a lame solution and still hope to find a better answer to this issue.
If you want to get a colection of RestApiBundle:Product documents, you MUST call the method "toArray" after calling find method from repository or getQuery method from query builder
/**
* #Route("/products.{_format}", defaults={"_format" = "json"})
* #REST\View()
*/
public function getProductsAction($_format){
$products = $this->get('doctrine_mongodb')->getManager()
->getRepository('RestApiBundle:Product')
->findAll()->toArray();
return $products;
}
also you can call array_values($products) for correct serialization of exclude strategy
Most likely the error lies somewhere in your config files or perhaps lack of? Add your configs and if I can I will update my answer.
For now I'll walk you through a simple implementation.
First lets start with the configs:
Note: I'm going to be using annotations for some of the settings see SensioFrameworkExtraBundle.
#app/config/config.yml
sensio_framework_extra:
view:
annotations: false
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
view_response_listener: 'force'
First we setup sensio extra bundle. The default config enables annotations are set to true. I disabled annotations for the view(I won't be using them here).
For fos_rest we are setting up the Listeners, we're keeping it simple so we're going to use the example from their docs.
We will create the entity:
<?php
namespace Demo\DataBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints;
use JMS\Serializer\Annotation\ExclusionPolicy; //Ver 0.11+ the namespace has changed from JMS\SerializerBundle\* to JMS\Serializer\*
use JMS\Serializer\Annotation\Expose; //Ver 0.11+ the namespace has changed from JMS\SerializerBundle\* to JMS\Serializer\*
/**
* Demo\DataBundle\Entity\Attributes
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Demo\DataBundle\Entity\AttributesRepository")
*
* #ExclusionPolicy("all")
*/
class Attributes
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var string $attributeName
*
* #ORM\Column(name="attribute_name", type="string", length=255)
*
* #Expose
*/
private $attributeName;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set attributeName
*
* #param string $attributeName
* #return Attributes
*/
public function setAttributeName($attributeName)
{
$this->attributeName = $attributeName;
return $this;
}
/**
* Get attributeName
*
* #return string
*/
public function getAttributeName()
{
return $this->attributeName;
}
}
You will notice a couple of annotation settings. First we set #ExclusionPolicy("all") then we manually set which object we want to #Expose to the API. You can find out more about this here and a list of JMS Serializer annotations
Now lets move on to a simple controller:
<?php
namespace Demo\DataBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest; //Lets use annotations for our FOSRest config
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\Rest\Util\Codes;
use Symfony\Component\HttpFoundation\Request;
use Demo\DataBundle\Entity\Attributes;
class AttributesController extends FOSRestController implements ClassResourceInterface
{
/**
* Collection get action
* #var Request $request
* #return array
*
* #Rest\View()
*/
public function cgetAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('DemoDataBundle:Attributes')->findAll();
return array(
'entities' => $entities,
);
}
}
This is a simple controller that will return everything.
Hopefully this helped. I think your error is coming from a bad setting related to the Serializer. Make sure your exposing some data.