symfony php- where is controller - model reference is defined - symfony

in what file, or where ,is the mapping of controller to model occur in
symfony 1.4
symfony 2
are there any yml/config files that tell the system to recognize the appropriate model?
what I mean is:
let's say a controller looks like this:
class jobActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$user=new PcUser();
$user->username=$request->GetParameters(...);
}
}
and PcUser.php is an entity file that has PcUser class inside.
where is the mapping done? how does the controller jobActions know PcUser?

It's done by symfony's autoloader. If the script doesn't know about class you are trying to use, then autoloader try include appropriate class based on namespace.
So, for your example:
class SomeController
{
public function executeIndex(sfWebRequest $request)
{
$user=new \NamespaceForBundle\Entity\PcUser();
$user->username=$request->GetParameters(...);
}
}
And if you ask about ORM mapping - you can do it in several ways like appropriate yaml configuration file, or annotations inside entities.

Related

How to extend FOSRestBundle RequestBodyParamConverter?

I am new to Symfony (5.3) and would like to extend the RequestBodyParamConverter (FOSRestBundle 3.0.5) to create a REST api. Using #ParamConverter annotation with the RequestBodyParamConverter works fine. However, I would like to create a custom converter, which does the exact same job as RequestBodyParamConverter plus a little extra work.
My first guess was to simply extend RequestBodyParamConverter and provide my custom subclass in the #ParamConverter annotation. However, RequestBodyParamConverter is defined as final and thus cannot be extended...
Injecting RequestBodyParamConverter / fos_rest.request_body_converter into a custom converter class (see example below) also fails because the service cannot be found. I assume this is because it is defined a private?
So, my last idea was to create a RequestBodyParamConverter inside my custom converter class. While this works, I am not sure if this is the right way to solve this problem. This way RequestBodyParamConverter is created twice. This is nothing special of course, but is this the Symfony way to solve this or are there other solutions?
Example:
Inject RequestBodyParamConverter in custom converter class
class MyParamConverter implements ParamConverterInterface {
protected $parentConverter;
public function __construct(ParamConverterInterface $parentConverter) {
$this->parentConverter = $parentConverter;
}
public function apply(Request $request, ParamConverter $configuration): bool {
doExtraWork();
return $this->parentConverter->apply(...);
}
}
// config/services.yaml
My\Project\MyParamConverter:
tags:
- { name: request.param_converter, converter: my_converter.request_body }
arguments:
# both fails since service is not found
$parentConverter: '#FOS\RestBundle\Request\RequestBodyParamConverter'
# OR
$parentConverter: '#fos_rest.request_body_converter'
Create RequestBodyParamConverter in custom converter class
class MyParamConverter implements ParamConverterInterface {
protected $parentConverter;
public function __construct(...parameters necessary to create converter...) {
$this->parentConverter = new RequestBodyParamConverter(...);
}
...
}
Symfony provide a way to decorate a registered service
To use it you need the FOS service id registered in the container.
To get it you can use this command
symfony console debug:container --tag=request.param_converter
Retrieve the Service ID of the service you want to override.
Then you can configure your service to decorate FOS one
My\Project\MyParamConverter:
decorates: 'TheIdOf_FOS_ParamConverterService'
arguments: [ '#My\Project\MyParamConverter.inner' ] # <-- this is the instance of fos service
Maybe you'll need to add the tags to this declaration, I'm not sure.
Let me know if you're facing an error.

Symfony override autowired services

I'm writing a Symfony 4 bundle and inside, in a compiler pass, I create multiple service definitions based on an abstract one (also enabling autowiring based on the argument name):
$managerDefinition = new ChildDefinition(Manager::class);
$managerDefinition->replaceArgument(0, $managerName);
...
$container->registerAliasForArgument($managerId, Manager::class, $managerName . 'Manager');
And this is the abstract service definition:
services:
MyBundle\Manager:
abstract: true
arguments:
- # manager name
So, in my App controller I can have this and it works correctly:
public function __construct(MyBundle\Manager $barManager)
{
// $barManager is MyBundle\Manager
}
Now, let's say at some point I decide to extend the Manager class in my App with additional methods:
class MyManager extends \MyBundle\Manager
{
public function newMethod() {
...
}
}
I override the bundle's abstract service like this:
services:
MyBundle\Manager:
class: App\Manager
abstract: true
arguments:
- # manager name
Everything still works as expected:
public function __construct(MyBundle\Manager $barManager)
{
// $barManager is App\Manager
$barManager->newMethod(); // Works
}
However, the IDE complains that newMethod() does not exist, as it doesn't exist in the typehinted MyBundle\Manager.
So, it seems more correct to change my constructor definition to let it know the actual class it's going to receive:
public function __construct(App\Manager $barManager)
However, I can't write this, as auto-wiring no longer works.
I suppose I could write a compiler pass in my App that registers autowiring for my custom App\Manager, but that seems like an overkill.
I can't shake the feeling that I'm doing something fundamentally wrong.
I guess my question is, what would be the best way to allow easy overriding of the abstract Manager definition in the bundle?

How to use Symfony autowiring with multiple entity managers

I would like to use the autowiring in a service that use 2 different entity manager. How to achieve something like that ?
use Doctrine\ORM\EntityManager;
class TestService
{
public function __construct(EntityManager $emA, EntityManager $emB)
{
}
}
My service.yml file use to be configured like that :
app.testservice:
class: App\Services\TestService
arguments:
- "#doctrine.orm.default_entity_manager"
- "#doctrine.orm.secondary_entity_manager"
There are already two good answers posted but I'd like to add a third as well as some context to help chose which approach to use in a given situation.
emix's answer is very simple but a bit fragile in that it relies on the name of the argument for injecting the correct service. Which is fine but you won't get any help from your IDE and sometimes might be a bit awkward. The answer should probably use EntityManagerInterface but that is a minor point.
DynlanKas's answer requires a bit of code in each service to locate the desired manager. It's okay but can be a bit repetitive. On the other hand, the answer is perfect when you don't know in advance exactly which manager is needed. It allows you to select a manager based on some dynamic information.
This third answer is largely based on Ron's Answer but refined just a bit.
Make a new class for each entity manager:
namespace App\EntityManager;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
class AEntityManager extends EntityManagerDecorator {}
class BEntityManager extends EntityManagerDecorator {}
Don't be alarmed that you are extending a decorator class. The class has the same interface and the same functionality as a 'real' entity manager. You just need to inject the desired manager:
# config/services.yaml
App\EntityManager\AEntityManager:
decorates: doctrine.orm.a_entity_manager
App\EntityManager\BEntityManager:
decorates: doctrine.orm.b_entity_manager
This approach requires making a new class for each entity manager as well as a couple of lines of configuration, but allows you to simply typehint against the desired class:
public function __construct(AEntityManager $emA, BEntityManager $emB)
{
}
It is, arguably, the most robust and standard way to approach the original question.
Dylan's answer violates the Demeter's Law principle. It's very easy and elegant since Symfony 3.4, meet Local service binding:
services:
_defaults:
bind:
$emA: "#doctrine.orm.default_entity_manager"
$emB: "#doctrine.orm.secondary_entity_manager"
Then in your service the autoloading will do the hard work for you:
class TestService
{
public function __construct(EntityManager $emA, EntityManager $emB)
{
…
}
}
The easy way would be to autowire ManagerRegistry in your constructor and use it to get the managers you want by using the names of the entity manger you have set in your configuration file (doctrine.yaml) :
use Doctrine\Common\Persistence\ManagerRegistry;
class TestService
{
private $emA;
private $emB;
public function __construct(ManagerRegistry $doctrine)
{
$this->emA = $doctrine->getManager('emA');
$this->emB = $doctrine->getManager('emB');
}
}
And you should be able to use them as you want.
Another way would be to follow this answer by Ron Mikluscak
Simply use EntityManagerInterface $secondaryEntityManager
If you're using Symfony's framework bundle (which I'm pretty sure you are), then Symfony >= 4.4 automatically generates camelcased autowiring aliases for every Entitymanger you define.
You can simply get a list of them using the debug:autowiring console command. For your configuration above, this should look something like this:
bin/console debug:autowiring EntityManagerInterface
Autowirable Types
=================
The following classes & interfaces can be used as type-hints when autowiring:
(only showing classes/interfaces matching EntityManagerInterface)
EntityManager interface
Doctrine\ORM\EntityManagerInterface (doctrine.orm.default_entity_manager)
Doctrine\ORM\EntityManagerInterface $defaultEntityManager (doctrine.orm.default_entity_manager)
Doctrine\ORM\EntityManagerInterface $secondaryEntityManager (doctrine.orm.secondary_entity_manager)
As described in https://symfony.com/doc/4.4/doctrine/multiple_entity_managers.html:
Entity managers also benefit from autowiring aliases when the framework bundle is used. For example, to inject the customer entity manager, type-hint your method with EntityManagerInterface $customerEntityManager.
So you should only need:
use Doctrine\ORM\EntityManagerInterface;
class TestService
{
public function __construct(
EntityManagerInterface $defaultEntityManager,
EntityManagerInterface $secondaryEntityManager
) {
// ...
}
}
The name $defaultEntityManager isn't mandatory, though it helps to distinguish between the two. Every argument that's typehinted with an EntityManagerInterface and isn't in the list returned by debug:autowiring EntityManagerInterface will result in the default Entitymanager being autowired.
Note: As written in the documentation and shown in the output of debug:autowiring, you need to use EntityManagerInterface for this aliased autowiring, not the actual EntityManager class. In fact, you should always autowire the Entitymanager using EntityManagerInterface.

Service in symfony2 - how service file should look like?

I am trying to create service in symfony2 which will verify if session contains certain information and if not redirect the user to another controller. I want this piece of code to work as a service as I will be using it in many controllers.
I have problem as manual on Symfony2 book does not provide information how service file should look like. Should it be a normal php class?
Please find below dump of my files with information on error that I receive.
In \AppBundle\Services I create file my_isbookchosencheck.php containing:
<?php
namespace AppBundle\my_isbookchosencheck;
class my_isbookchosencheck
{
public function __construct();
{
$session = new Session();
$session->getFlashBag()->add('msg', 'No book choosen. Redirected to proper form');
if(!$session->get("App_Books_Chosen_Lp")) return new RedirectResponse($this->generateUrl('app_listbooks'));
}
}
My service.yml:
my_isbookchosencheck:
class: AppBundle\Services\my_isbookchosencheck
My conntroller file:
/**
* This code is aimed at checking if the book is choseen and therefore whether any further works may be carried out
*/
$checker = $this->get('my_isbookchosencheck');
Error:
FileLoaderLoadException in FileLoader.php line 125: There is no extension able to load the configuration for "my_isbookchosencheck" (in C:/wamp/www/symfony_learn/app/config\services.yml). Looked for namespace "my_isbookchosencheck", found "framework", "security", "twig", "monolog", "swiftmailer", "assetic", "doctrine", "sensio_framework_extra", "fos_user", "knp_paginator", "genemu_form", "debug", "acme_demo", "web_profiler", "sensio_distribution" in C:/wamp/www/symfony_learn/app/config\services.yml (which is being imported from "C:/wamp/www/symfony_learn/app/config\config.yml").
There are few mistakes that you made, which I am going to explain in short, and I will give you an example of the service you want to create.
You created your service in AppBundle\Services, yet your namespace is registered differently - namespace AppBundle\Services\my_isbookchosencheck;. It should be namespace AppBundle\Services;. I would also advise you to use singular names when creating directories - in this case Service would be better, instead of Services.
You're using your __constructor directly to apply some logic and return the result of it. Better way would be to create a custom method, which could be accessed when necessary.
You're creating new instance of Session which means that you wont be able to access anything that was previously added and stored in session. The right way here, would be to inject RequestStack which holds the current Request and get the session from there.
I believe you also registered your service wrong. In your services.yml file, it should be under services: option. This is why you got the error you pasted.
So, let's see how your service should like.
services.yml
services:
book_service:
class: AppBundle\Service\BookService
arguments:
- #request_stack
- #router
BookService.php
namespace AppBundle\Service;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RouterInterface;
class BookService {
/* #var $request Request */
private $request;
/* #var $router RouterInterface */
private $router;
public function __construct(RequestStack $requestStack, RouterInterface $router) {
$this->request = $requestStack->getCurrentRequest();
$this->router = $router;
}
public function isBookChoosen() {
$session = $this->request->getSession();
// Now you can access session the proper way.
// If anything was added in session from your controller
// you can access it here as well.
// Apply your logic here and use $this->router->generate()
}
}
Now in your controller you can simply use it like this:
$this->get('book_service')->isBookChoosen()
Well this is a short example, but I hope you got the idea.
try
services:
my_isbookchosencheck:
class: AppBundle\Services\my_isbookchosencheck
in your services.yml, and check that you use the correct namespaces.
Your Class is fine and it should work, however may i suggest that you use
symfony2 session service instead of creating the session object yourself, you can pass it as a constructor argument:
<?php
// namespace edited
namespace AppBundle\Services;
use Symfony\Component\HttpFoundation\Session\Session;
class my_isbookchosencheck
{
public function __construct(Session $session);
{
$session->getFlashBag()->add('msg', 'No book choosen. Redirected to proper form');
if(!$session->get("App_Books_Chosen_Lp")) return new RedirectResponse($this->generateUrl('app_listbooks'));
}
}
and then edit your services.yml accordingly, so the service container will inject the session object:
services:
my_isbookchosencheck:
class: AppBundle\Services\my_isbookchosencheck
arguments: [#session]
Also check out his question on so:
How do you access a users session from a service in Symfony2?
Services are just regular PHP classes, nothing special. But you must register it in order to be recognized by the system. Here are the steps how you do it,
Create a regular PHP class (you can inject other services if it requires)
namespace Acme\DemoBundle\Service;
class MyService
{
private $session;
public function _construct(SessionInterface $session /* here we're injecting the session service which implements the SessionInterface */)
{
$this->session = $session;
}
// other methods go here, which holds the business logic of this class
}
ok, we created a class, we need to register it to be able to use it by service container, here how you do it:
the simplest way is to put it into config.yml file, like this:
services:
my_service:
class: Acme\DemoBundle\Service\MyService
arguments:
- #session
or, another way, is to create a file (e.g. services.yml, may be in config folder), and import it inside the config.yml file (the content of the file is the same as the first way):
imports:
- { resource: services.yml }
or, you can create a services.yml(the content of the file is the same as the first way) file inside you bundle's Resources folder, specify it under the load method of your Extension class (under the DependencyInjection folder), (this way requires some special directory and file structure, read about it in the doc):
class AcmeDemoExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources'));
$loader->load('services.yml');
}
}
In you case, you're not registering your service, the service container just couldn't find it. Register it by one of the above ways.

Symfony2 Adding 3rd party Bundles to a Service Controller

I am looking to get some help on how to add a 3rd party bundle to an existing service controller, specifically KnpSnappyBundle https://github.com/KnpLabs/KnpSnappyBundle.
The bundle was installed with composer, and registerBundles() function in app/AppKernel.php was updated.
I am having trouble with passing the bundle to a service controller, via its constructor.
1: Unsure how to figure out the path to use
--SomeController.php file--
...
use Symfony\Component\???\???
class FormDataController
{
...
private $pdf;
2: Also unsure how to figure out what the object type is named.
public function __construct(..., KnpSnappyBundle? $pdf )
{
...
$this->pdf= $pdf;
return $this;
}
3: Then in the servives.yml file i add the following argument to the correct controller
- "#knp_snappy.pdf"
After doing the above, the controller should be able to do the following to access the bundles functions, correct?
$this->pdf->...
Why are you doing this?
Quote:
The bundle registers two services:
the knp_snappy.image service allows you to generate images;
the knp_snappy.pdf service allows you to generate pdf files.
Just access them like this (if you use standard Symfony controller):
$this->get('knp_snappy.pdf')->...;
$this->get('knp_snappy.image')->...;
UPDATE:
If you are injecting knp_snappy.pdf service into your custom service, you should use Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator class.
--SomeController.php file--
...
use Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator;
class FormDataController
{
// ... //
private $pdf;
public function __construct(..., LoggableGenerator $pdf )
{
...
$this->pdf = $pdf;
return $this;
}
// ... //
}
When you run ./app/console container:debug command, it lists all available services and corresponding classes. Very helpful.

Resources