Too exhausted about the entrance of Controller __construct()

I learned from the tutorial and document that, I can only do the dependency injection from the controller construct methond.

Then I encountered problems.

For example:
I have a AtableGateway model class, and we used to write all the A table related logic inside this class.
as well as BtableGateway and CtableGateway, … Until ZtableGateway.

First of all, I inject AtableGateay as dependency via its __construct();

in the some1Action, we call AtableGateway’s amethod() function. In coding, I found I need to use BtableGateway’s bmethod() in amethod(), then I need to inject BtableGateway from controller’s construct method, and add 1 param on amethod() for passing the BtableGateway as DI.

When AtableGateway’s amethod() need BtableGateway’s bmethod() and B need C’s cmethod(), then C need dmethod()…
It became a long chain. everytime I need a more DI, I need to change a lot, need to do the DI one by one along the chain.

It is impossible because I just encounter, the chain is around 3-4 layers. I feel it is very inconvinient.

When I try to break and chain, and flat it inside the action. in action, call A’s a, B’s b directly. It can resolve the above problem. But it make the action very big and thick. I remember I see principle from the website that “thin controller/action, fat model”. I think it said not too much code inside action.

Then I am facing a choise and a long DI chain or a thick action, what should I do?

Ps: and I found those DI are mostly DB related, socalled tablegateway. I don’t know whether I can new the tablegateway manually as a normal class, use inside and new it.

The tablegateway DI idea comes from here Database and Models - tutorials - Laminas Docs

public function getServiceConfig()
    {
        return [
            'factories' => [
                Model\AlbumTable::class => function($container) {
                    $tableGateway = $container->get(Model\AlbumTableGateway::class);
                    return new Model\AlbumTable($tableGateway);
                },
                Model\AlbumTableGateway::class => function ($container) {
                    $dbAdapter = $container->get(AdapterInterface::class);
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Model\Album());
                    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
                },
            ],
        ];
    }

when register as lazy mode inside container, it is global shared and just single instance, maybe to use less memory on the webserver.

====================================split=================================
following is my code in a real project.

Controller action

public function __construct(ForumMainTable $forumMainTable, UserFavoriteTable $userFavoriteTable, BaseFuncModel $baseFuncModel, ForumClassTable $forumClassTable, Redis $redis, ThreadMainTable $threadMainTable, AdapterInterface $dbAdapter,ForumUserTable $forumUserTable,FormElementManager $formElementManager, DiyEntityTable $diyEntityTable, DiyEavsTable $diyEavsTable)
    {
        $this->forumMainTable = $forumMainTable;
        $this->userFavoriteTable = $userFavoriteTable;
        $this->baseFuncModel = $baseFuncModel;
        $this->forumClassTable = $forumClassTable;
        $this->redis = $redis;
        $this->threadMainTable = $threadMainTable;
        $this->dbAdapter = $dbAdapter;
        $this->forumUserTable = $forumUserTable;
        $this->formElementManager = $formElementManager;
        $this->diyEntityTable = $diyEntityTable;
        $this->diyEavsTable = $diyEavsTable;

    }
    // onDispatch 目前看来,不需要这个,打算用 JsonModel

    /** 版区 */
    public function indexAction()
    {
        $identityArr = $this->plugin('auth')->getIdentity($this->getRequest());
       //in purpose to keep this part thin, I wrap all the logic into indexforum method.
        $data = $this->forumMainTable->indexforum($identityArr,$this->userFavoriteTable, $this->baseFuncModel, $this->forumClassTable);

        return new JsonModel([
            'status' => true,
            'msg' => 'success',
            'code' => 111,
            'data' => $data,
        ]);
    }

ForumMainTable::indexforum

//版区首页,生成一个list,读取全部的
    public function indexforum($identityArr, UserFavoriteTable $userFavoriteTable, BaseFuncModel $baseFuncModel, ForumClassTable $forumClassTable)
    {
        $data = [];

        $forumMainList = $this->mainListArr($baseFuncModel); //版区原始的一个数组

        $cacheForumList = $baseFuncModel->fsCacheApdater('indexforum','data/cache/forum','forum','600');
        if ($cacheForumList)
        {
            $forumMainSorted = unserialize($cacheForumList); //版区按分类折叠的一个数组
        }
        else
        {
            $forumClassList = $forumClassTable->classListArr($baseFuncModel);
            $forumMainSorted = $this->forumMainSortedByClass($forumMainList,$forumClassList);
            $baseFuncModel->fsCacheApdater('indexforum','data/cache/forum','forum','600','set',serialize($forumMainSorted));
        }

        $data['forumMain'] = $forumMainSorted;

        $data['userFocus'] = $identityArr ? $userFavoriteTable->indexForumMainFocus($identityArr['uid'], $forumMainList, $baseFuncModel) : null;

        return $data;
    }

I placed the userfavorite related logic inside UserFavoriteTable::indexForumMainFocus in natural.

public function userFavoriteByType($uid,BaseFuncModel $baseFuncModel,$type = 1)
    {
        $cache = $baseFuncModel->fsCacheApdater('favorite-uid'.$uid.'-type'.$type,'data/cache/forum-favorite','forum',2592000); //特别的长一个月
        if ($cache)
        {
            return unserialize($cache);
        }
        else
        {
            $columnsArr = ['id','type','uid','entityid','addtime'];
            $userFavoriteList = $this->tableGateway->select(function(Select $select) use ($columnsArr, $type, $uid){
                $predicate = new Predicate();
                $predicate->equalTo('uid', $uid);
                $predicate->and;
                $predicate->equalTo('type', $type);
                $select->columns($columnsArr);
                $select->where($predicate);
                $select->limit(500); //只取500条,太多会有问题
            });

            $userFavoriteList = $baseFuncModel->selectResults2ArrayWithColumnsLimits($userFavoriteList,$columnsArr);
            $baseFuncModel->fsCacheApdater('favorite-uid'.$uid.'-type'.$type,'data/cache/forum-favorite','forum',2592000,'set',serialize($userFavoriteList));

            return $userFavoriteList;
        }
    }

As you will see, if I need to use some other tableModel logic inside UserFavoriteTable::indexForumMainFocus, I need to change a lot. along the chain to do the dependency injection.

If I break the chain(passing di one by one), and parallel then line by line in action, that will make the action seems very fat.

Hope to find a good way to solve this.

I have the exact same problem.

I have a data model with 20 - 30 - 40 different entities. And injecting the model in controllers is not practical, as I would have to create factories for injecting any combination of the many entities…

Have you found any reasonable way to use injection with a hugely complex data model?

I would think this is a problem for everyone out there using laminas with an ORM system like Propel, Laravel og Doctrine?

How do people out there use their ORM systems with laminas and injection? I’d appreciate any advice on this…

30-40 models is not a laminas problem: it’s a design problem.

Most likely, you are missing some sort of query service that goes straight to SQL, or a projection/read model that simplifies what you need.

In addition to that, remember that controllers/actions should likely only translate from/to HTTP, and not do much heavy lifting themselves.