I am trying to make a link that resends a confirmation token to a user after registering in Symfony3.
However, I get a deprecation message as follows:
User Deprecated: The "fos_user.mailer" service is private, getting it
from the container is deprecated since Symfony 3.2 and will fail in
4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.
Here is my controller:
public function resendActivationEmail($token, UserManagerInterface $userManager)
{
$user = $userManager->findUserByConfirmationToken($token);
if (is_null($user)) {return $this->redirectToRoute('fos_user_registration_register');}
$mailer = $this->get('fos_user.mailer');
$mailer->sendConfirmationEmailMessage($user);
return $this->redirectToRoute('fos_user_registration_check_email');
}
My services.yml:
services:
# default configuration for services in *this* file
_defaults:
autowire: truesubscribers, etc.
autoconfigure: true
public: false
I looked into the docs, it says that in Symfony3.4, services are private by default. I am using autowiring in my app, so how I get the fos_user.mailer without any deprecation warnings?
I tried setting Fosuserbundle services to public, doesnt help:
services.yml:
....
FOS\UserBundle:
public: true
Any help appreciated!
It's better to use DependencyInjection instead of call container directly. You should pass your mailer to your method:
public function resendActivationEmail($token, UserManagerInterface $userManager, \FOS\UserBundle\Mailer\MailerInterface $mailer)
{
$user = $userManager->findUserByConfirmationToken($token);
if (is_null($user)) {return $this->redirectToRoute('fos_user_registration_register');}
$mailer->sendConfirmationEmailMessage($user);
return $this->redirectToRoute('fos_user_registration_check_email');
}
For more informations about dependencyInjection : https://symfony.com/doc/3.4/service_container/injection_types.html
Use $mailer = $this->container->get('fos_user.mailer');
instead of $mailer = $this->get('fos_user.mailer');
Related
Here is a method signature in the controller :
public function myAction(Request $request, SessionInterface $session, LoggerInterface $logger)
This is what I wrote in services.yml :
AppBundle\Controller\:
resource: '../../Controller'
public: true
tags: ['controller.service_arguments']
Normally, all of the parameters (request, session and logger) are automatically injected.
I call this action of the controller using Ajax but there is a problem for logger (request and session are well injected):
Controller myAction requires that you provide a value for the "$logger" argument
This happens for one method only because the ajax calls to some other methods of the controller work perfectly.
The only difference I can see using some debug tools is that the one that does not work has connection : close though the others have connection: keep-alive.
I dont see why it is done differently.
When I erase the LoggerInterface parameter, it works good and connection is keep-alive like the others.
Can you help ?
Symfony 3 platform
services:
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
I have a some Controllers, Twig Extensions and other classes that all need caching. I'm using Redis as a cache. Currently I setup a new RedisCache in each of these places, like so:
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
$this->cache = new RedisCache(RedisAdapter::createConnection(getenv('REDIS_URL')), 'ImageHelper');
}
But this creates many connections to the Redis backend, which I believe is not good for performance.
What is the best way to either share a RedisConnection/Client between my Controllers/extensions/classes, or even share the RedisCache, but keep namespacing ability?
I'm using Symfony 4.
You should make the Redis cache a service, and inject it into your controllers (or other dependents) later on. Refer to Symfony's dependency injection docs if needed.
Here's an example, you might need to fine-tune it later on:
# config/services.yaml
services:
redis_connection:
class: 'RedisConnection'
factory: ['RedisAdapter', createConnection]
arguments:
- '%env(REDIS_URL)%'
redis_cache:
class: 'RedisCache'
arguments:
- '#redis_connection'
Note that I don't know which namespaces your classes have, so you'd have to adjust the config accordingly.
And then you would also have to set up the framework to inject the service to your controllers (or other dependents):
# config/services.yaml
services:
# ...
App\Controller\ExampleController:
arguments:
- '#logger'
- '#redis_cache'
Also, you should update the controllers to accept the new argument in constructor, like so:
public function __construct(LoggerInterface $logger, RedisCache $cache)
{
$this->logger = $logger;
$this->cache = $cache;
}
Refer to the service container documentation if you have any questions.
I am starting to work with services in Symfony and therefore created the example service from the symfony documentation:
namespace AppBundle\Service;
use Psr\Log\LoggerInterface;
class MessageGenerator
{
private $logger;
public function __construct(LoggerInterface $logger){
}
public function getMessage()
{
$this->logger->info('Success!');
}
}
I call that service in my controller (I also have the use Statement:
: use AppBundle\Service\MessageGenerator;
$messageGenerator = $this->get(MessageGenerator::class);
$message = $messageGenerator->getMessage();
$this->addFlash('success', $message);
My service is defined in the services.yml file:
app.message_generator:
class: AppBundle\Service\MessageGenerator
public: true
so in my eyes I did everything exactly as described in the documentation and when calling:
php app/console debug:container app.message_generator
in my commandline I get my service:
Option Value
------------------ ------------------------------------
Service ID app.message_generator
Class AppBundle\Service\MessageGenerator
Tags -
Scope container
Public yes
Synthetic no
Lazy no
Synchronized no
Abstract no
Autowired no
Autowiring Types -
Now when I execute the controller function where I call my service I still get the error:
You have requested a non-existent service "appbundle\service\messagegenerator".
Any ideas?
Symfony is a bit confusing at naming: you retrieve the service by requesting it by its defined name: app.message_generator.
$messageGenerator = $this->get('app.message_generator');
Symfony has recently suggested switching from a give-name (app.message_generator) that you are defining the service as, to the class name (AppBundle\Service\MessageGenerator). They are both just 'a name' to call the service.
You are trying to use both, when only the given name is defined.
In the long term, it's suggested to use the ::class based name, and quite possibly allow the framework to find the classes itself, and configure them itself too. This means that, by default, all services are private, and are handled by the framework & it's service container.
In the meantime, while you are learning, you can either:
$messageGenerator = $this->get('app.message_generator');
or define explicitly define the service, and make it public, so it can be fetched with ->get(...) from the container.
# services.yml
AppBundle\Service\MessageGenerator:
class: AppBundle\Service\MessageGenerator
public: true
# php controller
$messageGenerator = $this->get(MessageGenerator::class);
or just injected automatically into the controller, when that is requested
public function __construct(LoggerInterface $logger, MessageGenerator $msgGen)
{
$this->messageGenerator = $msgGen;
}
public function getMessage()
{
$result = $this->messageGenerator->do_things(....);
$this->logger->info('Success!');
}
I am using rollbar.com to collect all details about exceptions in symfony2 app. However I don't understand how can I configure monolog so it would pass username and user id to rollbar.
I see that I can pass rollbar config as shown here and I am thinking person_fn is what I need. Still I don't know where to put this function (this should be in service because I need to check security token) and how to pass it to rollbar.
# config_prod.yml
rollbar:
type: rollbar
level: error
token: %rollbar_token%
config:
person_fn: getUserForRollbarRightAboutNowOrSomething
Found solution:
update monolog/monolog bundle to at least 1.17.0 version.
create ContextProcessor and update user information
#src/AppBundle/Monolog/RollbarContextProcessor
namespace AppBundle\Monolog;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class RollbarContextProcessor
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function processRecord($record)
{
if ($this->tokenStorage->getToken()) {
$user = $this->tokenStorage->getToken()->getUser();
if ($user instanceof User) {
$record['context']['payload']['person'] = [
'id' => $user->getId(),
'username' => $user->getUsername(),
'email' => $user->getEmail(),
];
}
}
return $record;
}
}
configure ContextProcessor as service with monolog.processor tag.
# app/config/config_prod.yml
services:
monolog.processor.rollbar_context:
class: AppBundle\Monolog\RollbarContextProcessor
arguments: [#security.token_storage]
tags:
- { name: monolog.processor, method: processRecord, handler: rollbar }
monolog:
handlers:
rollbar:
type: rollbar
level: error
token: %rollbar_token%
Your question has two parts:
Rollbar
person_fn is exactly what you need. You should be able to add a reference to the function by using a string (e.g.: "MyClass::static_function_reference" or "my_function_name").
Symfony
Disclaimer: I don't use or know much about Symfony.
This question has some excellent examples of how to get the current user in Symfony. (Punch line: in a controller you can call $this.getUser())
This question has a good example of how to inject the current user in a service. (Make a Twig Extension that depends on the SecurityContext or TokenStorage, use those dependencies to get a user objet).
Finally, there's the classic PHP move: as soon as you have a user add it to $_REQUEST. I'm not sure if Symfony co-opts this, but it'd be a valid way in a non-framework PHP application.
I used this version to implement the 2 bundles. However, I want the user to set a password for his account so after successful authentification and user creation, he'll be refirected to a form where he'll enter that. Since FOSUBUserProvider is a service, I was thinking to make another service that will handle the password form. I injected the second service into the first one but I need #templating which I've set up as a parameter but I have no idea how to take it and I'm getting a warning that he's missing. How do I solve this?
# /FOSUBUserProvider
$passwordSetter = Controller::get('register_social_password_picker');
I understand that it needs a second parameter(for templating), where do I take it from? Or am I approaching this the wrong way?
class RegisterPassword
{
protected $user;
protected $templating;
public function __construct($user, $templating)
{
$this->user = $user;
$this->templating = $templating;
}
...
}
services.yml
my_user_provider:
class: AppBundle\Security\Core\FOSUBUserProvider
arguments: [#fos_user.user_manager,{facebook: facebook_id, twitter: twitter_id, linkedin: linkedin_id}, "#register_password" ]
register_password:
class: AppBundle\Service\RegisterPassword
arguments: ["#templating" ]
LE:
services.yml
register_password:
class: AppBundle\Service\RegisterPassword
arguments: [ setMailer, ["#templating"] ]
class RegisterPassword
{
protected $user;
protected $templating;
public function __construct( )
{
}
public function setPassword(User $user, $templating)
{
$this->templating = $templating;
...
}
Your problem is that the service constructor is expecting two arguments ($user and $templating) and you are passing only one (#templating). These parameters are passed when the service is built, so my guess is that you do not want $user to be a parameter for the constructor (at this point you do not know which user is going to use the service), so just pass the $templating parameter and you can add the user later on with a different call.
Since this seems to generate a circular reference problem, you may use one of three different strategies:
Inject the full service container and get the templating service when you need it (not recommended)
Use setter injection (http://symfony.com/doc/current/components/dependency_injection/types.html)
Since this calls the setter method after the service is built my guess is that the circular reference will be avoided (not 100% sure)
If this does not work, create the setter method but do not call it at service construction point but rather call it later when you want to use the service
However, the approach I tried was a little off, the redirection after login can be easily set in security.yml>security>firewalls>firewall_name>default_target_path
Mine looks like:
default_target_path: /redirect