FOSRestBundle annotations not working - symfony

FOSRestBundle annotations are not working for me somehow.
use FOS\RestBundle\Controller\Annotations as Rest;
// ...
/**
* #Rest\RequestParam(name="email")
*/
public function someAction(Request $request)
{
// some code
}
Here's my config:
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: false
routing_loader:
default_format: json
view:
view_response_listener: 'force'
formats:
xml: false
json : true
default_engine: none
templating_formats:
html: true
access_denied_listener:
json: true
allowed_methods_listener: true
Requests to this action ignores annotation and just executes method's code. It seems that listener that should resolve these annotations is not running. Any suggestions?

change param_fetcher_listener: true to param_fetcher_listener: force and move code to:
use FOS\RestBundle\Request\ParamFetcher
/**
* #Rest\RequestParam(name="email")
*/
public function someAction(ParamFetcher $paramFetcher) {
$email = $paramFetcher->get('email');
}
Note: Request parameter must be passed as POST

Related

Symfony - log to multiple log files in a service

I'm trying to inject multiple monolog handler into a service. Right now my parent class injects a logger and the children class injects another logger. My goal is it to be able to log specific actions to specific log files.
My service.yaml:
App\Services\PrinterManager:
arguments: ['#doctrine.orm.entity_manager','#logger', '', '', '', '','']
tags:
- { name: monolog.logger, channel: printerface}
App\Services\Printer\Printer:
autowire: true
autoconfigure: false
public: false
parent: App\Services\PrinterManager
arguments:
index_2: '#logger'
index_3: '#oneup_flysystem.printer_invoice_filesystem'
index_4: '#oneup_flysystem.printerface_content_filesystem'
index_5: '#oneup_flysystem.sftp_filesystem'
index_6: '#App\Services\PrinterApiService'
tags:
- { name: monolog.logger, channel: printerlog}
My monolog.yaml:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event, !printerface", "!printerlog"]
printerface:
type: stream
level: debug
channels: ["printerface"]
path: "%kernel.logs_dir%/printerface.log"
printerlog:
type: stream
level: debug
channels: ["printerlog"]
path: "%kernel.logs_dir%/printerlog.log"
But it seems that the current service configuration breaks the constructor and I get the following error:
The argument must be an existing index or the name of a constructor's parameter.
Is there any way to use two log files in a service?
I've not done it with a parent/child class, but with something a little simpler I'm using named parameters, this is what I have (with three different loggers):
# App/Subscribers/WebhookLoggingListener.php file
public function __construct(
LoggerInterface $logger,
LoggerInterface $mailgunLog,
LoggerInterface $dripLog) {
}
# services.yml
App\Subscribers\WebhookLoggingListener:
arguments:
$logger: "#logger"
$mailgunLog: "#monolog.logger.mailgun"
$dripLog: "#monolog.logger.drip"
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
If I was using the other loggers elsewhere I could also bind them to specific variable names:
services:
_defaults:
# ... other config
bind:
$dripLog: "#?monolog.logger.drip"
This is the method that Symfony is using to replace parent's arguments in a child service:
/**
* You should always use this method when overwriting existing arguments
* of the parent definition.
*
* If you directly call setArguments() keep in mind that you must follow
* certain conventions when you want to overwrite the arguments of the
* parent definition, otherwise your arguments will only be appended.
*
* #param int|string $index
* #param mixed $value
*
* #return self the current instance
*
* #throws InvalidArgumentException when $index isn't an integer
*/
public function replaceArgument($index, $value)
{
if (\is_int($index)) {
$this->arguments['index_'.$index] = $value;
} elseif (0 === strpos($index, '$')) {
$this->arguments[$index] = $value;
} else {
throw new InvalidArgumentException('The argument must be an existing index or the name of a constructor\'s parameter.');
}
return $this;
}
As you can see, the indexes must be eighter same the argument variable names in the parent's constructor with a prefixed $ or an integer indicating the related argument.
So I think you must define your child service as below:
App\Services\Printer\Printer:
autowire: true
autoconfigure: false
public: false
parent: App\Services\PrinterManager
arguments:
2: '#logger'
3: '#oneup_flysystem.printer_invoice_filesystem'
4: '#oneup_flysystem.printerface_content_filesystem'
5: '#oneup_flysystem.sftp_filesystem'
6: '#App\Services\PrinterApiService'
tags:
- { name: monolog.logger, channel: printerlog}
Update:
After I reproduced your problem, I figured out that the solution is as below. With this solution, the Symfony autowiring will work for the child service.
App\Services\Printer\Printer:
autowire: true
autoconfigure: false
public: false
parent: App\Services\PrinterManager
arguments:
$arg2: '#logger'
$arg3: '#oneup_flysystem.printer_invoice_filesystem'
$arg4: '#oneup_flysystem.printerface_content_filesystem'
$arg5: '#oneup_flysystem.sftp_filesystem'
$arg6: '#App\Services\PrinterApiService'
tags:
- { name: monolog.logger, channel: printerlog}
$arg2, $arg3, $arg4, $arg5 and $arg6 must be replaced by your class constructor's argument names.

how to use default locale in symfony3.4 routes

I want to open my websites like these:
http://127.0.0.1:8000/ (Works)
http://127.0.0.1:8000/en/ (Works)
http://127.0.0.1:8000/en/mrg (Works)
http://127.0.0.1:8000/mrg (Doesn't Work)
So I put this code in routing.yml:
teach:
resource: "#TeachBundle/Controller/"
type: annotation
prefix: /{_locale}
requirements:
_locale: "|en|fa"
and my controller is like this:
/**
* #Route("/mrg")
*/
public function mrgAction(Request $request)
{
$lang=$request->getLocale();
return new Response("<html><body>Your language: <b> $lang </b></body></html>");
}
All urls worked but http://127.0.0.1:8000/mrg doesn't work and returns:
No route found for "GET /mrg"
I need use default language for example if I try to open http://127.0.0.1:8000/mrg then open http://127.0.0.1:8000/en/mrg.
Is there any solution to fix this problem?
Add defaults option to your routing configuration to set the default locale if it's not set in your controllers #Routes:
teach:
resource: "#TeachBundle/Controller/"
type: annotation
prefix: /{_locale}
requirements:
_locale: "|en|fa"
defaults:
_locale: 'en' # or '%locale%'
Try this:
{_locale}
/**
* #Route("/{_locale}/mrg")
*/
public function mrgAction(Request $request)
{
$lang=$request->getLocale();
return new Response("<html><body>Your language: <b> $lang </b></body></html>");
}
Setting a Default Locale
# app/config/config.yml
framework:
default_locale: en
If doesn't work, Can You show your app/config/routing.yml.
I advice you to use : BeSimpleI18nRoutingBundle
https://github.com/BeSimple/BeSimpleI18nRoutingBundle
use BeSimple\I18nRoutingBundle\Routing\Annotation\I18nRoute;
class NoPrefixController
{
/**
* #I18nRoute({ "en": "/welcome", "fr": "/bienvenue", "de": "/willkommen" }, name="homepage")
*/
public function indexAction() { }
}

Custom exception with fosrest bundle in symfony2

Hi I am trying to add custom exception but it is not working
now my response is like:(also the breakdown debuger is not trigger that class)
{
"error": {
"code": 500,
"message": "Required Parameter Missing"
}
}
this is the default exception of fos rest
I added new class that wrapp the exception
class ExceptionWrapperHandler implements ExceptionWrapperHandlerInterface {
public function wrap($data)
{
$exception = $data['exception'];
$newException = array(
'success' => false,
'exception' => array(
'exceptionClass' => $exception->getClass(),
'message' => $data['status_text']
)
);
return $newException;
}
}
my config file:
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
routing_loader:
default_format: json
view:
view_response_listener: force
formats:
json: true
xml: true
templating_formats:
html: false
# exception_wrapper_handler: CLASS path
exception:
enabled: true
service:
exception_handler: appname.exception_handler
serializer: jms_serializer.serializer
serializer:
serialize_null: true
You need to add your Exception class to the exception.messages array in your fos_rest config in your config.yml for it to show your custom message
fos_rest:
exception:
enabled: true
messages:
'AppBundle\ExpeptionHandler\ ExceptionWrapperHandler': true

Merge ParamConverter and BodyConverter objects on FOSRestBundle

I'm using the param converter to get object from url and body converter to get object form body.
This perfectly works for GET and POST methods, but I have to do it with a tricky way for an update (PUT):
/**
* #param PowerDNSDomain $domain
* #param PowerDNSRecord $record
* #param PowerDNSRecord $updatedRecord
* #param ConstraintViolationListInterface $validationErrors
*
* #ParamConverter("updatedRecordData", converter="fos_rest.request_body")
*
* #return View
*/
public function putAction(PowerDNSDomain $domain, PowerDNSRecord $record, PowerDNSRecord $updatedRecord, ConstraintViolationListInterface $validationErrors)
{
if ($validationErrors->count() > 0) {
return $this->handleBodyValidationErrorsView($validationErrors);
}
$record->setName($updatedRecord->getName().'.'.$domain->getName())
->setContent($updatedRecord->getContent())
->setTtl($updatedRecord->getTtl())
->setPrio($updatedRecord->getPrio());
$this->get('manager.dns')->saveRecord($record);
return $this->view($record);
}
In a nutshell, I have to retrieve two PowerDNSDomain object (one from the URL, one from the body) and update each fields manually.
This is overkill, isn't it?
The preferred way would be to have a method signature like this:
public function putAction(PowerDNSDomain $domain, ConstraintViolationListInterface $validationErrors)
Where the PowerDNSDomain $domain would be the result of the body converter object merged into the param converter one.
In this case, I will just have to get validation errors and save the object.
How can I make this possible ? :-)
Related configuration:
fos_rest:
routing_loader:
default_format: json
body_converter:
enabled: true
validate: true
serializer:
serialize_null: true
view:
formats:
xml: false
json: true
rss: false
yml: true
view_response_listener: force
param_fetcher_listener: force
format_listener:
rules:
- { path: '^/api/', priorities: ['json', 'yml'], fallback_format: json, prefer_extension: true }
- { path: '^/', stop: true } # FOSRest should not handle other routes than API
media_type:
enabled: true
Possibly related issue: https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1238

how to define variable in yaml symfony2

I'm not really familiar with YAML so I open parameters.yml and config.yml files to see example how to use parameters or variable in YAML.
parameters.yml:
parameters:
database_driver: pdo_mysql
database_host: 127.0.0.1
database_port: 3306
database_name: homlist
config.yml:
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
But when I tried it with doctrine mapping yaml file like this:
parameters:
table_name: test
Mockizart\Bundle\BlogBundle\Entity\MockblogTag:
type: entity
table: "%table_name%"
it's error like this:
An exception occurred while executing 'SELECT count(DISTINCT %0_.id) AS sclr0 FROM %table_name% %0_':
this is my mapping file Resources\Config\Entity\MockblogTag
Mockizart\Bundle\BlogBundle\Entity\MockblogTag:
type: entity
table: mockblog_tag
indexes:
user_id:
columns:
- user_id
name:
columns:
- name
slug:
columns:
- slug
id:
id:
type: integer
nullable: false
unsigned: false
comment: ''
id: true
generator:
strategy: IDENTITY
fields:
dateCreated:
type: integer
nullable: false
unsigned: false
comment: ''
column: date_created
name:
type: string
nullable: false
length: 60
fixed: false
comment: ''
slug:
type: string
nullable: false
length: 100
fixed: false
comment: ''
totalPost:
type: integer
nullable: false
unsigned: false
comment: ''
column: total_post
manyToOne:
user:
targetEntity: ORD\UserBundle\Entity\User
joinColumn:
referencedColumnName: id
type: integer
nullable: false
unsigned: false
lifecycleCallbacks:
How to define variable in yaml symfony2 ?
The way of defining parameters it's correct, however I see from comments that your purpose is to configure the class used for User object:
As Cerad said you can't do that. But if you want to configure the class you use for the User, you can have a manager service class.
<?php
namespace YourNamespace\UserBundle\Manager;
use Doctrine\Common\Persistence\ObjectManager;
class UserManager
{
/**
* #var ObjectManager
*/
protected $em;
/**
* Your user class
*
* #var string
*/
protected $className;
public function __construct(ObjectManager $em, $class)
{
$this->em = $em;
$this->className = $class;
}
public function createInstance()
{
return new $this->className;
}
public function getRepository()
{
return $this->em->getRepository($this->className);
}
}
And the services definitions will be like this:
services:
your_user.manager:
class: YourNamespace\UserBundle\Manager\UserManager
arguments: ['#doctrine.orm.entity_manager', 'YourNamespace\UserBundle\Entity\User']
In your controller you can use this manager class like this:
$userManager = $this->get('your_user.manager');
$user = $userManager->createInstance();
I think this is a good way to have a central point when dealing with user object. And if someday for whatever reason you decide to use a different class for user you just modify the argument 'YourNamespace\UserBundle\Entity\User'.
Also in this way you can use 'YourNamespace\UserBundle\Entity\User' argument as parameter, so the definition will change to:
services:
your_user.manager:
class: Moneytablet\UserBundle\Manager\UserManager
arguments: ['#doctrine.orm.entity_manager', '%user_class%']
and in you parameters.yml you can have:
parameters:
user_class: YouNamespace\UserBundle\Entity\User
I really like working this way, you can create save(), remove() methods on manager class and so on. Also later on when creating new services you can inject this manager like a regular service if it's a dependency.
And if you want a new manager for a different entity, you can create a new service definition with different construct arguments, but with the same service class.

Resources