How to inject dependency using Factory Pattern in Symfony2 - symfony

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);

Related

Why do actions with class and public method don't fire __construct()

I'm trying to understand how WordPress works with actions, classes, and methods.
If there is a class "TestClass" and it has a public method 'method1'
The method can be hooked to any action as "add_action('theHook', ['TestClass', 'method1']);"
From my understanding. If you don't initialize the class, you can not access its public methods and objects. Now, I would assume that WordPress has to follow this, and it must initialize my "TestClass", which will cause for public __construct() to fire.
However, after testing this, it does not fire __construct()..
Why is this?. I know a fix would be to self initialize inside 'method1', but I'm trying to figure out why WordPress behaves this way.
Because WordPress call your method as a static function: TestClass::method()
There is various solution:
1. Init class before add Action
Initialize your class before add action, like that:
$test = new TestClass();
add_action('hook', [$test, 'method']);
2. Call hook inside your Class:
class TestClass {
public function __construct() {
// Your construct
}
public function method() {
// Your Method
}
public function call_hook() {
add_action('hook', [$this, 'method']);
}
}
$test = new TestClass();
$test->call_hook();
3. Use a singleton
And if you need to to have only one instance of your class and call it in various place, you have to take a look to Singleton design pattern.
Demonstration:
class MySingletonClass {
private static $__instance = null;
private $count = 0;
private function __construct() {
// construct
}
public static function getInstance() {
if (is_null(self::$__instance)) {
self::$__instance = new MySingletonClass();
}
return self::$__instance;
}
public function method() {
$this->count += 1;
error_log("count:".$this->count);
}
}
$singleton = MySingletonClass::getInstance();
add_action('wp_head', [$singleton, 'method']);
$singleton2 = MySingletonClass::getInstance();
add_action('wp_footer', [$singleton2, 'method']);

Autowiring services created with factory

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.

Symfony2 : Doctrine : PHPUnit : Set entity Id during flushing with mocked entity manager in unit tests

Symfony 2.8.13 / Doctrine ORM 2.5.5 / PHPUnit 5.7.5
I want to test a method of a class that makes use of the doctrine entity manager. This public method calls a private one that instantiates a Bookmark entity, flushes it and returns this entity. Then later, in the tested method I need to access the entity Id. Everything is mocked excepted the Bookmark entity itself. The main problem is that there is no setId() method in my entity. Here is the code and my main idea to solve this issue but I don't know if it is correct ?
Tested class and method
class BookmarkManager
{
//...
public function __construct(TokenStorageInterface $tokenStorage, ObjectManager $em, Session $session)
{
//...
}
public function manage($bookmarkAction, $bookmarkId, $bookmarkEntity, $bookmarkEntityId)
{
//...
$bookmark = $this->add($bookmarkEntity, $bookmarkEntityId);
//...
$bookmarkId = $bookmark->getId();
//...
}
private function add($entity, $entityId)
{
//...
$bookmark = new Bookmark();
//...
$this->em->persist($bookmark);
$this->em->flush();
return $bookmark;
}
}
Test
class BookmarkManagerTest extends \PHPUnit_Framework_TestCase
{
public function testThatRestaurantAdditionToBookmarksIsWellManaged()
{
//...
// THIS WON'T WORK AS NO setId() METHOD EXISTS
$entityManagerMock->expects($this->once())
->method('persist')
->will($this->returnCallback(function ($bookmark) {
if ($bookmark instanceof Bookmark) {
$bookmark->setId(1);
}
}));
//...
$bookManager = new BookmarkManager($tokenStorageMock, $entityManagerMock, $sessionMock);
//...
}
}
Solutions ?
1- Make usage of reflection class as proposed here :
$entityManagerMock->expects($this->once())
->method('persist')
->will($this->returnCallback(function ($bookmark) {
if ($bookmark instanceof Bookmark) {
$class = new \ReflectionClass($bookmark);
$property = $class->getProperty('id');
$property->setAccessible(true);
$property->setValue($bookmark, 1);
//$bookmark->setId(1);
}
}));
2- Create a test Boookmark entity that extends from the real one and add a setId() method. Then create a mock of this class and replace and customize the one got from the ReturnCallback method with this one ? It seems crappy...
Any thoughts ? Thanks for your help.
The reflection looks interesting but it decreases readability of tests (mixing with mocks makes the situation tough).
I would create a fake for entity manager and implements there setting id based on reflection:
class MyEntityManager implements ObjectManager
{
private $primaryIdForPersitingObject;
public function __construct($primaryIdForPersitingObject)
{
$this->primaryIdForPersitingObject = $primaryIdForPersitingObject;
}
...
public function persist($object)
{
$reflectionClass = new ReflectionClass(get_class($object));
$idProperty = $reflectionClass->getProperty('id');
$idProperty->setAccessible(true);
$idProperty->setValue($object, $this->primaryIdForPersitingObject);
}
public function flush() { }
...
}
Once you implemented this, you can inject the instance of MyEntityManager and make your tests small and easier to maintain.
You test would look like
<?php
class BookmarkManagerTest extends \PHPUnit_Framework_TestCase
{
public function testThatRestaurantAdditionToBookmarksIsWellManaged()
{
// ...
$entityManager = MyEntityManager(1);
//...
$bookManager = new BookmarkManager($tokenStorageMock, $entityManager, $sessionMock);
//...
}
}
Of course, a situation may be harder if there is a need of setting different ids for many persisting objects. Then you can, for example, increase $primaryIdForPersitingObject on persist call
public function persist($object)
{
$reflectionClass = new ReflectionClass(get_class($object));
$idProperty = $reflectionClass->getProperty('id');
$idProperty->setAccessible(true);
$idProperty->setValue($object, $this->primaryIdForPersitingObject);
$this->primaryIdForPersitingObject++;
}
It may be extended even further to have separate primaryIdForPersitingObject each entity class, and your tests will be still clean.

How can I bind a Symfony config tree to an object

How would I go about binding a Symfony config tree to a class rather than returning an array?
Using Symfony\Component\Config\Definition\Processor returns an array.
In my case I want the config to be bound to a class so I can use methods to combine parts of the data.
Here is a simple example of my use case. I want the config bound to a class so I can use a method to join table.name and table.version together (my actual use case is more complex, but this is a simple example)
config.yml
db:
table:
name: some_table
version: v2
ConfigurationInterface
class DBConfiguration implements ConfigurationInterface
{
/**
* {#inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('db');
$rootNode
->children()
->arrayNode('table')
->children()
->scalarNode('name')->isRequired()->end()
->scalarNode('version')->end()
->end()
->end()
;
return $treeBuilder;
}
}
Class I want to bind the config to
class DB
{
public $table;
public function __construct()
{
$this->table = new Table();
}
}
class Table
{
public $name;
public $version;
/**
* #return string
* Calculate the full table name.
*/
public function getTableName()
{
return $this->name.'-'.$this->version;
}
}
The Symfony Config component doesn't support that.
However, in a Symfony project, this is usually done at the container compile phase. In your bundle's Extension class, you will have access to the configuration tree of your bundle in array form.
You can then take this array and assign it to a service defined in the service container that will create your config object.
This is exactly how DoctrineBundle's configuration class is built:
Abstract services (for the configuration and the factory) are defined in dbal.xml
When loading DoctrineBundle's extension, an instance of the abstract config service is created for each defined connection.
An instance of the abstract factory service is created for each defined connection.
The options array is then passed to the abstract factory service along with the configuration
When creating an instance, the factory then does the necessary transformations.
As far as I know, Symfony has no native support for this, however, you could implement it yourself. You could use subset of Symfony Serializer Component in charge of deserialization, but I think it would be an overkill. Especially since I don't see any PublicPropertyDenormalizer, only GetSetMethodNormalizer (which is denormalizer too). Therefor you would have to either make your config objects have get/set methods or roll PublicPropertyDenormalizer on your own. Possible but it really seems like an overkill and doesn't look like helping much:
Symfony Serializer Component
$array = [
'field1' => 'F1',
'subobject' => [
'subfield1' => 'SF1',
],
];
class MyConfigObject implements Symfony\Component\Serializer\Normalizer\DenormalizableInterface
{
private $field1;
private $subobject;
public function getField1()
{
return $this->field1;
}
public function setField1($field1)
{
$this->field1 = $field1;
}
public function getSubobject()
{
return $this->subobject;
}
public function setSubobject(SubObject $subobject)
{
$this->subobject = $subobject;
}
public function denormalize(\Symfony\Component\Serializer\Normalizer\DenormalizerInterface $denormalizer, $data, $format = null, array $context = array())
{
$obj = new static();
$obj->setField1($data['field1']);
$obj->setSubobject($denormalizer->denormalize($data['subobject'], 'SubObject'));
return $obj;
}
}
class SubObject
{
private $subfield1;
public function getSubfield1()
{
return $this->subfield1;
}
public function setSubfield1($subfield1)
{
$this->subfield1 = $subfield1;
}
}
$normalizer = new \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer();
$obj = (new MyConfigObject())->denormalize($normalizer, $array);
Native PHP Way
Imo this is a lot easier than above as Symfony Serializer wasn't really ment for that.
$array = [
'field1' => 'F1',
'subobject' => [
'subfield1' => 'SF1',
],
];
trait Denormalizable
{
public function fromArray($array)
{
foreach ($array as $property => $value) {
if (is_array($value)) {
if ($this->$property instanceof ArrayDenormalizableInterface) {
$this->$property->fromArray($value);
} else {
$this->$property = $value;
}
} else {
$this->$property = $value;
}
}
}
}
interface ArrayDenormalizableInterface
{
public function fromArray($array);
}
class MyConfigObject implements ArrayDenormalizableInterface
{
use Denormalizable;
public $field1;
public $subobject;
public function __construct()
{
$this->subobject = new SubObject();
}
}
class SubObject implements ArrayDenormalizableInterface
{
use Denormalizable;
public $subfield1;
}
$myConf = new MyConfigObject();
$myConf->fromArray($array);
Whatever way you choose, you can now just take array returned from symfony processor and turn it into a config object you need.

Symfony Factory class

I would like to use a ResultFactory class as a service in my Symfony 2 application:
My Result factory class will be responsible to create a BaseResult instance.
Depending on the type passed to the get factory method, the ResultFactory will create the right ResultObject.
Here's what could be the code:
class ResultFactory
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function get($type, $param)
{
$instance = null;
switch ($type) {
case 'Type1':
$instance = new Type1Result($param);
break;
case 'Type2':
$instance = new Type2Result($param);
break;
}
return $instance;
}
}
My question is:
I would like to use a service in my ResultObject. How do i inject this service to my ResultObject?
Thanks!
You are not using your service inside a result object. your factory is generating the result object.
You can define your factory service in services.yml of your bundle as:
result.factory:
class: ResultFactory
arguments: ["#translator"]
And in your controller you can call the service:
$resultObject = $this->get('result_factory')->get($type, $param);
Also you have core example how to create factory service using symfony2 in [the docs].(http://symfony.com/doc/current/components/dependency_injection/factories.html)

Resources