How to pass phpStan with a customLoginLinkNotification? - symfony

I'm using the login_link (docs) to authenticate an user who forgot his password.
Following the docs, I customized the email and I override the htmlTemplate but in real life phpStan stop me saying :
Call to an undefined method Symfony\Component\Mime\RawMessage::htmlTemplate().
I'm trying to fix it but I'm stuck :(
Any idea ?

The return type hint for EmailMessage::getMessage() is RawMessage, which in fact has no methode htmlTemplate().
However in this particular case, you are actually getting a NotificationMail (see the related code here) which eventually inherits from the RawMessage but implements the htmlTemplate() method (or, to be more precises, it inherits from the TemplatedEmail which implements said method).
There are a couple of ways to fix this issue with phpstan:
Check if $email is in fact an instance of NotificationMail
$email = $emailMessage->getMessage();
if ($email instanceof NotificationMail) {
$email->htmlTemplate(...);
}
Add an assert which will throw an exception during runtime if $email is not a NotificationMail
$email = $emailMessage->getMessage();
assert($email instanceof NotificationMail);
$email->htmlTemplate(...);
Add an inline #var. Note that this method is discouraged as per the phpstan documentation. (Thanks to #Ondřej Mirtes for pointing that out)
/** #var NotificationMail $email */
$email = $emailMessage->getMessage();

Related

Controller class not seeing method from repository class

I have spent the last few hours trying to find an answer however not gaining at all. I feel that it will end up being something simple that I did not have knowledge of. This section of code is of course incorrect but I wanted to show the idea of what I was trying to do here. I currently have a base repository class that has the findAllQueryBuilder method within it that will allow for reduced code. Right now I have about 10 repository classes that all need to use findAllQueryBuilder. This function is within the basecontroller I have built. The main issue is that this controllor does not see findAllQueryBuilder because as you can see this takes in the parameters to determine which data location is needed. I've done this once already in another location in my code with an interface however that was with a class I will put that example here also.
public function listAction(Request $request, RepositoryTypeInterface $repositoryName, $table, $sql, $route)
{
$filter = $request->query->get('filter');
$repository = "Bundle:$repositoryName";
$qb = $this->getDoctrine()
->getRepository($repository, 'tenant')
->findAllQueryBuilder($filter, $table, $sql);
As you can see in the following I set the type as the interface to make sure it knew that it was going to be that then.
public function newAction(Request $request, EntityTypeInterface $controllerType, formType, $repository, $routeName)
{
And then instantiated it within the childclass controllor
public function newAction(Request $request, EntityTypeInterface $controllerType = null, $formType = ContactType::class, $repository = 'Contact', $routeName = 'api_contacts_show')
{
$controllerType = new Contact();
return parent::newAction($request, $controllerType, $formType, $repository, $routeName);
}
So The first example above has me attempting the same thing however I am not sure how to apply this to this situation. as "Bundle:$repository" is a string and the parameter used is a string and it's not an entity so creating an instance doesn't make sense when all I need is its functionality. I just need some way to have access to this functionality. any ideas would work but I'm feeling like I'm missing something simple.
Here is the exact error I get,
Method 'findAllQueryBuilder' not found in \Doctrine\Common\Persistence\ObjectRepository
Referenced method is not found in subject class.
I'm sure there is a way to apply this concept to fix the lack of 'seeing' the function however as of right now I'm not completely sure.
Thank you anyone in advance.
EDIT:
I'm using a basecontrollor that is setting this up for the other controllers ie:cantactscontrollor class which uses the ContactEntity class mapped to the contactrepository class which is a child of a baserepository class which is where findAllQueryBuilder is located, I'm not sure how to map that.
public function listAction(Request $request, $repositoryName, $table, $sql, $route)
{
$filter = $request->query->get('filter');
$repository = "TenantBundle:$repositoryName";
/** #var RepositoryTypeInterface $qb */
$qb = $this->getDoctrine()
->getRepository($repository, 'tenant');
$queryResults = $qb->findAllQueryBuilder($filter, $table, $sql);
Sorry for the late response on this but here is the answer I was able to find. As I thought it was a simple thing but essentially the Docblock labeled $qb as the said interface above making sure it knew that any class that implements it will be accepted. Now knowing what type it will be, the error message is no longer there. Notice also how I split it up.

How to use the User value resolver in Symfony 3.2?

I'm building a small website using symfony 3.2.
There is a page in which the user can change its profile data with a form. I used the structure seen in the official tutorial. Here is the declaration of my controller :
/** #Route("/profil", name="profil_show_user") */
public function userProfileAction(Request $request, UserInterface $user) {
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
//...
$user->getProfile();//and do stuff
//...
}
My problem is that if the user is disconnected for being inactive for too long, or if someone bookmark the page but is not connected, I have this ugly error coming :
Controller
"AppBundle\Controller\ProfilController::userProfileAction()" requires
that you provide a value for the "$user" argument. Either the argument
is nullable and no null value has been provided, no default value has
been provided or because there is a non optional argument after this
one. 500 Internal Server Error - RuntimeException
In the New in Symfony 3.2 changelog, there is something about the new User value resolver. I tried to change to UserInterface $user = null, and it make the page redirect to the path I set in failure_path of security.yml, which is the good behaviour.
But then if I'm connected and go to profil_show_user, I get that other error :
Error: Call to a member function getProfile() on null
I search thoroughly the symfony documentation but couldn't find anything.
Could someone explain to me what goes wrong, what I misunderstood and how can I make this work ?
EDIT :
I thought I might say that if I don't use te value resolver, everything works fine. This is an educationnal and curiosity question about a new feature which I don't manage to use. This code works :
/** #Route("/profil", name="profil_show_user") */
public function userProfileAction(Request $request) {
$user = $this->getUser();
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
//...
$user->getProfile();//and do stuff
//...
}

Symfony getUser type hinting

I find it somewhat annoying to have to constantly use #var on getUser. It seems sloppy.
So I was thinking about starting to use this instead
<?php
// in the controller
$user = Customer::isCustomer($this->getUser());
// in the entity
/**
* #param Customer $user
*
* #return Customer
*/
public static function isCustomer(Customer $user)
{
return $user;
}
Is this a good idea? Bad idea? Horrible idea?
A type hint is the better option in this case.
Why would you write more code by adding checks manually rather than adding a simple type hint to your param.
Your four lines of codes representing two conditions give exactly the same result as:
/**
* #param Customer|null $user
*
* #return Customer|null
*/
public static function isCustomer(Customer $user = null)
{
// If $user is null, it works
// If $user is a Customer instance, it works
// If it's other, an exception is thrown
return $user;
}
Type hinting optimises and give more readability to a code.
It's a convention in symfony2, php and more.
It's commonly used as a constraint (or contract) with you and your method.
Also, it's the only alternative for an interface or an abstract class to add requirement to a parameter, because they don't have a body, and so cannot write conditions.
Update
In SensioLabs Insight, Object type hinting represents a warning using the following message :
The parameter user, which is an object, should be typehinted.
Because the verb should is used, I consider it's not a mandatory requirement, just a very good practice in case of it doesn't cause any problem.
Also, you can use the example you given without making your code horrible.

CSRF Token from the controller

I have a controller getting a form posted.
public function myPostAction(Request $request)
{
$form = $this->createForm('my_form', $my_object);
$form->handleRequest($request);
#...
I can see my CSRF token posted as parameter
my_form[_token] => lH38HTm5P0Cv3TOc4-9xi2COx-cZ670mpJ_36gR8ccI
I simply need to read it
$form->get('_token')
This tells me
Child "_token" does not exist.
How can I get this token ?
Here is the workaround I'm going to use meanwhile:
$token = $request->get($form->getName())['_token'];
I also noticed by chance that the intention used to generate the token is the form name
$csrf = $this->get('form.csrf_provider');
$intention = $form->getName();
$token = $csrf->generateCsrfToken($intention);
Like #Pierre de LESPINAY said, it is possible to do it by retrieving Token Manager service.
This service can also be injected in your constructor like that :
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
...
public function __construct(CsrfTokenManagerInterface $tokenManager)
{
$this->tokenManager = $tokenManager;
}
And used later like previously demonstrated :
$token = $this->tokenManager->getToken('myformname')->getValue();
You can get it with:
$request->request->get('my_form[_token]');
If you didn't disable CSRF-protection it will be applied and validated automatically and you don't need to check it by self.

Symfony2: How to get user Object inside controller when using FOSUserBundle?

I'm using FOSUserBundle to authenticate my users.
I'm trying to get the user object inside the Controller to register a trip where I should add the user object to this Trip before save.
I did not found how to do that because next method where I found it in symfony doc:
$user = $this->container->get('security.context')->getToken()->getUser();
renders the username as string, but I need the whole object.
Currently, I use this method, but it's not working properly.
$username = $this->container->get('security.context')->getToken()->getUser();
$em = $this->container->get('doctrine')->getEntityManager();
$user = $em->getRepository('SiteUtilisateurBundle:Utilisateur')->find($username);
How can I correctly do this?
I think Ramon is right. You already have the user object.
Also in Symfony > 2.1.x you can use
$this->getUser();
inside the controller.
The documentation for the getUser method indicates:
either returns an object which implements __toString(), or a primitive string is returned.
And if we look in the FOS\UserBundle\Model\User class over here (the base user class used by the FOSUserBundle) we can see that it does indeed have a __toString method:
public function __toString()
{
return (string) $this->getUsername();
}
I think that you actually get the User object but because it implements a __toString method it can be rendered directly in templates.
In Twig you can use:
{{ dump(user) }}
To see what kind of object you have. But You are actually using an object, not a string.
Solution:
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->findUserByUsername($this->container->get('security.context')
->getToken()
->getUser())
In FOSUser 1.3 you can't call directly $this->getUser in SecurityController.
You have to call $this->container->get('security.context')->getToken()->getUser();
And this is enough to access the user object.
No need to call $user = $em->getRepository('SiteUtilisateurBundle:Utilisateur')->find($username);
Furthermore your find method automatically and implicitly cast your initial $username object to string because it doesn't wait an object as argument.
I had the same issue, to resolve it add the FOS classes in your use section i.e:
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Model\UserInterface;
In symfony >= 3.2, documentation states that:
An alternative way to get the current user in a controller is to
type-hint the controller argument with UserInterface (and default it
to null if being logged-in is optional):
use Symfony\Component\Security\Core\User\UserInterface\UserInterface;
public function indexAction(UserInterface $user = null)
{
// $user is null when not logged-in or anon.
}
This is only recommended for experienced developers who don't extend
from the Symfony base controller and don't use the ControllerTrait
either. Otherwise, it's recommended to keep using the getUser()
shortcut.
Here is blog post about it
For FOSUser ^1.3 you can get current user from inside a controller that extends BaseController like this :
$user = $this->container->get('security.token_storage')->getToken()->getUser();
public function indexAction()
{
/* #var $user \FOS\UserBundle\Model\UserInterface */
if ($user = $this->getUser())
{
echo '<pre>';
print_r($user);
print_r($user->getRoles()); // method usage example
exit;
return $this->redirectToRoute('dashboard');
}
return $this->redirectToRoute('login');
}

Resources