I tried to call a function in a controller using service:
#BookManager.php
<?php
namespace App\Service;
use App\Entity\BookContent;
use Doctrine\ORM\EntityManagerInterface;
class BookManager
{
protected $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function getBookTitle(string $page){
return $this->entityManager->getRepository(BookContent::class)
>findOneBy(["page"=>$page])->getTitle();
}
In service.yml
....
services:
book.manager:
class: App\Service\BookManager
arguments: ['#doctrine.orm.entity_manager']
public: true
Finally I call it in Controller ;
$pageName = $this->container->get('request_stack')->getMasterRequest()->get('_route');
$bookTitle = $this->container->get('book.manager')->getBookTitle($pageName);
But I get this error
Service "book.manager" not found: even though it exists in the app's container, the container inside "App\Controller\HomeController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "serializer", "session" and "twig" services. Try using dependency injection instead.
Any idea?
EDIT
it's work when I use dependency injection but only when I do query with $id
$this->entityManager->getRepository(BookContent::class)-
>findOneById(["id"=>$id])->getTitle();
when I do it with findOneBy(["page"=>$page]) I get this error:
Impossible to access an attribute ("title") on a null variable.
By default Symfony 5 will autowire/auto configure your services. You can remove the book.manager from your service.yaml.
You can then use dependency injection in your controllers to access your services, like this for example:
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Service\BookManager; //<-- Add use
class YourController extends AbstractController
{
/**
* #Route("/", name="home")
*/
public function index(Request $request, BookManager $bookManager): Response
{
$pageName = $request->attributes->get('_route');
$bookTitle = $bookManager->getBookTitle($pageName);
return ...
}
}
Related
How do I obtain the name of the current route in the controller in Symfony 5?
When I try this I get NULL:
$this->Request = Request::createFromGlobals();
$route = $this->Request->attributes->get('_route');
var_dump($route);
It's not recommended to create request inside your controller.
Preferred way of obtaining already created Request is DI and autowiring:
// src/Controller/BlogController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class BlogController extends AbstractController
{
/**
* #Route("/blog", name="blog_list")
*/
public function list(Request $request)
{
$routeName = $request->attributes->get('_route');
$routeParameters = $request->attributes->get('_route_params');
var_dump($routeName);
}
}
This request is processed by Symfony HttpKernel and filled with additional information.
More info: https://symfony.com/doc/current/routing.html#getting-the-route-name-and-parameters
How did it get there: https://github.com/symfony/symfony/blob/b2609c4bae69ca383b97cb520da2ed9be1c48449/src/Symfony/Component/Routing/Matcher/UrlMatcher.php#L217
Found an answer here:
Just add RequestStack as a function parameter and call RequestStack->getCurrentRequest()->get('_route');
use Symfony\Component\HttpFoundation\RequestStack;
public function yourCalledFunction(Utilities $u, RequestStack $requestStack) {
$route = $requestStack->getCurrentRequest()->get('_route');
}
While using Symfony 3.3, I am declaring a service like this:
class TheService implements ContainerAwareInterface
{
use ContainerAwareTrait;
...
}
Inside each action where I need the EntityManager, I get it from the container:
$em = $this->container->get('doctrine.orm.entity_manager');
This is a bit annoying, so I'm curious whether Symfony has something that acts like EntityManagerAwareInterface.
Traditionally, you would have created a new service definition in your services.yml file set the entity manager as argument to your constructor
app.the_service:
class: AppBundle\Services\TheService
arguments: ['#doctrine.orm.entity_manager']
More recently, with the release of Symfony 3.3, the default symfony-standard-edition changed their default services.yml file to default to using autowire and add all classes in the AppBundle to be services. This removes the need for adding the custom service and using a type hint in your constructor will automatically inject the right service.
Your service class would then look like the following:
use Doctrine\ORM\EntityManagerInterface;
class TheService
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
// ...
}
For more information about automatically defining service dependencies, see https://symfony.com/doc/current/service_container/autowiring.html
The new default services.yml configuration file is available here: https://github.com/symfony/symfony-standard/blob/3.3/app/config/services.yml
Sometimes I inject the EM into a service on the container like this in services.yml:
application.the.service:
class: path\to\te\Service
arguments:
entityManager: '#doctrine.orm.entity_manager'
And then on the service class get it on the __construct method.
Hope it helps.
I ran into the same issue and solved it by editing the migration code.
I replaced
$this->addSql('ALTER TABLE user ADD COLUMN name VARCHAR(255) NOT NULL');
by
$this->addSql('ALTER TABLE user ADD COLUMN name VARCHAR(255) NOT NULL DEFAULT "-"');
I don't know why bin/console make:entity doesn't prompt us to provide a default in those cases. Django does it and it works well.
So I wanted to answer your subquestion:
This is a bit annoying, so I'm curious whether Symfony has something
that acts like EntityManagerAwareInterface.
And I think there is a solution to do so (I use it myself).
The idea is that you slightly change your kernel so tha it checks for all services which implement the EntityManagerAwareInterface and injects it for them.
You can also add write an EntityManagerAwareTrait that implements the $entityManager property and the setEntityManager()setter. The only thing left after that is to implement/use the interface/trait couple the way you would do for the Logger for example.
(you could have done this through a compiler pass as well).
<?php
// src/Kernel.php
namespace App;
use App\Entity\EntityManagerAwareInterface;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use function array_key_exists;
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;
public function process(ContainerBuilder $container): void
{
$definitions = $container->getDefinitions();
foreach ($definitions as $definition) {
if (!$this->isAware($definition, EntityManagerAwareInterface::class)) {
continue;
}
$definition->addMethodCall('setEntityManager', [$container->getDefinition('doctrine.orm.default_entity_manager')]);
}
}
private function isAware(Definition $definition, string $awarenessClass): bool
{
$serviceClass = $definition->getClass();
if ($serviceClass === null) {
return false;
}
$implementedClasses = #class_implements($serviceClass, false);
if (empty($implementedClasses)) {
return false;
}
if (array_key_exists($awarenessClass, $implementedClasses)) {
return true;
}
return false;
}
}
The interface:
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\EntityManagerInterface;
interface EntityManagerAwareInterface
{
public function setEntityManager(EntityManagerInterface $entityManager): void;
}
The trait:
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\EntityManagerInterface;
trait EntityManagerAwareTrait
{
/** #var EntityManagerInterface */
protected $entityManager;
public function setEntityManager(EntityManagerInterface $entityManager): void
{
$this->entityManager = $entityManager;
}
}
And now you can use it:
<?php
// src/SomeService.php
declare(strict_types=1);
namespace App;
use Exception;
use App\Entity\EntityManagerAwareInterface;
use App\Entity\Entity\EntityManagerAwareTrait;
use App\Entity\Entity\User;
class SomeService implements EntityManagerAwareInterface
{
use EntityManagerAwareTrait;
public function someMethod()
{
$users = $this->entityManager->getRepository(User::Class)->findAll();
// ...
}
}
I´m using Symfony 2.1, I have this controller:
<?php
namespace app\UsuariosBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use app\UsuariosBundle\Entity\Usuarios;
use app\ClientesBundle\Entity\Clientes;
use app\UsuariosBundle\Util\Util;
class DefaultController extends Controller
{
//Some functions
public function conexionAction(Request $request){
$util=new Util();
$sesion=$util->compruebaSesion(1,1,1);
}
}
?>
And this is my helper class, his folder is in UsuariosBundle\Util\Util.php
<?php
namespace app\UsuariosBundle\Util;
class Util
{
public function compruebaSesion($tipo,$idUsuario,$login){
//Here I need the entity manager
}
}
?>
I need the entity manager to do some updates in the database with the util class from everywhere in the app, but I don´t know how to get the entity manager here,out of the controller.
|-| UPDATE |-|
I have updated my code with the instructions of Vincent T and I´m gettin this error:
"Argument 1 passed to app\UsuariosBundle\Service\Util::__construct() must be an instance of Doctrine\ORM\EntityManager, none given, "
app/config/services.yml
services:
util:
class: app\UsuariosBundle\Service\Util
arguments: ["#doctrine.orm.entity_manager"]
src/UsuariosBundle/Service/Util.php
<?php
namespace app\UsuariosBundle\Util;
use Doctrine\ORM\EntityManager;
class Util
{
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function compruebaSesion($tipo,$idUsuario,$login){
$query= $this->em->createQuery('some query');
}
}
?>
Create a service as described in the docs sf docs services
and then you can just either inject that as a base service or get it using $this->get('name_of_helper_service') in a controller.
# app/config/services.yml
services:
app.service.util:
class: app\UsuariosBundle\Service\UtilService
arguments: ["#doctrine.orm.entity_manager"]
helper class
<?php
namespace app\UsuariosBundle\Util;
use Doctrine\ORM\EntityManager;
class Util
{
/**
* #Var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function compruebaSesion($tipo,$idUsuario,$login){
$query= $this->em->createQuery('some query');
}
}
?>
Controller class
<?php
namespace app\UsuariosBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use app\UsuariosBundle\Entity\Usuarios;
use app\ClientesBundle\Entity\Clientes;
use app\UsuariosBundle\Util\Util;
class DefaultController extends Controller
{
//Some functions
public function conexionAction(Request $request){
$util= $this->get('app.service.util');
$sesion=$util->compruebaSesion(1,1,1);
}
}
?>
I'm trying to getDoctrine() outside of the controller.
I've created this service:
config/services.yml
services:
update_command:
class: project\projBundle\Command\Update
arguments: ['#doctrine.orm.entity_manager']
and in my app/config/config.yml
imports:
- { resource: "#projectprojBundle/Resources/config/services.yml" }
so and the class that I want to use:
namespace project\projBundle\Command;
use Doctrine\ORM\EntityManager;
class Update {
protected $em;
public function __construct(EntityManager $em) {
$this->em = $em;
}
but every time I want to do this: (I'm doing this right?)
$up = new Update();
i got this error:
Catchable Fatal Error: Argument 1 passed to ...\Update::__construct() must be an instance of Doctrine\ORM\EntityManager, none given, called in .../Update.php line 7
Simple solution
If you're implementing a Symfony command (that can be executed in a cron tab), you can access the service container from the command.
<?php
namespace MyProject\MyBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class UpdateCommand extends ContainerAwareCommand
{
protected $em;
protected function configure()
{
$this->setName('myproject:mybundle:update') ;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->em = $this->getContainer()->get('doctrine.orm.entity_manager');
}
}
That way, you get the entity manager from a command and don't need to declare this class as a service. You can therefore remove the configuration you added in the services.yml file.
An other solution (cleaner)
This solution allows better separation of concerns and can therefore be easily unit tested and reused in other parts of your Symfony application (not only as a command).
Move all the logic part of your "update" command to a dedicated class that you will declare as a service:
<?php
namespace MyProject\MyBundle\Service;
use Doctrine\ORM\EntityManager;
class MyUpdater
{
protected $em;
public function __construct($em)
{
$this->em = $em;
}
public function runUpdate()
{
// All your logic code here
}
}
Declare it as a service in your services.yml file:
services:
myproject.mybundle.myupdater:
class: MyProject\MyBundle\Service\MyUpdater
arguments: ['#doctrine.orm.entity_manager']
Simply call your service from your command :
<?php
namespace MyProject\MyBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class UpdateCommand extends ContainerAwareCommand
{
protected function configure()
{
$this->setName('myproject:mybundle:update') ;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$myUpdater = $this->getContainer()->get('myproject.mybundle.myupdater');
$myUpdater->runUpdate();
}
}
You have to either inject your newly created #update_command service or get it from the container in order to have the #doctrine.orm.entity_manager service injected automatically.
You're just creating the object with no argument, not a service. Update expects to retrieve an entity manager instance but you don't provide it.
$up = new Update();
In a ContainerAware class like a controller get your service like this:
$up = $this->container->get('update_command');
Otherwise turn the class where you want to use the update_command into a service aswell and inject #update_command as you did with the entity manager in the service itself.
remove below codes in app/config/config.yml, your services.yml will be autoload...
imports:
- { resource: "#projectprojBundle/Resources/config/services.yml" }
in a Action new a instance you can do:
$up = $this->get('update_command');
I am struggling with the combination of a Controller, an EntityRepository, and my Doctrine Fixtures in Symfony2.
My ImageParts Entity are pieces of images. I want to randomly generate images, using random pieces.
So, I have an entity called ImagePart, and an EntityRepository with the name 'ImagePartRepository'.
Within that EntityRepository, I created a function called 'getRandomImagePart()', which is working fine when testing it using a route.
But I cannot figure out how I can use this function within my fixtures. I THINK I have to declare a service, but even then I cannot get it to work. The type of error messages I get tells me that I'm clearly doing something structurally wrong.
Furthermore, I am wondering if I use the Symfony2 framework the correct way, functional. Eg: Should I be able to use an EntityRepository within my fixtures.
Controller:
<?php
namespace AMM\AMMBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\Finder\Finder;
use AMM\AMMBundle\Entity\ImagePart;
use Doctrine\ORM\Mapping as ORM;
class ImagePartController extends Controller implements ContainerAwareInterface
{
/**
* #var ContainerInterface
*/
protected $container;
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function getRandomImagePartAction($type)
{
/*
$sql = "
SELECT a.imagePartBase64 FROM imageparts a
WHERE a.imagePartCategory = '".$type."'
ORDER BY RAND()
LIMIT 1
";
*/
$em = $this->getDoctrine()
->getManager();
$imagePart = $em->createQueryBuilder()
->select('g')
->from('AMMBundle:ImagePart', 'i')
->addOrderBy('i.id', 'ASC')
->getQuery()
->getSingleResult();
// $conn = $this->get('database_connection');
//$randomimagepart = $conn->fetchAll($sql);
// return $randomimagepart[0]['imagePartBase64'];
return $imagePart;
}
// ..
public function generateRandomImage()
{
$object->getRandomImagePartAction('BACKGROUND');
}
Fixture file ImageFixtures.php
<?php
namespace AMM\AMMBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use AMM\AMMBundle\Entity\ImagePart;
class GenerateImages implements FixtureInterface, ContainerAwareInterface, OrderedFixtureInterface
{
private $container;
public function load(Objectmanager $manager)
{
$image= new Image();
$imageGenerator = $this->container
->get('imageGenerator')
->GenerateRandomImage();
$manager->persist($image);
$manager->flush();
}
My services.yml:
services:
imageGenerator:
class: AMM\AMMBundle\Controller\ImagePartController
So, basically what I do is call a function from a function in the same controller. This works fine when testing it, for example, in a view.
But when trying to load doctrine fixtures, the following error occurs:
Fatal error: Call to a member function has() on a non-object in C:\wamp\www\amm\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Controller\Controller.php on line 198
The end of the stack trace:
5.7077 41983128 15. AMM\AMMBundle\Controller\ImagePartController->getRandomImagePartAction() C:\wamp\www\amm\src\AMM\AMMBundle\Controller\ImagePartController.php:119
5.7077 41983128 16. Symfony\Bundle\FrameworkBundle\Controller\Controller->getDoctrine() C:\wamp\www\amm\src\AMM\AMMBundle\Controller\ImagePartController.php:44
Ok, I found the problem.
I had to add this to my services.yml:
calls: - [setContainer, [#service_container]] –