0

I posted another question trying to find a way to statically access a repository class outside of a controller in a custom "helper" class.

So far the only way I have figured out how to achieve this is using the code below. If anyone wants to chime into the other question about "best practice" or "design patterns" please do.

I opened this question to seek the best method on having a singleton service (?) loaded when symfony boots so other classes can access it statically without any dependency injection. I haven't had much luck on finding any official docs or common practices. I know singleton is anti practice, but is the method below the best way, or is there a more ideal solution?

services.yml

parameters:
    entity.device: Asterisk\DbBundle\Entity\Device
services:
    asterisk.repository.device:
    class: Asterisk\DbBundle\Entity\Repositories\DeviceRepository
    factory: ["@doctrine.orm.asterisk_entity_manager", getRepository]
    arguments:
        - %entity.device%
    tags:
        - {name: kernel.event_listener, event: kernel.request, method: onKernelRequest}

DeviceRepository

class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
    /** @var  ExtendedEntityRepository */
    protected static $instance;

    public function __construct(EntityManager $entityManager, ClassMetadata $class)
    {
        parent::__construct($entityManager, $class);

        if(static::$instance instanceof static == false)
            static::$instance = $this;
    }

    public static function getInstance()
    {
        return static::$instance;
    }

    public function onKernelRequest($event)
    {
        return;
    }
}
Community
  • 1
  • 1
StrikeForceZero
  • 2,379
  • 1
  • 25
  • 36

2 Answers2

1

Glad to see you are not running around anymore.

Your approach is not going to work unless someone grabs the repository out of the container first so self::$instance is initialized. But you really don't want to do this anyways. Super hacky.

You want to inject the repository service into your kernel listener. Trying to make the repository act as a kernel listener is just not a good design. So just make a service for your repository and then a second one for the listener. It may seem a bit strange at first but it really does work well in practice and it's the way S2 is designed.

If for some reason you are stuck with the notion that you have to be able to access the container globally then be aware that your kernel is defined globally(take a look at app.php) and it has a getContainer method in it.

$repo = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');

But again, there should be no need to do this.

==============================

Update - It looks like you are trying to use the listener functionality just to setup singletons. You should try to avoid singletons but if you really think you need them then the global access to the kernel can be used:

class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
  /** @var  ExtendedEntityRepository */
  protected static $instance;

  public static function getInstance()
  {
    if (!static::$instance) {
        static::$instance = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
    }
    return static::$instance;
  }

Poor design but at least it get's rid of the listener hack and it avoids creating the repository until it's actually needed. It aslo means you can access the repository from commands (listeners are not setup when commands are called).

Cerad
  • 48,157
  • 8
  • 90
  • 92
  • Could you elaborate a little on injecting the repo service into a kernel listener? Are you saying what I'm doing is fine but I should inject into a single kernel listener instead of each repo being a kernel listener (like my example code above)? – StrikeForceZero Oct 15 '15 at 01:15
  • Are you using the listener capability just as a means to setup a repository singleton instance? – Cerad Oct 15 '15 at 10:56
  • yes, becuase I know of no other way to have it loaded when symfony boots – StrikeForceZero Oct 15 '15 at 14:20
0

I do not understand what the profit will be about this method. The idea of the servicecontainer is to make just one instance of each class and give a reference (or pointer if you like) to any method who asks to use this same instance. Let me proof it:

Service definition:

// app/config.yml
services:
    app.test:
        class: Vendor\AppBundle\Service\Test

and a custom class:

// src/AppBundle/Service/Test.php
namespace AppBundle/Service;

class Test {
    public $test = 0;
}

and a controller:

// src/AppBundle/Controller/DefaultController
namespace AppBundle/Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction()
    {
        $instance1 = $this->get('app.test');
        $instance2 = $this->get('app.test');

        $instance1->test = 1;

        echo $instance2->test; // RETURNS 1 !!!
        exit;
    }
Frank B
  • 3,667
  • 1
  • 16
  • 22
  • correct: but if you need to access a service outside of a controller (see other question linked in question) I need to be able to call Test::someMethod() outside of the controller – StrikeForceZero Oct 14 '15 at 20:14
  • http://stackoverflow.com/questions/12056178/how-to-access-service-container-in-symfony2-global-helper-function-service – Frank B Oct 14 '15 at 20:28