According to the official documentation, Autowiring allows you to manage services in the container with minimal configuration. It reads the type-hints on your constructor (or other methods) and automatically passes the correct services to each method.
So in order to have the service, the container must search for the ID that corresponds to the type-hint.
So, this are, the following interface & classes in my example
interface ServiceInterface
{
public function doSomThing(): void;
}
class Service1 implements ServiceInterface {
public function doSomThing(): void
{
echo "service1";
}
}
class Service2 implements ServiceInterface {
public function doSomThing(): void
{
echo "service2";
}
}
class Service3 implements ServiceInterface {
public function doSomThing(): void
{
echo "service3";
}
}
//service.yml
DEL\Bundle\CoreBundle\Service\Base\Service1: ~
DEL\Bundle\CoreBundle\Service\Base\Service2: ~
DEL\Bundle\CoreBundle\Service\Base\Service3: ~
And finally, this is the factory service that creates a set of classes that implement the interface ServiceInterface.
//service.yml
DEL\Bundle\CoreBundle\Service\Base\ServiceInterface:
autowire: false
factory: ['#delcore.factory.service', serviceChoice]
class ServiceFactory {
public function serviceChoice(string $param): ServiceInterface
{
switch ($param) {
case 'label1':
return new Service1();
break;
case 'label2':
return new Service2();
break;
case 'label3':
return new Service3();
break;
}
}
The problem is that the method returns an object that implements ServiceInterface which cannot be found by container through "autowiring".
What can I do? knowing that you don't want to remove the factory entry from the service.interface configuration.
Related
I need to share action methods between different controllers. Take for example the following 2 controllers:
public class AController : Controller
{
public ActionResult Index()
{
//print AController - Index
}
public ActionResult Test()
{
//print test
}
}
public class BController : Controller
{
public ActionResult Index()
{
//print BController - Index
}
}
Both controllers have an Index method which is different. The Test method however can be called from both controllers. So I want that when the following urls are entered the Test() method will execute:
AController/Test
BController/Test
I would appreciate any suggestions on how to achieve this.
Assuming the implementation of the Test() action is the same for both controllers, refactor it into a common service:
public interface ITestService {
string Test();
}
public TestService: ITestService {
public string Test() {
// common implementation
return "The test result";
}
}
Then set up Dependency Injection to acquire this service.
Your controllers then can use the common service.
public class AController : Controller {
private readonly ITestService _testService;
public AController(ITestService testservice) {
_testService = testservice;
}
public ActionResult Test() {
var vm = new TestViewModel();
vm.TestResult = _testService.Test();
return View("Test", vm);
}
}
public class BController : Controller {
private readonly ITestService _testService;
public BController(ITestService testservice) {
_testService = testservice;
}
public ActionResult Test() {
var vm = new TestViewModel();
vm.TestResult = _testService.Test();
return View("Test", vm);
}
}
Because the View Test.cshtml is rendered by both controllers, it should be placed in the Views\Shared\ folder.
You can define your own routes as described here: https://learn.microsoft.com/aspnet/core/mvc/controllers/routing
So you can define as many routes as you like to point to the "Test" method inside "AController" just like this:
routes.MapRoute("Atest", "AController/Test",
defaults: new { controller = "AController", action = "Test" });
routes.MapRoute("Btest", "BController/Test",
defaults: new { controller = "AController", action = "Test" });
But you have to define them before the "default" route because otherwise the entered URL will match the default route conditions and so it will enter that route.
It´s also possible to define the route directly in top of the method.
public class AController : Controller
{
[Route("/Some/Route")]
public ActionResult Test()
{
}
}
I want to throw in an alternative solution. Create a base controller class to be inherited by the other two. Whatever you have there will be part of the children.
public class BaseController : Controller
{
public ActionResult Index()
{
//print AController - Index
}
// Add more methods to be shared between the other controllers
}
public class AController : BaseController
{
// Has Index method already from parent
// Unique method for A
public ActionResult Test()
{
//print test 1
}
}
public class BController : BaseController
{
// Has Index method already from parent
// Unique method for B
public ActionResult Test()
{
//print test 2
}
}
This implements the actual functionality in a single place. We use this method for many projects with no issues.
I don't know how to define a callback that doesn't need any parameter. In order to describe the question, let me make up a setup - mine is different but contains several more classes, harder to digest.
Lets say we have
public class CustomerA {
public Payment payWithCash() {...}
}
and
public class CustomerB {
public Payment payWithCreditCard() {...}
}
and in addition
public abstract class Factory {
public Callback<Void, Payment> getPaymentCallback();
// Some other methods
...
}
then here comes the problem: I'd like to implement something like this
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return CustomerA::payWithCash;
}
}
and in a different class
public class CashFactoryB extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return CustomerB::payWithCreditCard;
}
}
What happens is, that the compiler complains, that CustomerB does not define payWithCreditCard(Void) and CustomerA likewise fails for payWithCash(Void).
So how to state this correct that there is no parameter to the Callback?
I am aware that I could probably solve my problem as well with interfaces, but I like to understand how to solve this with a Callback.
Thank you in advance!
Consider making Factory generic:
public abstract class Factory<T> {
public Callback<T, Payment> getPaymentCallback();
// Some other methods
...
}
And then you can do
public class CashFactoryA extends Factory<CustomerA> {
public Callback<CustomerA, Payment> getPaymentCallback() {
return CustomerA::payWithCash;
}
}
and
public class CashFactoryB extends Factory<CustomerB> {
public Callback<CustomerB, Payment> getPaymentCallback() {
return CustomerB::payWithCreditCard;
}
}
EDIT:
This answer is a wrong approach.
First, you are trying to refer payWithCash from not a instance of CustomerA but the class CustomerA, so payWithCash must be static method.
public class CustomerA {
public static Payment payWithCash() {...}
}
Then use lambda expression which is the same interface as your Callback.
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return param -> CustomerA.payWithCash();
}
}
The code above has the same meaning as the code below. The param argument will be ignored as a result.
public class CashFactoryA extends Factory {
public Callback<Void, Payment> getPaymentCallback() {
return new Callback<Void, Payment>(){
#Override
public Payment call(Void param) {
return CustomerA.payWithCash();
}
};
}
}
I have this situation
abstract class Importer {
const NW = 1;
public static function getInstance($type)
{
switch($type)
{
case(self::NW):
return new NWImporter();
break;
}
}
protected function saveObject(myObject $myObject)
{
//here I need to use doctrine to save on mongodb
}
abstract function import($nid);
}
and
class NWImporter extends Importer
{
public function import($nid)
{
//do some staff, create myObject and call the parent method to save it
parent::saveObject($myObject);
}
}
and I want to use them like this
$importer = Importer::getInstance(Importer::NW);
$importer->import($nid);
my question is: how to inject doctrine to be used in saveObject method?
thanks
You need to configure your importer as a symfony service :
services:
test.common.exporter:
# put the name space of your class
class: Test\CommonBundle\NWImporter
arguments: [ "#doctrine" ]
then in NWImporter define a constructor with a parameter that will have the doctrine instance
public function __construct($doctrine)
{
$this->doctrine= $doctrine;
}
with this solution you can avoid using a factory method as symfony does it for you but if you wanna to keep it, When you call $importer = Importer::getInstance(Importer::NW); from your controller you can inject the doctrine argument in your factory method :
abstract class Importer {
const NW = 1;
public static function getInstance($type, $doctrine)
{
switch($type)
{
case(self::NW):
return new NWImporter($doctrine);
break;
}
}
protected function saveObject(myObject $myObject)
{
//here I need to use doctrine to save on mongodb
}
abstract function import($nid);
}
then in your controller you should to do something like that :
$doctrine = $this->container->get('doctrine');
$importer = Importer::getInstance(Importer::NW, $doctrine);
$importer->import($nid);
in my implementation, I have an interface as: ICachingManager. I've got now one implementation. I also created a manager class as:
public class CachingManager
{
#region Members
private ICachingManager service;
#endregion
#region Constructors
public CachingManager(ICachingManager service)
{
this.service = service;
}
#endregion
#region Public Methods
public void EnCache<T>(string key, T value)
{
this.service.EnCache<T>(key, value);
}
public T DeCache<T>(string key)
{
return this.service.DeCache<T>(key);
}
#endregion
}
In case I had one implementation, then I can easily register the CachingManager class with Unity, automatically Unity resolves and injects the ICachingManager.
In case I had more than one implementation using named types, then how can I can make use of Unity? Do I need to make use of an Abstract Factory to decide on which named type to initialize?
Is it a good idea to make use of such a composite class or use directly implementations of the interface with Abstract Factory?
You don't have to create an abstract factory. You can inject a given named implementation:
public class MyClient
{
[Dependency("NamedManager")]
public ICachingManager CachingManager { get; set; }
// or in the constructor
public MyClient([Dependency("NamedManager")] ICachingManager cachingManager) {
// ...
}
}
or you can configure the container to do the same thing:
public class MyClient
{
public MyClient(ICachingManager cachingManager) {
// ...
}
}
...
void ContainerBuilder() {
...
Container.RegisterType<MyClient>(
new InjectionConstructor(
new ResolvedParameter<ICachingManager>("NamedManager")));
...
}
My understanding of Factory Method Pattern is (Correct me if i am wrong)
Factory Method Pattern
"Factory Method allow the client to delegates the product creation (Instance Creation) to the subclass".
There are two situation in which we can go for creating Factory Method pattern.
(i) When the client is restricted to the product (Instance) creation.
(ii) There are multiple products available.But a decision to be made which product instance
need to be returned.
If you want to create Abstract Method pattern
You need to have abstract product
Concrete Product
Factory Method to return the appropriate product.
Example :
public enum ORMChoice
{
L2SQL,
EFM,
LS,
Sonic
}
//Abstract Product
public interface IProduct
{
void ProductTaken();
}
//Concrete Product
public class LinqtoSql : IProduct
{
public void ProductTaken()
{
Console.WriteLine("OR Mapping Taken:LinqtoSql");
}
}
//concrete product
public class Subsonic : IProduct
{
public void ProductTaken()
{
Console.WriteLine("OR Mapping Taken:Subsonic");
}
}
//concrete product
public class EntityFramework : IProduct
{
public void ProductTaken()
{
Console.WriteLine("OR Mapping Taken:EntityFramework");
}
}
//concrete product
public class LightSpeed : IProduct
{
public void ProductTaken()
{
Console.WriteLine("OR Mapping Taken :LightSpeed");
}
}
public class Creator
{
//Factory Method
public IProduct ReturnORTool(ORMChoice choice)
{
switch (choice)
{
case ORMChoice.EFM:return new EntityFramework();
break;
case ORMChoice.L2SQL:return new LinqtoSql();
break;
case ORMChoice.LS:return new LightSpeed();
break;
case ORMChoice.Sonic:return new Subsonic();
break;
default: return null;
}
}
}
**Client**
Button_Click()
{
Creator c = new Creator();
IProduct p = c.ReturnORTool(ORMChoice.L2SQL);
p.ProductTaken();
}
Is my understanding of Factory Method is correct?
What you have there is actually more of an Abstract Factory Pattern, only that you factory (Creator) is not abstract. The factor method pattern is specifically useful for subclassing:
class A {
public:
A() : m_Member( GetMember() )
{
}
protected:
virtual ISomeInterface * GetMember() { // default impl here }
private:
ISomeInterface * m_Member;
}
Now subclasses of A can override GetMember to make the superclass use a specific implementation of ISomeInterface.
Define an interface for creating an
object, but let subclasses decide
which class to instantiate. Factory
Method lets a class defer
instantiation to subclasses.
more details and example there: http://www.dofactory.com/Patterns/PatternFactory.aspx
Yes, that appears to be a correct way to implement this, although pretty simplistic. In reality, you may want to account for the passing-in of various parameters that may not always be consistent across all types. Dictionaries/Lists/Hashtables/etc. are useful for this, as is serialized items and/or XML and other dynamicish things.