2

Is there a way to pass constructor arguments for dependencies when using the automatically generated factory classes?

I have an object structure that I would instantiate like this without the object manager:

new A(
  new AA(
    new AAA(
      'xyz'         <-- value determined at runtime
    )
  ),
  new AB(
    new ABA(),
    new ABB()
    [1,2,3]         <-- value determined at runtime
  ),
  new AC(
    ...
  )
);

Now I could replace this with a mix of factories and constructor injected arguments1, but I'm wondering if there is another way, something like this:

$aFactory->create(
  [
    AAA::class => ['param1' => 'xyz'],
    AB::class => ['param3' =>  [1,2,3]],
  ]
);

Alternatively it would also be useful if I could define a constructor argument to be fetched from a certain method with the type definition in di.xml.

Is there anything like this?


1) like this

$aFactory->create(
  [
    'aa' => $aaFactory->create(
      [
        'aaa' => $aaaFactor->create(
          [
            'param1' => 'xyz'
          ]
        )
      ]
    ),
    'ab' => $abFactory->create(
      [
        'param3' => [1,2,3]
      ]
    )
  ]
);

But what I'm trying to avoid is having to depend on all these factories

5
  • Isn't what you're describing the exact purpose of the $data argument ? magento.stackexchange.com/a/118040/2380 Sep 26, 2016 at 13:32
  • @RaphaelatDigitalPianism the data argument adds arguments to the class to be created, but not for its dependencies, or am I missing something? Sep 26, 2016 at 13:33
  • It does not add arguments, it populates data from an array. But it definitely not deals with the existing dependencies from the constructor. My point was more "why not use the $data array instead of injecting class dependencies". Sep 26, 2016 at 13:36
  • I updated my question with code how I would do it with factories and data arguments, is it that what you mean? Sep 26, 2016 at 13:45
  • 1
    if you don't want that many direct dependencies, you can create one big factory that depends on the other factories. And you will depend on that big one only. Now I'm really curious about what you are building.
    – Marius
    Sep 26, 2016 at 13:49

2 Answers 2

1

I'm not entirely sure about what you're trying to achieve but I think you could do something like this:

$aFactory->create(['param1' => 'xyz', 'param3' => [1,2,3]]);

Then in the constructor of $aFactory you can do:

$this->_aa = $aaaFactory->create($data['param1']);
$this->_ab = $abFactory->create($data['param3']);
4
  • In theory, yes. But to make it more complicated, A is not part of the Magento module. So I need a way without changing these classes. Sep 26, 2016 at 14:01
  • @fschmengler what about creating a new class that extends A so you can change the constructor ? Sep 26, 2016 at 14:02
  • It's also final :D besides, I don't like that solution, then I'd rather use the factories. But I just realized that I probably don't need to change these classes for your suggestion, just create my own factories instead of the autogenerated ones. Sep 26, 2016 at 14:15
  • @fschmengler "it's also final" damn yeah out of ideas here. My solution is definitely not the most elegant I agree but your problem doesn't sound that complex, refactoring the factories (see what I did here?) could definitely lead to a more elegant solution Sep 26, 2016 at 14:16
0

Since what I tried is not possible, I ended up with a mix of preference definitions, generated factories and custom factories and decided case by case what makes sense.

  1. I replaced the outermost instantiation with a factory. AFactory is the autogenerated factory for A (autogeneration of factories works for external libraries just as well as for classes of the module)

    $this->aFactory->create([
        ...
    ]);
    
  2. For all constructor parameters of A without runtime configuration (ex. AC) I defined preferences in di.xml to pass the right objects.

  3. For object structures that I want to be able to replace as a whole, I created custom factories, with an interface and defined them as preference. Ex. AA:

    interface AAInterrfaceFactory
    {
        /** @return AAInterface */
        public function create();
    }
    
    class AAFactory implements AAFactory
    {
        public function create()
        {
            return new AA(
                new AAA(
                  'xyz'         <-- value determined at runtime
                )
           );
        }
    }
    
  4. For objects from the library that could not be replaced anyways (ex. AB), I stick with "new". If possible, parameters are injected into the class where the whole creation happens.

So in the end the instantiation can look like this:

return $this->aFactory->create(
    [
        'aa' => $this->aaFactory->create(),
        'ab' => new AB(
            $this->aba,
            $this->abb,
            [1, 2, 3]
        )
    ]
);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.