Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access Symfony Config Parameter in Entity Repository or Anywhere

Tags:

php

symfony

I have a whole lot of configuration settings that I would like to be able to access throughout my application. For example, I have an entity repository with some custom queries. I would like to have a global parameter that sets the default "limit" to something like 10 records unless I specifically change it.

class ViewVersionRepository extends EntityRepository {

    /**
     * Get all the versions for the specified view id, ordered by modification time.
     * @param integer $id
     * @param integer $limit
     * @param integer $offset default 0
     * @return array
     */
    public function findByViewIdLimit($id, $limit = NULL, $offset = 0) {

        // this won't work because we don't have access to the container... ;(
        $limit = ($limit != NULL) ? $limit : $this->container->getParameter('cms.limit');

        return $this->createQueryBuilder('v')
            ->where('v.viewId = :id')
            ->orderBy('v.timeMod', 'DESC')
            ->setParameter('id', $id)
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

}

In my pre-Symfony life, I could easily set this in my parameters.yml:

cms.limit: 10

or create a constant for this in some app config file:

define('cms.limit', 10);

But in Symfony, if I use a parameter, how can I access that from my entity repository since the "container" is not available in an entity repository?

People say "pass it in as an argument" but honestly, that's just clutter and senseless work for every time I call the query. What's the point of having parameters if you can't access them!

Others say you have to construct some global service model (which I haven't understood yet either and seems like a HUGE effort for just fetching parameters).

So what is the "best" way to access global parameters like this in ANY context (including the one here)?

I don't want to access the kernel, but if that's the last resort, why is it "wrong" to get the parameter from the kernel like:

global $kernel;
$assetsManager = $kernel->getContainer()->get('acme_assets.assets_manager');‏

I can't believe it is this hard to access global parameters. I understand the idea of needing to be explicit about the dependencies. But in an application where the parameters will always be available, and you always want to use them as say a default, why isn't this standard and easy?


Update

Per the answers below I have turned my ViewVersionRepository into a service (that is what you were suggesting right?).

class ViewVersionRepository extends EntityRepository

{

    // do not evidently need to set $this->viewVersion either
    // protected $viewVersion;
    protected $limit;

    /**
     * Allow limit to be set by parameter, injected by service definition
     * @param $limit
     */
    public function setLimit($limit) {
        $this->limit = $limit;
    }



    /**
     * Get all the versions for the specified view id, ordered by modification time.
     * @param integer $id
     * @param integer $limit default from global parameter
     * @param integer $offset default 0
     * @return array
     */
    public function findByViewIdLimit($id, $limit = NULL, $offset = 0) {

        $limit = (!empty($limit)) ? $limit : $this->limit;

        return $this->createQueryBuilder('v')
            ->where('v.viewId = :id')
            ->orderBy('v.timeMod', 'DESC')
            ->setParameter('id', $id)
            ->setFirstResult($offset)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

}

Add Service:

gutensite_cms.view_version_repository:
    class: Gutensite\CmsBundle\Entity\View\ViewVersionRepository
    factory_service: 'doctrine.orm.default_entity_manager'
    factory_method: 'getRepository'
    arguments:
        - 'Gutensite\CmsBundle\Entity\View\ViewVersion'
    calls:
        - [setLimit, ['%cms.limit.list.admin%']]

This results in an error complaining about the factory_service definition:

You have requested a non-existent service "doctrine.orm.default_entity_manager". Did you mean one of these: "doctrine.orm.cms_entity_manager", "doctrine.orm.billing_entity_manager"?

Fortunately, if I change factory_service to the suggested doctrine.orm.cms_entity_manager it works. Can someone explain why?

But then I get another error related to a method in the repository not existing (because the repository is broken). I thought that having the arguments in the service definition, means I needed to add a __construct($viewVersion) method to ViewVersionRepository to accept the arguments list. But that resulted in a nasty error that broke the repository. So I removed that because evidently this must be in the default Symfony EntityRepository (how am I supposed to know that?). I did keep a custom setLimit() method for the passing the global parameters into the call.

So now the service works... but that seems like SUCH a ton of work, just to get access to global parameters that should function as a DEFAULT if no parameters are passed into the method.

Why shouldn't we use a $GLOBAL or a constant from a config file in these sorts of situations where we are setting defaults? This doesn't break the dependency model, the custom variables can still be passed into the method, only we have a global app wide default. I don't understand why that is frowned upon...

like image 781
Chadwick Meyer Avatar asked May 16 '14 18:05

Chadwick Meyer


2 Answers

It's worth while to study and understand how to create a repository as a service. Something like:

view_version_repository:
    class:  My\SomeBundle\Entity\ViewVersionRepository
    factory_service: 'doctrine.orm.default_entity_manager'
    factory_method:  'getRepository'
    arguments:  
        - 'My\SomeBundle\Entity\ViewVersion'
    calls:
         - [setLimit, ['%limit_parameter%']]

In your controller you would do:

$viewVersionRepository = $this->container->get('view_version_repository');

See docs: http://symfony.com/doc/current/components/dependency_injection/factories.html

The effort expended in learning how to use services will repay itself many times over.

like image 67
Cerad Avatar answered Sep 30 '22 19:09

Cerad


I personally used a custom config class. You can import your config class wherever you need - entity, repository, controller etc.

To answer your question why "pass it as an argument" just study the dependency injection pattern. It is really a flexible and cool way to solve many of the problems when you want to maintain your project for a longer time.

like image 28
Laurynas Mališauskas Avatar answered Sep 30 '22 18:09

Laurynas Mališauskas