Could any of you symfony2 gurus enlighten me as to how I can write a flash message from a symfony2 service?
I thought I had what I needed when I injected the container as below, but apparently not, I get error
Fatal error: Call to undefined method appDevDebugProjectContainer::getRequest() in /var/www/cloudsign_beta/src/BizTV/CommonBundle/Helper/globalHelper.php on line 135
So apparently I can not access the request... If I have to pass that as well from the controller I will soon loose the point of a service, it being unable to do anything by itself =)
<?php
namespace BizTV\CommonBundle\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityManager as EntityManager;
use BizTV\CommonBundle\Entity\Log;
class globalHelper {
private $container;
private $em;
public function __construct(Container $container, EntityManager $em) {
$this->container = $container;
$this->em = $em;
}
public function log($type,$message) {
// currently $type can be 'success', 'fail' or 'error'.
$currentUser = $this->container->get('security.context')->getToken()->getUser();
$currentCompany = $this->container->get('security.context')->getToken()->getUser()->getCompany();
//if the $type is one that we want to write to the log then create log entity (we don't log failed attempts at operations, but we do log errors.
if ($type == 'success') {
$em = $this->em;
$now = new \DateTime("now");
$entity = new Log();
$entity->setCompany($currentCompany);
$entity->setExecutor($currentUser);
$entity->setTime($now);
$entity->setEventType($type);
$entity->setEventMessage($message);
$em->persist($entity);
$em->flush();
}
//flash out the $message message text
$container = $this->container;
$session = $container->getRequest()->getSession()->setFlash($type, $message);
}
}
$session = $container->get('request')->getSession()->setFlash($type, $message);
But be careful as you may not be aware of whether request exists or not. A proper way of managing this would be by restricting your service to the request scope.
Related
protected function execute(InputInterface $input, OutputInterface $output, UserPasswordHasherInterface $passwordHasher): int
{
$io = new SymfonyStyle($input, $output);
$arg_email = $input->getArgument('email');
$arg_password = $input->getArgument('password');
if ($arg_email || $arg_password) {
$io->note(sprintf('You used the email adress: %s', $arg_email));
$io->note(sprintf('You used the password: %s', $arg_password));
$user = New User();
dd($passwordHasher->hashPassword(
$user,
'test'
));
$user->setEmail($arg_email);
$user->setPassword($passwordHasher->hashPassword(
$user,
'test'
));
$user->setActive(1);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
$io->success('User has been created!');
}
return Command::SUCCESS;
}
So I'm trying to build a custom command that will get your arg. (email/pass) and hash the password and create a user. Simple enough I thought.. I'm not quite sure whats going wrong because I'm getting weird compilere errors, I've tried multiple solutions provided by the internet but it comes all back to some what the same error.
"type" => 64 "message" => "Declaration of
App\Command\CreateUserCommand::execute(Symfony\Component\Console\Input\InputInterface
$input, Symfony\Component\Console\Output\OutputInterface $output,
App\Command\UserPasswordHasherInterface $passwordHasher): int must be
compatible with
Symfony\Component\Console\Command\Command::execute(Symfony\Component\Console\Input\InputInterface
$input, Symfony\Component\Console\Output\OutputInterface $output)"
"file" =>
"E:\BackYardBBQ\project\src\Command\CreateUserCommand.php"
Some of the things i tried are:
putting the interface inside the __constructor for auto wiring = getting an error that you cant use auto wiring
building it inside an controller or the authenticator = not working for the same reason my other failed attempts where.
building it inside the constructor of the controller
The error if you try autowiring
Cannot autowire service "App\Command\CreateUserCommand": argument
"$entityManager" of method "__construct()" has type
"App\Command\EntityManagerInterface" but this class was not found.
Make sure you enable namespaces
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use App\Entity\User;
You also need to add EntityManagerInterface and UserPasswordHasherInterface to your team constructor
private EntityManagerInterface $entityManager;
private UserPasswordHasherInterface $passwordHasher;
public function __construct(EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasherInterface){
$this->entityManager = $em;
$this->passwordHasher = $passwordHasherInterface;
parent::__construct();
}
Now you will be able to use them in your command like this:
$user->setPassword($this->passwordHasher->hashPassword($user, 'test'));
$this->entityManager->persist($user);
$this->entityManager->flush();
I added a VichImageType field on edit profile twig template...so im trying to check image dimensions using vich_uploader.pre_upload as Event.
In my class i got the image properties and if their dimensions arent bigger enough i tried to stop propagation and flashed a message to the twig template but, i dont know why, the event keeps propagating and redirects to fos_user_profile_show showing the image setup. Also, i tried to redirect again to fos_user_profile_edit but i cant use $event because "Vich\UploaderBundle\Event\Event" doesnt implement setController(). How can achieve this?
This is the method of the Listener class:
namespace BackendBundle\EventListener;
use Vich\UploaderBundle\Event\Event;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
class ComprobarDimensionesDeImagen
{
private $requestStack;
private $router;
public function __construct(RequestStack $requestStack, RouterInterface $router)
{
$this->requestStack = $requestStack;
$this->router = $router;
}
public function onVichUploaderPreUpload(Event $event)
{
$object = $event->getObject();
$mapping = $event->getMapping();
$imagen = getimagesize($object->getImageFile());
if (250 > $imagen[0] || 250 > imagen[1]) {
$request = $this->requestStack->getCurrentRequest();
$session = $request->getSession();
$event->stopPropagation();
$session->getFlashBag()->add('error', "Minimum dimensions: 250x250 \n");
$url = $this->router->generate('fos_user_profile_edit');
/*
* Testing different methods of redirect
*
* $response = new RedirectResponse($url);
* $event->setResponse($response);
*/
$event->setController(function() use ($request) {
return new RedirectResponse($url);
});
}
}
}
When i edit the profile again, I can see the flash message and the image setup in VichImageType field (i didnt expect that stopping propagation). Any help will be very welcome.
SOLVED: Just using #Assert\Image in my Entity class did the validation. No service neither listener needed
The argument for ->setController must be a callable . In your case, the function you pass as an argument returns an object of type Response. In order to be callable, the method should have the suffix Action. See also this post.
I'm in a symfony 2.8 project. Another developer wrote a Voter to check if a user has the permissions or not for a specific task. I already use this function in a controller without problems but now I'm writing a service to get a dynamic menu and I don't know how to access to the method isGranted:
Error: Attempted to call an undefined method named "isGranted" of
class.
namespace NameSpaceQuestion\Question\Service;
use Doctrine\ORM\EntityManager;
class MenuBuilder
{
private $areasTools;
public $menuItems = array();
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function createMenuCourse($course,$mode,$user)
{
$repository = $this->em->getRepository('eBundle:AreasTools');
$areas = $repository->findAll();
//Retrieve every area from the db
$itemsMenu = array();
foreach ($areas as $area) {
//If the user has permissions the area is included in the menu and proceed to check the tools that belong to the current area
if($this->isGranted( $mode,$area,$user ) ){
$itemsMenu[$area->getName()] = array();
$toolsPerCourse = $this->em->getRepository('eBundle:CourseTool')->findByAreaToolAndCourse($area, $course);
foreach ($toolsPerCourse as $toolCourse) {
//If the user has permissions the tool is included in the menu under the respective Area
if( ($this->isGranted( $mode,$toolCourse,$user ) )){
array_push($itemsMenu[$area->getName()], $toolCourse->getName());
}
}
}
}
return $itemsMenu;
}
}
You'll have to use Dependency Injection to get the AuthorizationChecker in your MenuBuilder class. You can read about it here: http://symfony.com/doc/current/cookbook/security/securing_services.html
Because it looks like you're already injecting the EntityManager, just add the AuthorizationChecker to your code:
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class MenuBuilder
{
protected $authorizationChecker;
public function __construct(EntityManager $em, AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
$this->em = $em;
}
function createMenuCourse()
{
if ( $this->authorizationChecker->isGranted('EDIT',$user) ) {
//build menu
}
}
}
First thing to understand is that the Symfony base controller has a number of helper functions such as isGranted implemented. But of course, if you are not inside of a controller then you don't have access to them. On the other hand, it's instructive to look at the base class and copy out the needed functionality.
The isGranted functionality relies on a authorization checker service which you will need to inject into your menu builder resulting in something like:
class MenuBuilder
{
private $em;
private $authorizationChecker;
public function __construct(
EntityManager $em,
$authorizationChecker // security.authorization_checker
) {
$this->em = $em;
$this->authorizationChecker = $authorizationChecker;
}
protected function isGranted($attributes, $object = null)
{
return $this->authorizationChecker->isGranted($attributes, $object);
}
Darn it. Stephan beat me by one minute. Must learn to type faster.
I want to implement the security.token_storage as a service to get the user that is logged in; so when the user writes a post or a comment the field "Author" is automatically set.
I cannot get it working:
Attempted to call method "get" on class "Blog\BlogBundle\Services\PostManager".
How can I implement it as a service and use it?
The UserManager (as a service):
namespace Usuarios\UsersBundle\Services;
class UserManager
public function getloggedUser()
{
$loggedUser = $this->get('security.token_storage')->getToken()->getUser();
return $loggedUser;
}
The service.yml for the UserManager config:
services:
user_manager:
class: %user_manager.class%
arguments:
- #doctrine.orm.entity_manager
The PostManager, that uses the getloggedUser function:
namespace Blog\BlogBundle\Services;
class PostManager
private $em;
private $formFactory;
private $um;
/**
* #param EntityManager $em
* #param formFactoryInterface $formFactory
* #param UserManager $um
*/
public function __construct(EntityManager $em, FormFactoryInterface $formFactory, UserManager $um)
{
$this->em = $em;
$this->formFactory = $formFactory;
$this->um = $um;
public function createComment (Post $post, Request $request)
{
$comment = new Comment();
$comment->setPost($post);
//this is the line failing:
$comment->setAuthorName($this->um->getloggedUser());
$form = $this->formFactory->create(new CommentType(), $comment);
$form->handleRequest($request);
if ($form->isValid()) {
$this->em->persist($comment);
$this->em->flush();
return true;
}
return $form;
Note "user_manager" is defined as a service and is fully functional since other functions using it are working. Why cannot I call the UserManager service from the PostManager service? The error I get is:
Catchable Fatal Error: Argument 3 passed to Blog\BlogBundle\Services\PostManager::__construct() must be an instance of Usuarios\UsersBundle\Services\UserManager, none given, called in C:\xampp\htdocs\eScribely2\app\cache\dev\appDevDebugProjectContainer.php on line 2535 and defined
services do not have the get convenience method that controllers do. You need to pass the container in as an argument when you build the service and store it as a member variable and then call you service.
$this->container->get('user_manager');
Optionally you can just pass in your user manger and not have to use the container at all.
I'm running the equivalent of this code in lots and lots of controller actions, basically it grabs the user's username, and if that username is attached to a blog entity it will allow the user to see the blog entity(s):
$em = $this->getDoctrine()->getManager();
$user = $this->get('security.context')->getToken()->getUser();
$entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
return $this->render('MySiteBundle:Blog:index.html.twig', array(
'entities' => $entities,
I want to move it into a service so I can cut down on code repetition. I want to avoid doing as much logic in my controllers as possible.
That being said, I'm not sure how I can access the user session and doctrine in a service.
Here's my services.yml:
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
And here's how I was attempting to call it in the controller:
public function testAction() {
$response = $this->get('mysite.user.blog');
return new Response($response);
}
I did try using an event subscriber/listener tag, but that doesn't seem to accomplish the task I want.
Here is my completely horrible attempt at a service. I couldn't get any response from it without using a constructor.
namespace MySite\SiteBundle\Services;
use MySite\SiteBundle\Entity\Blog;
class BlogUser {
protected $entities;
public function __construct(){
$em = $this->getDoctrine()->getManager();
$user = $this->get('security.context')->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
}
Am I going about this the completely wrong way? Is there a better way that I'm missing?
EDIT/ANSWER:
modified my naming convention a little:
//services.yml
mysite.user.blog.entities:
class: Mysite\SiteBundle\Services\BlogUser
arguments: ["#doctrine.orm.entity_manager", "#security.context"]
In the controller action:
$userEntities = $this->get('mysite.user.blog.entities');
$entities = $userEntities->getEntities();
In the service itself:
class BlogUser {
protected $entities;
public function __construct($em, $securityContext){
$user = $securityContext->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
public function getEntities(){
return $this->entities;
}
}
Still needs two lines to get the $entities variable in the controller, but this is way better than defining the same thing over and over.
"Security.context" has been deprecated since Symfony 2.6
After some community discussions, it was decided that SecurityContext gives too many dependencies to retrieve a simple Token/User object. That's why, starting with Symfony 2.6, thesecurity.context service has been deprecated and split into two new services:security.authorization_checker and security.token_storage.
Source
Thus, the new way to do it would be, first configure your service as:
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
arguments: ["#doctrine.orm.entity_manager", "#security.token_storage"]
Then in the service class constructor:
class BlogUser
{
protected $user;
protected $entities;
public function __construct(EntityManager $em, TokenStorage $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
}
Yes, you are doing it in wrong way. Let's look at your code:
# call to undefined object method getDoctrine()
$em = $this->getDoctrine()->getManager();
# call to undefined object method get()
$user = $this->get('security.context')->getToken()->getUser();
You cannot call getting entitymanager and security.context in your service in the same way like in your controller. Instead, you have to inject entitymanager and security.context services. Example:
# services.yml
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
calls:
- [ setUserFromSecurityContext, [ #security.context ]]
- [ setEntityManager, [ #doctrine.orm.entity_manager ]]
And improved service:
namespace Catablog\SiteBundle\Services;
use MySite\SiteBundle\Entity\Blog;
class BlogUser {
private $entityManager;
private $user;
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function setUserFromSecurityContext(SecurityContext $securityContext)
{
# notice, there are a cases when `getToken()` returns null, so improve this
$this->user = $securityContext->getToken()->getUser();
}
public function getEntities(){
# your code here
}
}
More info about Dependency injection
You are looking on how to 'inject' other services into your custom service. Take a look at Service Container documentation.
In your case, you can inject doctrine.orm.entity_manager and security.context services into your BlogUser class via constructor injection. For example:
class BlogUser {
public function __construct($em, $securityContext) {
$user = $securityContext->getToken()->getUser();
$this->entities = $em->getRepository('MySiteBundle:Blog')->findBy(array('user' => $user));
}
}
And configure your service as the following:
mysite.user.blog:
class: MySite\SiteBundle\Services\BlogUser
arguments: ["#doctrine.orm.entity_manager", "#security.context"]