Injecting dependencies using annotations in Symfony - php

I come from a Spring/Java background and i'm learning Symfony/PHP. It's amazingly similar and i love it.
In Spring we can #Autowire dependencies. What is the equivalent of that in Symfony? I want to inject my dependencies using annotations. And i dont want to specify that in an xml or yml.
For example:
class foo {
/**
* #Inject \ABC\XYZ\Dependency
*/
private $dependency;
public function abc(){
$dependency->bar();
}
};
Also (now this just got on my mind now), is it possible to do something like this. Using annotations, declaring a name and scope for the service:
/**
* #Service("someService")
* #Scope("session / request / ..")
*/
class foo {
/**
* #Inject \ABC\XYZ\Dependency
*/
private $dependency;
public function abc(){
$dependency->bar();
}
};

Autowiring will be available as of Symfony 2.8: http://symfony.com/blog/new-in-symfony-2-8-service-auto-wiring

Related

Dependency injection of FilesystemCache (SimpleCache) in Symfony 4.0

I am trying to implement a SimpleCache concrete instance in one of my service classes to also allow caching, however I am having some issues at wiring the dependencies.
config/services.yml
services:
Psr\SimpleCache\CacheInterface: '#Symfony\Component\Cache\Simple\FilesystemCache'
src/Services/Shouter/Sources/Twitter.php
<?php
namespace App\Services\Shouter\Sources;
use Psr\SimpleCache\CacheInterface;
class Twitter
{
/**
* Cache instance
* #var Psr\SimpleCache\CacheInterface
*/
protected $cache;
public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}
}
This is the error that I get:
You have requested a non-existent service "Symfony\Component\Cache\Simple\FilesystemCache".
Fixed by adding Symfony\Component\Cache\Simple\FilesystemCache: in the services.yaml.

PhpStorm phpUnit autocomplete broken because of setUp method

Make PhpStorm autocomplete fields defined in phpUnit's setUp method.
If I define a mock in setUp method:
public function setUp()
{
$this->testRepo = $this->getMockBuilder(TestRepository::class)
->disableOriginalConstructor()
->getMock();
}
When I want to use this mock in other methods:
public function testExample()
{
$this->testRepo->.... at this point phpStorm does not show autocomplete options
}
I understand that phpStorm doesn't know that setUp method is run before each other test method but maybe there's a way to fix this behavior.
I also don't want to add phpDoc to each property defined. I find this pretty robust and ugly:
/**
* #var PHPUnit_Framework_MockObject_MockObject
*/
protected $testRepo;
PS: Stackoverflow editor is s**t
Change the PHPDoc annotation as follow:
/**
* #var \PHPUnit_Framework_MockObject_MockObject|TestRepository
*/
protected $testRepo;
hope this help

Circular Dependency with Doctrine ORM, Doctrine PHPCR-ODM, and Event Listeners

I am working on a complex Symfony project that blends Doctrine ORM objects with Doctrine PHPCR-ODM documents. Everything works fine, but I have been unable to solve circular dependency injection problems between listeners in the container.
The scenario is, I have multiple ODM documents that set ORM references as they are loaded, which is accomplished via an event listener. An example configuration is:
services.yml:
example.event_listener.my_document:
class: Example\Common\EventListener\MyDocumentEventListener
arguments: [#doctrine]
tags:
- { name: doctrine_phpcr.event_listener, event: postLoad }
- { name: doctrine_phpcr.event_listener, event: prePersist }
Example\Common\EventListener\MyDocumentEventListener.php:
namespace Example\Common\EventListener;
use Example\Common\ODM\Document\MyDocument;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ODM\PHPCR\DocumentManager;
/**
* Listener for {#link Example\Common\ODM\Document\MyDocument} events.
*/
class MyDocumentEventListener
{
/*
* #var Doctrine\Common\Persistence\ManagerRegistry
*/
private $managerRegistry;
/**
* Constructor.
*
* #param Doctrine\Common\Persistence\ManagerRegistry $documentManager A Doctrine {#link Doctrine\Common\Persistence\ManagerRegistry}.
*/
public function __construct(ManagerRegistry $managerRegistry)
{
$this->managerRegistry = $managerRegistry;
}
/**
* After loading a document, ensure that the references exist
* to each ORM dependency.
*
* #param Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args)
{
if (get_class($args->getObject()) == 'Example\Common\ODM\Document\MyDocument') {
$this->loadDependencies($args->getObject(), $args->getObjectManager());
}
}
/**
* Prior to persisting a document, ensure that the references exist
* to each ORM dependency.
*
* #param Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args)
{
if (get_class($args->getObject()) == 'Example\Common\ODM\Document\MyDocument') {
$this->loadDependencies($args->getObject(), $args->getObjectManager());
}
}
/**
* Pull relational information from the ORM database to populate
* those fields in the {#link Example\Common\ODM\Document\MyDocument} document that
* require it. Each field is populated as a reference, so it will be
* loaded from the database only if necessary.
*
* #param Example\Common\ODM\Document\MyDocument $document The MyDocument to load dependencies for.
* #param Doctrine\ODM\PHPCR\DocumentManager $documentManager The DocumentManager for the MyDocument.
*/
private function loadDependencies(MyDocument $document, DocumentManager $documentManager)
{
$reflectionClass = $documentManager->getClassMetadata(get_class($document))->getReflectionClass();
$exampleProperty = $reflectionClass->getProperty('example');
$exampleProperty->setAccessible(true);
$exampleProperty->setValue(
$document,
$this->managerRegistry->getManager()->getReference('Example\Common\ORM\Entity\MyEntity', $document->getExampleId())
);
}
}
Everything above works perfectly fine when working with MyDocument objects. (This is basically an exact implementation of what is described in the Doctrine documentation for blending ORM and MongoDB ODM).
Now the problem is when I also want to do the reverse within the same application -- that is to say, I also want to have an ORM entity which has a listener that fills in a reference or references to ODM documents.
Without adding more code, let's say I extend my services.yml configuration to:
example.event_listener.my_document:
class: Example\Common\EventListener\MyDocumentEventListener
arguments: [#doctrine]
tags:
- { name: doctrine_phpcr.event_listener, event: postLoad }
- { name: doctrine_phpcr.event_listener, event: prePersist }
example.event_listener.my_entity:
class: Example\Common\EventListener\MyEntityEventListener
arguments: [#doctrine_phpcr]
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: postLoad }
This will now fail, because we have a circular dependency: the container tries to inject the ODM listener into the DocumentManager's listeners, which in turn tries to inject the EntityManager, which in turn tries to inject its own listeners, which each try to inject the DocumentManager, and so on. (Note that this example uses the Registry rather than the manager, but the result is the same).
I have tried a few different approaches to resolve this but haven't hit on one that works yet. Has anybody been able to get bi-directional listeners between ORM and ODM to work like this in a single project?
I have found few examples around this, unfortunately. My workaround thus far would be to create a service to handle the loading/persisting of these objects and then run everything through that, but it seems very hackish compared to using an elegant event-driven system.
Cleaning up this old question with what turned out to be the correct answer as noted in a comment above: injecting the full container. Normally I avoid this, but it turned out to be the only way to solve this particular problem.
In case anybody is looking for an example of an ODM listener that loads ORM-based dependencies, here is a working example of what I arrived at:
Service definition:
example.event_listener.odm.my_document:
class: Example\Common\EventListener\MyDocumentEventListener
arguments: [#service_container]
tags:
- { name: doctrine_phpcr.event_listener, event: postLoad }
The listener:
<?php
namespace Example\Common\EventListener;
use Example\Common\Document\MyDocument;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ODM\PHPCR\DocumentManager;
use Symfony\Component\DependencyInjection\Container;
class MyDocumentEventListener
{
/*
* #var \Symfony\Component\DependencyInjection\Container
*/
private $container;
/**
* Constructor.
*
* #param \Symfony\Component\DependencyInjection\Container $container A Symfony dependency injection container.
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* After loading a document, ensure that the references exist
* to each ORM dependency.
*
* #param \Doctrine\Common\Persistence\Event\LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args)
{
if (get_class($args->getObject()) == 'Example\Common\Document\MyDocument') {
$this->loadDependencies($args->getObject(), $args->getObjectManager());
}
}
/**
* Pull relational information from the ORM database to populate
* those fields in the document that require it. Each field is
* populated as a reference, so it will be loaded from the database only
* if necessary.
*
* #param \Example\Common\Document\MyDocument $document The document to load dependencies for.
* #param \Doctrine\ODM\PHPCR\DocumentManager $documentManager The DocumentManager for the document.
*/
private function loadDependencies(MyDocument $document, DocumentManager $documentManager)
{
$documentReflectionClass = $documentManager->getClassMetadata(get_class($document))->getReflectionClass();
$someOrmProperty = $documentReflectionClass->getProperty('orm_property');
$someOrmProperty->setAccessible(true);
$someOrmProperty->setValue(
$document,
$this->container->get('doctrine.orm.entity_manager')->getReference('Example\Common\Model\MyModel', $document->getOrmPropertyId())
);
}
}
This allows the document class to store an ID to an ORM model entity, and each time the document is loaded, it fills in the actual reference to the ORM model within the document. This makes it behave as if it knew about the ORM property all along, and works pretty well.
This example uses PHPCR-ODM but would work just fine for MongoDB ODM as well.

Is there a way to tell PHPStorm to hide a method/function/variable/etc

I'm looking for a way to make PHPStorm hide some methods from code completion. I've tried to annotate the DocBlocks with #access private but that does not hide them from view.
Is there any way to hide private API, short of writing/generating a stub file with a limited interface and referencing that in my project?
for example:
Lets say the library has this in it:
<?php
interface IDoABunchOfStuff
{
/**
* My library users use this
*/
public function doFoo();
/**
* My Library needs this but requires that my users don't see it.
*
* #access private
* #visibility none
* #package mylib
* #internal
*/
public function doBar();
}
class Foo extends Something implements IDoABunchOfStuff
{
/**
* does foo
*/
public function doFoo()
{
// ...
}
/**
* does bar. for internal use only
*
* #access private
* #visibility none
* #package mylib
* #internal
*/
public function _doBar()
{
// ...
}
}
And my library user is typing:
<?php
myAwesomeFunction(IDoABunchOfStuff $aFoo)
{
if($->$oFoo->[CTRL+SPACE] // invoking code completion...
Is it possible to (and if it is how do I) make it so that my user never sees _doBar?
Neither of the different annotations i've tried seem to have the desired effect.
P.S. I'm using PHPStorm 4.0.3
additional:
In this case I am implementing ArrayAccess and I don't want offsetGet, offsetSet, offsetExists and offsetUnset cluttering up my code completion window but I've had similar problems elsewhere enough to warrant asking a more generalized question.
Nope -- you cannot do such thing in current version of PhpStorm.
There is a ticket on Issue Tracker that suggests using #access tag for this purpose, but currently it is not scheduled to be implemented for any particular version: http://youtrack.jetbrains.com/issue/WI-5788
Feel free to vote/comment/etc and maybe it will be implemented sooner.

Preserving auto-completion abilities with Symfony2 Dependency Injection

I'm using PHP Storm as my IDE, but I believe that other IDE's such as Netbeans will have the same issue as I'll explain below.
When using a framework like Symfony2, we have the wonderful world of Dependency Injection added. So objects can simply be instantiated using code like the following snippet:
$myThingy = $this->get('some_cool_service');
This is very handy, as objects are already configured beforehand. The one problem is, that auto-completion breaks entirely in basically any PHP IDE, as the IDE does not know what type the get() method is returning.
Is there a way to preserve auto-completion? Would creating for example an extension of Controller be the answer? For example:
class MyController extends Controller {
/**
* #return \MyNamespace\CoolService
*/
public getSomeCoolService() {
return new CoolService();
}
}
and then for application controllers, specify MyController as the base class instead of Controller?
What about using a Factory class, or any other possible methods?
It is more involving, but you can still do this with eclipse PDT:
$myThingy = $this->get('some_cool_service');
/* #var $myThingy \MyNamespace\CoolService */
UPDATE:
The example on this page shows you may also use the other way round with phpStorm:
$myThingy = $this->get('some_cool_service');
/* #var \MyNamespace\CoolService $myThingy */
You could define private properties in your controllers
class MyController extends Controller
{
/**
* #var \Namespace\To\SomeCoolService;
*/
private $my_service;
public function myAction()
{
$this->my_service = $this->get('some_cool_service');
/**
* enjoy your autocompletion :)
*/
}
}
I use base Controller class for bundle. You need to annotate the return in method. At least that works on Eclipse.
/**
* Gets SomeCoolService
*
* #return \Namespace\To\SomeCoolService
*/
protected function getSomeCoolService()
{
return $this->get('some_cool_service');
}
I don't like /*var ... */, because it gets too much into code.
I don't like private properties, because you can wrongly assume that services are already loaded.
I use Komodo Studio, and tagging variables with #var, even inside methods, preserves auto completion for me.
namespace MyProject\MyBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
class WelcomeController extends ContainerAware
{
public function indexAction()
{
/*#var Request*/$request = $this->container->get('request');
$request->[autocomplete hint list appears here]
}
}
working with netbeans IDE 7.1.2 PHP

Categories