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

  • Isn't what you're describing the exact purpose of the $data argument ? magento.stackexchange.com/a/118040/2380 – Raphael at Digital Pianism Sep 26 '16 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? – Fabian Schmengler Sep 26 '16 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". – Raphael at Digital Pianism Sep 26 '16 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? – Fabian Schmengler Sep 26 '16 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 '16 at 13:49
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']);
  • 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. – Fabian Schmengler Sep 26 '16 at 14:01
  • @fschmengler what about creating a new class that extends A so you can change the constructor ? – Raphael at Digital Pianism Sep 26 '16 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. – Fabian Schmengler Sep 26 '16 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 – Raphael at Digital Pianism Sep 26 '16 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, privacy policy and cookie policy

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