symfony2: documentation generated by NelmioApiDocBundle shows an additional route - php

I'm using NelmioApiDocBundle and FOSRestBundle to create an API.
In my routing.yml I have set prefix to /api.
The routes in my ApiController class are configured as follows:
/**
* #Get("/login/{username}/{password}")
* #ApiDoc(
* description="User Loggin",
* resource=true,
* parameters={
* {"name"="username", "dataType"="string", "required"=true, "description"="Username"},
* {"name"="password", "dataType"="string", "required"=true, "description"="Password"},
* }
* )
*/
public function loginAction($username, $password)
{
// ...
}
My problem is that the generated documentation shows these two routes:
/api/login/{username}/{password}
/login/{username}/{password}
I only want the route prefixed with /api to show up.
How can I achieve that ?

It's likely that NelmioApiDocBundle isn't aware of the prefix setting in your routing.yml.
As the bundle usually expects annotations you should add the #Prefix annotation at class-level instead.
use FOS\RestBundle\Controller\Annotations\Prefix;
/**
* #Prefix("/api")
*/
class ApiController
Don't forget to clear your cache after implementing the changes.

I found a solution, i override the template resources.html.twig of NelmioBundle and check manually if the uri contains /api or not

Related

Symfony - Open API 3 - Set JSON response as example in controlller

Im using Open Api 3 (exactly NelmioApiDocBundle) in Symfony to document APIs and I have the following code:
* #Route("/login", name="user_login", methods={"POST"})
*
* #OA\Response(
* response=200,
* #OA\JsonContent(
* type="object",
* #OA\Property(property="code", type="number"),
* #OA\Property(property="error", type="boolean"),
* #OA\Property(property="message", type="string")
* )
* )
This works and shows me the following:
What I want to know is if there is an easier way to define these responses so I don't have to write so many lines at each endpoint.
I wish there was a way to define a JSON file as an interface and have it read from there. I've tried a thousand things but nothing works for me and I don't know how to do it and I've already given up.
Thanks a lot!
I have tried to use #Model, #Schema, modify YAML files... etc... I have read the documentation, but I have not seen any valid example for my case in this version of Open Api.
If you use models, you can put in the following
#OA\JsonContent(ref=#Model(type=LoginResponse::class))
Your response class should be something like this:
use OpenApi\Annotations as OA;
class LoginResponse
{
/** #OA\Property(type="number") */
public $code;
/** #OA\Property(type="boolean") */
public $error;
/** #OA\Property(type="string") */
public $message;
}
Make sure the properties are publicly accessible and you have symfony/property-info installed. You can also use jms/serializer, but that's a bit more work
More info at: https://symfony.com/bundles/NelmioApiDocBundle/current/index.html#use-models

Turn off pluralisation for an endpoint in APi Platform

In a Symfony 5 project we're using the APi Platform to generate a REST API.
One of the entity classes is called FarmMetadata.
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
/**
* #ApiResource()
* #ORM\Table(... some settings ...)
* #ORM\Entity
*/
class FarmMetadata
{
// properties and methods
}
When I run php bin/console debug:router it shows the following routes for this resource:
api_farm_metadatas_get_collection GET ANY ANY /api/farm_metadatas.{_format}
api_farm_metadatas_post_collection POST ANY ANY /api/farm_metadatas.{_format}
api_farm_metadatas_get_item GET ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_delete_item DELETE ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_put_item PUT ANY ANY /api/farm_metadatas/{id}.{_format}
api_farm_metadatas_patch_item PATCH ANY ANY /api/farm_metadatas/{id}.{_format}
However the word "metadata" is already plural. There's no such thing as metadatas. How can I turn off the pluralisation for this endpoint?
I tried using shortName:
* #ApiResource(
* shortName="FarmMetadata" // also "farm_metadata"
* )
but it doesn't change the output.
If I use:
* #ApiResource(
* shortName="Metadata"
* )
then the route names and paths are changed:
api_metadata_get_collection GET ANY ANY /api/metadata.{_format}
api_metadata_post_collection POST ANY ANY /api/metadata.{_format}
api_metadata_get_item GET ANY ANY /api/metadata/{id}.{_format}
api_metadata_delete_item DELETE ANY ANY /api/metadata/{id}.{_format}
api_metadata_put_item PUT ANY ANY /api/metadata/{id}.{_format}
api_metadata_patch_item PATCH ANY ANY /api/metadata/{id}.{_format}
but that's not what I want.
I know that I can declare a path for every operation, but that would hurt the DRY principle.
How can I achieve the desired behaviour?
You could use "path" option on each operation.
Cf https://api-platform.com/docs/core/operations/#configuring-operations
For example
* shortName="Metadata",
* itemOperations={
* "get"={
* "path"="/metadata/{id}"
I don't think this is possible by configuration: these routes are built in the private method ApiPlatform\Core\Bridge\Symfony\Routing\ApiLoader::addRoute (at least in v2.6 which I'm using), and this uses a static call to a pluralizer - so: decorating the ApiLoader is not easily possible (as the addRoute method is private), and exchanging the ways of generating the route is not possible (due to the usage of a static method call).
Looks like you need to open a feature request ticket in their bug tracker...
you can do easily as you want
api_platform:
...
path_segment_name_generator: App\InfraStructure\ApiPlatform\Core\SingularPathSegmentNameGenerator
create SingularPathSegmentNameGenerator
<?php
declare(strict_types=1);
namespace App\InfraStructure\ApiPlatform\Core;
use ApiPlatform\Core\Operation\PathSegmentNameGeneratorInterface;
use ApiPlatform\Core\Util\Inflector;
final class SingularPathSegmentNameGenerator implements PathSegmentNameGeneratorInterface
{
public function getSegmentName(string $name, bool $collection = true): string
{
return Inflector::tableize($name);
}
}

Symfony 3 - Route excluding beginning of specific path (url)

I'm creating an application with Symfony 3.2.9, and I would to do a Admin panel to manage application. Application works like CMS, so is creating new pages with URL, like domain.com/pagename1 and also domain.com/pagename1/subpagelevel2 ect. Problem is when I want to create URL for Admin panel, and URL should looks like: domain.com/admin, but also admin panel need some sub pages, like domain.com/admin/manage or domain.com/admin/manage/edit/1 ect.
I created DefaultController with routing like :
/**
* #Route("/", name="homepage")
*/
and AdminController with routing like :
/**
* #Route("/admin", name="admin")
*/
Problem is that when I want to dynamically create new sub page of application I need to create routing like:
/**
* #Route("/{page}")
*/
But this overwrite my Admin panel sub pages (eg. domain.com/admin/manage).
Is it way, to exclude or overwrite path from default DefaultController by AdminController? I want to have possibility to create all URL-s from DefaultController excepts paths beginning like domain.com/admin ... and so on.
From documention in https://symfony.com/doc/current/routing.html you can use the requirements parameter to specify a more strict match
I guess something like this whould work:
DefaultController:
/**
* #Route("/", name="homepage")
*/
AdminController:
/**
* #Route("/admin", name="admin")
*/
Other Controller:
/**
* we exclude page=admin from this controller
* #Route("/{page}", requirements={"page": "^(?!admin).+"}))
*/
Routes are searched in the order they are listed - so put the most generic at the end of the list, and it will find and use /admin before /{page}
For example, one of my last routes at the bottom of app/conf/routing.yml is
# http://symfony.com/doc/current/routing/redirect_trailing_slash.html
remove_trailing_slash:
path: /{url}
defaults:
_controller: AppBundle:Default:removeTrailingSlash
requirements:
url: .*/$
methods: [GET]
Cleanest on your use case:
Why not simply create a separate Bundle for Admin and put a prefix in AdminBundle routes?
Depend on routing orders and/or REGEX in routes is not recommended just to avoid a Bundle Creation. This is for what Bundles have been thought.
app/config/routing.yml
admin:
resource: "#AdminBundle/Controller/"
type: annotation
prefix: /admin
Then, all controllers/routes under AdminBundle will work under /admin prefix.
For example, IndexController/DefaultController/WhatEverController with this route inside AdminBundle:
/**
* #Route("/")
*/
public function indexAction()
{
//My code
}
Will match "/admin" instead of "/"
And:
/**
* #Route("/{page}")
*/
public function indexAction()
{
//My code
}
Will match "/admin/{page}" instead of "/{page}"
Since Symfony 5.1, you can define route priority :
/**
* #Route("/admin", name="admin", priority=10)
*/
/**
* #Route("/{slug}", name="pages", priority=99)
*/

Symfony 2 route with no custom controller code

I have my routes defined using annotations in my SF2 application, however there are a handful of pages which although they have a distinct route and Twig template they require no controller code whatsoever which leads to empty methods such as this:
/**
* #Route(
* "/courselimit",
* name = "course_limit"
* )
* #Template("CRMPiccoBundle:Course:Limit.html.twig")
*
* #param Request $request
*
* #throws \Exception
*/
public function courseLimitAction(Request $request)
{
}
This, to me, seems pointless and messy. Is there a way to avoid this in SF2 without converting all my routes to be managed in YAML files?
You would edit app/config/routing.yml like so:
# app/config/routing.yml
course_limit:
path: /courselimit
defaults:
_controller: FrameworkBundle:Template:template
template: path/Limit.html.twig
Examples are shown in the Render Template without a custom Controller:
http://symfony.com/doc/2.7/templating/render_without_controller.html

Cache variable in controller in Symfony2

Is it possible to store data in one private variable of a controller (Symfony2)?
One example:
/**
* Class CatsController
*
* #Route("cats")
* #Cache(expires="+600 seconds", public=true)
* #package oTeuGato\AppBundle\Controller
*/
class CatsController extends Controller {
/**
* #var $advertisements Advertisement[]
*/
private $advertisements;
/**
* Index advertisements page
*
* #Route("", name="oTeuGato_Cats")
* #Method("GET")
* #return Response
*/
public function indexAction()
{
$this->advertisements = ....(Use a service for gets advertisements)
}
/**
* Index advertisements by page
*
* #Route("/{id}", requirements={"id" = "\d+"}, defaults={"id" = 1}, name="oTeuGato_Cats_ByPage")
* #Method("GET")
* #return Response
*/
public function indexByPageAction(){
....
}
In this example whenever someone calls the URL: cats/1 in the controller I need them to have all advertisements of the previously called method (/cats).
Is this possible?
Note:
I enabled the cache in the app.php file and app_dev.php.
Thanks for help and sorry for my English ;)
Symfony doesn't provide a mechanism for what you are describing. But any solution that would work for PHP more generally, will work for Symfony.
It depends if you want to remember advertisements for each user or for all users. If you want to remember it for each user, use sessions as Gareth Parker suggested. If you want to remember it for all users, then you would need APC user caching, memcache or another memory-based key-value store.
You may also have luck using Doctrine result cache. See http://doctrine-orm.readthedocs.org/en/latest/reference/caching.html
No, it's not. Not like that, anyway. What you want is to use sessions instead. Sessions are what you use to store variables between requests. Here are some examples

Categories