Error: Call to a member function get() on a non-object - php

I am trying to send mail with Swift_Message however when I go to send the data it will not send and I get an error of
FatalErrorException: Error: Call to a member function get() on a
non-object in
/vagrant/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
line 252
Here is the Email Controller that I am using.
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
class EmailController extends Controller{
public function createMessage($subject, $from, $from_name, $to, $to_name, $body){
// Create the message
$message = \Swift_Message::newInstance()
// Give the message a subject
->setSubject($subject)
// Set the From address with an associative array
->setFrom(array($from => $from_name))
// Set the To addresses with an associative array
->setTo(array($to => $to_name))
// Give it a body
->setBody($body, 'text/html');
return $message;
}
public function sendEmail($message, $urlAlias){
$this->get('mailer')->send($message);
return $this->redirect($this->generateUrl($urlAlias));
}
}
I understand that its unable to access the object which I think is part of the container class but I can seem to get it to pull up. I have tried using $this->container->get(...
but that also does not work. What am I missing. This seems like it should be really straight forward.
I am calling this function from a different bundle using an action to call the current controller. I don't know if that makes a difference.
Ok so when looking in /vagrant/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
The line it errors on is
/**
* Gets a service by id.
*
* #param string $id The service id
*
* #return object The service
*/
public function get($id)
{
return $this->container->get($id);
}
}
Which makes me feel like 'mailer; is not a good $id but it is used in Symfony's examples and in a lot of other private examples.
Don't know if this helps or not but figured it was worth mentioning.
Could this be because of the swiftmailer: setting inside of my config.yml file?
routing.yml file
fuel_form_homepage:
pattern: /hello/{name}
defaults: { _controller: FuelFormBundle:Default:index }
referral_form:
pattern: /form/referral/{hash}
defaults: { _controller: FuelFormBundle:Form:referralForm }
referral_result:
pattern: /form/referral/result
defaults: { _controller: FuelFormBundle:Form:referralResult }
user_form:
pattern: /form/user
defaults: { _controller: FuelFormBundle:Form:userForm }
home:
pattern: /
defaults: { _controller: FuelFormBundle:Default:home}
This is the function that calls
public function userFormAction(request $request){
$user = new User();
$form = $this->createForm('user', $user);
$form->handleRequest($request);
if($form->isValid()){
$user->setTimeCreated();
$user->setTimeUpdated();
$date = $user->getTimeCreated();
$timestamp = $date->format("U");
$hash = $user->getFirstName() . $user->getLastName() . $timestamp ;
$user->setUserHash(md5($hash));
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
print_r($user);
//TODO: #Email: #Body: make sure to replace with correct information.
//Calls a service named email_bundle_controller
$emailController = $this->get('email_bundle_controller');
$fullName = $user->getFirstName() . $user->getLastName();
$body = "please visit the following url to start referring! <a href='http://localhost:8080/app_dev.php/form/referral/" . $user->getUserHash() . "'>Your URL</a>";
$message = $emailController->createMessage('Welcome to Fuel PRM References', 'bsaverino#gmail.com', 'Brad Saverino', $user->getEmail(), $fullName, $body);
$emailController->sendEmail($message, 'user_form');
}
return $this->render('FuelFormBundle:Default:mainForm.html.twig', array('form' => $form->createView(),));
}
This is the service that allows me to call on the other bundle.
services:
fuel_form.form.type.referral:
class: Fuel\FormBundle\Form\Type\ReferralType
tags:
- { name: form.type, alias: referral}
fuel_form.form.type.user:
class: Fuel\FormBundle\Form\Type\UserType
tags:
- { name: form.type, alias: user}
email_bundle_controller:
class: Fuel\EmailBundle\Controller\EmailController
This is the FuelEmailBundle.php
namespace Fuel\EmailBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use \Symfony\Component\DependencyInjection\ContainerInterface;
class FuelEmailBundle extends Bundle
{
private static $containerInstance = null;
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
self::$containerInstance = $container;
}
public static function getContainer()
{
return self::$containerInstance;
}
}
These are the changes that were made to the sendEmail function
public function sendEmail($message, $urlAlias){
$container = FuelEmailBundle::getContainer();
$mailer = $container->get('mailer');
$mailer->send($message);
return $this->redirect($this->generateUrl($urlAlias));
}

As Cerad had mentioned above, you are getting the error as container is not set. One way of fixing this issue would be to pass a container instance to your bundle so that you can call the container from anywhere in your project.
Edit the class corresponding to your bundle(BundleName.php) to include two methods setContainer and getContainer. See the example below.
namespace Venom\CoreBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use \Symfony\Component\DependencyInjection\ContainerInterface;
class VenomCoreBundle extends Bundle
{
private static $containerInstance = null;
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
self::$containerInstance = $container;
}
public static function getContainer()
{
return self::$containerInstance;
}
}
Use the appropriate namespaces.
Then, use the namespace for the bundle in classes where you need the container.
You may call the container by
$container = VenomCoreBundle::getContainer();
Then, call the mailer
$mailer = $container->get('mailer');

Related

Symfony Twig Extension breaks other service - Is templating done before security?

I am working on a Symfony 2.7 WebApp. One of the bundles I created includes a service that offer some user related stuff, e.g. userHasPurchases().
Problem is, that including a Twig Extesion breaks another service:
AppShopService
namespace AppShopBundle\Service;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...
class AppShopService {
protected $user;
public function __construct(TokenStorageInterface $tokenStorage, ...) {
$this->user = $tokenStorage->getToken() ? $tokenStorage->getToken()->getUser() : null;
...
}
public function userHasPurchases(User $user) {
$user = $user ? $user : $this->user;
$result = $user...
return result;
}
}
AppShopBundle\Resources\config\services.yml
services:
app_shop.service:
class: AppShopBundle\Service\AppShopService
arguments:
- "#security.token_storage"
- ...
So far everything works fine: The AppShopServices is created with the current user and userHasPurchases() work as expected.
Now I have add a Twig Extension to be able to use userHasPurchases() within my templates:
Twig Extension
namespace AppShopBundle\Twig;
use AppShopBundle\Service\AppShopService;
class AppShopExtension extends \Twig_Extension {
private $shopService;
public function __construct(AppShopService $shopService) {
$this->shopService = $shopService;
}
public function getName() {
return 'app_shop_bundle_extension';
}
public function getFunctions() {
$functions = array();
$functions[] = new \Twig_SimpleFunction('userHasPurchases', array(
$this,
'userHasPurchases'
));
return $functions;
}
public function userHasPurchases($user) {
return $this->shopService->userHasPurchases($user);
}
}
Including Extension in AppShopBundle\Resources\config\services.yml
services:
app_shop.service:
class: AppShopBundle\Service\AppShopService
arguments:
- "#security.token_storage"
- ...
app_shop.twig_extension:
class: AppShopBundle\Twig\AppShopExtension
arguments:
- "#app_shop.service"
tags:
- { name: twig.extension }
After icluding the Twig Extension, AppShopService and its method userHasPurchases does not work any more. Problem is, that the constructor of AppShopService does not set user anymore since $tokenStorage->getToken() now returns null.
How is this possible? I have changed nothing except including the Twig Extension. As soon as I remove the Twig Extension from services.yml everything works correctly again.
My only guess is, that the creation fo the Twig Extension is done before any security. But why?
Any idea what might be wrong here?
don't interact with the tokenStorage in the constructor but only in the userHasPurchases method.
namespace AppShopBundle\Service;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
...
class AppShopService {
protected $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage, ...) {
$this->tokenStorage = $tokenStorage;
}
public function userHasPurchases(User $user) {
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
$result = $user...
return result;
}
}
Hope this help

Use custom Service inside Custom Event

I'm learning Symfony and I'm trying to create: a new service and Event
My service send emails
config.yml
parameters:
MyService.class: Acme\UserBundle\Services\sendEmail
MyService.transport: sendmail
service.yml
services:
MyService:
class: %MyService.class%
arguments: [#mailer]
sendEmail.php
class sendEmail {
private $mail;
public function __construct ($mail) {
$this->mail = $mail;
}
public function sendMail () {
$msg = \Swift_Message::newInstance()
->setSubject('Hi')
->setFrom('xxx#example.com')
->setTo('cc#gmail.com')
->setBody('ok');
$this->mail->send($msg);
}
}
My Event
I have created these class
UserEvent.php
<?php
namespace Acme\UserBundle\Event;
final class UserEvents {
const NEW_USER='new.user';
}
newUserEvent.php
<?php
/**
* EVENT DISPATCHER
*/
namespace Acme\UserBundle\Event;
use Acme\UserBundle\Entity\User;
use Symfony\Component\EventDispatcher\Event;
class NewUserEvent extends Event {
protected $user;
public function __construct (User $user) {
$this->user = $user;
}
public function getUser () {
return $this->user;
}
}
newUserListener.php
<?php
namespace Acme\UserBundle\Event;
use Acme\UserBundle\Services\sendEmail;
class NewUserListener {
public function sendEmailToUsers(NewUserEvent $event,sendEmail $service)
{
// ... send email to users
}
}
in my controller
$em = $this->getEm();
$dispatcher = new EventDispatcher();
// attach listener
$listner = new NewUserListener();
$dispatcher->addListener(UserEvents::NEW_USER,array($listner,'sendEmailToUsers'));
$user = $em->getRepository('AcmeUserBundle:User')->findOneBy(array('username' => 'alex')); //mock
$event = new NewUserEvent($user,$this->get('MyService'));
$dispatcher->dispatch(UserEvents::NEW_USER,$event);
return new Response('hi');
I'd like to use my service inside my event by I have this error
ContextErrorException: Catchable Fatal Error: Argument 2 passed to
Acme\UserBundle\Event\NewUserListener::sendEmailToUsers() must be an
instance of Acme\UserBundle\Services\sendEmail, none given in
Acme/UserBundle/Event/NewUserListener.php
There are many points here :
You create a new event dispatcher in your controller, but the event dispatcher service is already available in a standard Symfony app, you should use it and register your listener with the kernel.event_listener tag for your service :
http://symfony.com/doc/current/cookbook/service_container/event_listener.html
$disptacher = $this->container->get('event_dispatcher');
Secondly, nowhere you inject your service in your listener ?

How to send var to view from event listener in symfony2?

I am trying to send var to view from event listener in symfony2 but I am stacked.
1) Is this possible?
2) What kernel event to use (kernel.view or kernel.request)?
3) Finally how to send some var to view?
My best guess is that I have to extend return from controller and then to let controller do his job.
I need to send some array of objects (entities).
I see several ways to handle this.
Adding a global variable from a kernel.request listener
The idea is to add a global variable straight after the kernel.request event.
services.yml
services:
class: Acme\FooBundle\Listener\MyListener
arguments:
- #twig
tags:
-
name: kernel.event_listener
event: kernel.request
method: onKernelRequest
MyListener
class MyListener
{
protected $twig;
public function __construct(\Twig_Environment $twig)
{
$this->twig = $twig;
}
public function onKernelRequest(GetResponseEvent $event)
{
$myVar = 'foo'; // Process data
$this->twig->addGlobal('myvar', $myVar);
}
}
You can now use it at any time by doing
{{ myvar }}
From a kernel.view listener
First, you need to understand when kernel.view is called. It's only called when the return of the controller is not an instance of Response object.
That said, doing
// Acme/FooBundle/FooController#fooAction
return $this->render();
returns a Response object, so kernel.view is not called.
Defining controllers
The idea is to make all controller returns an array of data, just like #Template requirements.
// Acme/FooBundle/FooController#fooAction
return array(
'template' => 'AcmeFooBundle:Foo:foo.html.twig',
'data' => array(
'entity' => $entity
)
);
Defining the service
Since you already have your service definition, you just need to add some requirements in your service declaration.
You need the #templating service to render the data.
You need to set itself as a kernel.view listener
// Acme/FooBundle/Resources/config/services.yml
services:
acme_foo.my_listener:
class: Acme\FooBundle\Listener\MyListener
arguments:
- #templating
tags:
-
name: kernel.event_listener
event: kernel.request
method: onKernelRequest
-
name: kernel.event_listener
event: kernel.view
method: onKernelView
Creating the service
// Acme/FooBundle/Listener/MyListener.php
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
class MyListener
{
protected $templating;
protected $myVar;
public function __construct(EngineInterface $templating)
{
$this->templating = $templating;
}
public function getMyVar()
{
return $this->myVar;
}
public function onKernelRequest(GetResponseEvent $event)
{
$this->myVar = ""; // Process MyVar data
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$result = $event->getControllerResult();
if (null === $this->myVar || !isset($result['template']) || !isset($result['data'])) {
return;
}
$data = array_merge($result['data'], array('myvar' => $this->myVar));
$rendered = $this->templating->render($result['template'], $data);
$event->setResponse(new Response($rendered));
}
}
And there you are. The listener is creating a new response, adding your custom definition of myvar to any template rendered by him.
From a TWIG extension
An alternative is to create a TWIG extension. In the following example, I'm assuming the MyListener definition is the same as above.
Defining services
As per the documentation given above, you just have to create a simple extension class.
// Acme/FooBundle/Resources/config/services.yml
services:
acme_foo.my_listener:
class: Acme\FooBundle\Listener\MyListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
acme_foo.my_extension:
class: Acme\FooBundle\Extension\MyExtension
arguments:
- #acme_foo.my_listener
tags:
- { name: twig.extension }
Defining the service
Just like in documentation, we'll create a simple function.
// Acme/FooBundle/Extension/MyExtension.php
use Acme\FooBundle\Listener\MyListener;
class MyExtension extends \Twig_Extension
{
protected $listener;
public function __construct(MyListener $listener)
{
$this->listener = $listener;
}
public function getName()
{
return 'my_extension';
}
public function getFunctions()
{
return array(
'myvar' => new \Twig_Function_Method($this, 'getMyVar')
);
}
public function getMyVar()
{
return $this->listener->getMyVar();
}
}
Usage
Then you can use it in any view by doing
{{ myvar() }}
From a common controller
I don't like this idea, but this is an alternative. You just have to create a BaseController which will override the default render method.
// Acme/FooBundle/Controller/BaseController.php
abstract class BaseController extends Controller
{
public function render($view, array $parameters = array(), Response $response = null)
{
$parameters = array_merge(
$parameters,
array(
'myvar' => $this->get('my_listener')->getMyVar()
)
);
return parent::render($view, $parameters, $response);
}
}
There's an alternative method here that I've had to do. I wanted to get some data, run it through json_encode(), then add that as a JavaScript variable to the response. Here's what I ended up doing.
I'm subscribing to kernel.response:
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => 'onKernelResponse'
];
}
public function onKernelResponse(FilterResponseEvent $event)
{
/** -- SNIP -- Cutting out how I get my serialised data **/
$serialized = json_encode($data);
/** Shove it into the response as some JS at the bottom **/
$dom = new \DOMDocument;
libxml_use_internal_errors(true);
$dom->loadHTML($event->getResponse()->getContent());
libxml_use_internal_errors(false);
$node = $dom->createElement('script', "var data = $serialized;");
$dom->getElementsByTagName('body')->item(0)->appendChild($node);
$event->getResponse()->setContent($dom->saveHTML());
}
This is one way of doing it. Honestly, I don't like any of the methods described on this page. There should be a better way, but there isn't. This is what I'm using, though, and it works well. Just make sure you don't call your variable "data"; use something that won't be taken up elsewhere and preferably shove it in it's own (function() { } JS namespace.
I don't know how to pass variables directly to view, but you can change response object, with kernel.response event listener. Take a look how symfony debug toolbar injects bottom bar, you could use similar technique.
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php#L106

FatalErrorException: Error: Call to a member function has() on a non-object

I have a read a lot of topics on this and I can't seem to find a solution to my problem.
I feel like the problem is obvious and maybe I have just been staring at it too long.
The error is FatalErrorException: Error: Call to a member function has() on a non-object in /vagrant/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php line 198
Looking at the error line, it says.
public function getDoctrine()
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application.');
}
return $this->container->get('doctrine');
}
Here is my code...
This is the main controller that is calling the DAO Controller
public function clickThroughAction(request $request, $hash)
{
if (isset($hash)) {
$dbController = $this->get('database_controller');
print_r($dbController->getReferralIdByHash($hash));
return $this->redirect($this->generateUrl('home'));
} else {
return 0;
}
}
This is the service that is being used.
services:
database_controller:
class: Fuel\FormBundle\Controller\DatabaseController
This is the dao controller that is calling the database.
public function getReferralIdByHash($hash)
{
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'Select u From FuelFormBundle:UserReferrals u WHERE u.user_hash = :hash'
)->setParameter('hash', $hash);
$referral = $query->getResult();
if (!$referral) {
throw $this->createNotFoundException(
'No product referral found'
);
$logger = $this->get('logger');
$logger->info('I just got the logger');
$logger->crit('An error occurred, hash for referrals is not recognized. current hash is: ' . $hash . " .");
return $this->redirect($this->generateUrl('home'));
}
$clickThroughCount = $referral[0]->getClickThrough();
$referral[0]->setClickThrough($clickThroughCount + 1);
$em->flush();
return $referral;
}
I think the problem is that the doctrine container is not present which is why I am having issues. I am not sure how to solve this.
Any help is appreciated. Thanks!
Edit
Ok so here is what I changed.
Main Controller stayed the same.
DAO Controller a couple of things were added.
class DatabaseController extends Controller{
protected $entityManager;
public function __construct($entityManager) {
$this->entityManager = $entityManager;
}
public function getReferralIdByHash($hash)
{
$em = $this->entityManager;
$query = $em->createQuery(
'Select u From FuelFormBundle:UserReferrals u WHERE u.user_hash = :hash'
)->setParameter('hash', $hash);
$referral = $query->getResult();
if (!$referral) {
throw $this->createNotFoundException(
'No product referral found'
);
$logger = $this->get('logger');
$logger->info('I just got the logger');
$logger->crit('An error occurred, hash for referrals is not recognized. current hash is: ' . $hash . " .");
return $this->redirect($this->generateUrl('home'));
}
$clickThroughCount = $referral[0]->getClickThrough();
$referral[0]->setClickThrough($clickThroughCount + 1);
$em->flush();
return $referral;
}
}
Service ended up looking like this
services:
database_controller:
class: Fuel\FormBundle\Controller\DatabaseController
arguments: ["#doctrine.orm.entity_manager"]
The problem is the container not being injected into the controller here.
Normally, Symfony does this automatically if you're extending Symfony\Bundle\FrameworkBundle\Controller\Controller, which itself extends Symfony\Component\DependencyInjection\ContainerAware:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class YourController extends Controller
The container is injected into the controller (if not explicitly defined as a service) using setter injection calling the method setContainer() with the container as an argument.
Now, as you configured your controller as a service you need to add the setContainer call to your service configuration:
services:
database_controller:
class: Fuel\FormBundle\Controller\DatabaseController
calls:
- [setContainer, ["#service_container"]]
Clear your cache afterwards.
Not sure why you would make a controller a service. For what use case? Normally a service is a Plain Old PHP Object.
About your problem .. since you are using the controller as a service it does not get the container automatically. So you have to inject the entire container, which is kind of heavy if you just need doctrine.
So it's better just to inject the things you really need. To inject doctrine, in your yml below class:
arguments: ["#doctrine.orm.entity_manager"]
Then in your controller constructor:
public function __construct($entityManager) {
$this->entityManager = $entityManager;
}
Possible you will need to call the parent constructor (be aware of that).
If you want do inject the complete service container anyway, here is the right section in the manual how you can do that: http://symfony.com/doc/current/cookbook/service_container/scopes.html#passing-the-container-as-a-dependency-of-your-service
One more suggestion: when you are moving database connection out of main controller you need to construct new instance of Entity Manager for instance:
class StoreController extends Controller{
public function addstoreAction(Request $request){
$checkStore = new StoreExistsCheck($this ->getDoctrine()->getManager());
//your code here
}
using /myBundle/Model folder
namespace myBundle\Model;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use myBundle\Entity\Store;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Query;
class StoreExistsCheck extends Controller{
protected $em = null;
protected $kernel = null;
public function __construct(EntityManager $em) {
$this->em = $em;
}
public function storeExists($argosStore){
$storeExists = $this->em ->getRepository('myBundle:Store')
->findOneBystoreNumber($argosStore->getStoreNumber());
return $storeExists;
}

FOSUserBundle : get repository from inside FormHandler

I need to set a default value to a new user before saving it.
The problem is that I can't find a way to get an object through its repository from inside the FormHandler.
<?php
namespace Acme\UserBundle\Form\Handler;
use FOS\UserBundle\Form\Handler\RegistrationFormHandler as BaseHandler;
use FOS\UserBundle\Model\UserInterface;
class RegistrationFormHandler extends BaseHandler
{
protected function onSuccess(UserInterface $user, $confirmation)
{
$repository = $this->container->get('doctrine')->getEntityManager()->getRepository('AcmeUserBundle:Photo');
if($user->isMale()){
$photo = $repository->getDefaultForMale();
$user->setPhoto($photo);
}
else {
$photo = $repository->getDefaultForFemale();
$user->setPhoto($photo);
}
parent::onSuccess($user, $confirmation);
}
}
The problem comes from the following line :
$repository = $this->container->get('doctrine')->getEntityManager()->getRepository('AcmeUserBundle:Photo');
... and I can't find a way to get this repository, or the entity manager from this FormHandler.
Many thanks for your help !
A
You have to define a service that reference your extended handler class and point it in app/config.yml. e.g
The class,
//namespace definitions
class MyHandler extends RegistrationFormHandler{
private $container;
public function __construct(Form $form, Request $request, UserManagerInterface $userManager, MailerInterface $mailer, ContainerInterface $container)
{
parent::__construct($form, $request, $userManager, $mailer);
$this->container = $container;
}
protected function onSuccess(UserInterface $user, $confirmation)
{
$repository = $this->container->get('doctrine')->getEntityManager()->getRepository('AcmeUserBundle:Photo');
// your code
}
The service,
my.registration.form.handler:
scope: request
class: FQCN\Of\MyHandler
arguments: [#fos_user.registration.form, #request, #fos_user.user_manager, #fos_user.mailer, #service_container]
Lastly in app/config.yml,
fos_user:
#....
registration:
#...
form:
handler: my.registration.form.handler
FOS got his own UserManager. Try to use this.

Categories