rdlowrey / auryn

IoC Dependency Injector
MIT License
722 stars 65 forks source link

Multiple prepare calls #135

Closed designermonkey closed 1 year ago

designermonkey commented 8 years ago

I know this may not be the desired functionality, but here goes anyway.

I use a set of classes to define stuff in the injector at route call runtime, so it is only ever defined when it's needed. I thought I would be able to use multiple calls to certain auryn functions to build up a list of processes, for example prepare.

The main app would use prepare to set up the core parts of a class, and then each route would add it's requirements to the prepare 'queue'. Obviously this doesn't work currently, and I am basically suggesting it as a great piece of functionality to include.

Any thoughts on this?

Danack commented 8 years ago

I use a set of classes to define stuff in the injector at route call runtime, so it is only ever defined when it's needed.

That sounds fine.

I thought I would be able to use multiple calls to certain auryn functions to build up a list of processes, for example prepare.

That sounds ......less fine.

Are you able to share the code where you're trying to do this? I think possibly you're using the wrong approach, and that there is likely to be a better way of doing what you want.

designermonkey commented 8 years ago

Basically, going from our discussion on #134, I'm trying to dynamically stick repository classes inside a collection based on the requirements of the route callable.

So, I always need comments and pages for example, but not products, so I only want to add products when a product route is triggered. Using middleware, this works really well, and the app is only half configured before the router is triggered.

Because I have to define the requirements for each repository, and I am defining those requirements in a route specific middleware class, I run those specific middleware classes in a list, so for products, I would run (for example) Comments\InjectorDefinitions and Pages\InjectorDefinitions in the middleware list for that route. This means that I can't prepare each class more than once, so one of the middlewares overrules the previous, and the app isn't defined properly.

I hope this makes sense? Code is not really available as this is a huge project and is abstracted out quite a lot (I wouldn't know where to start with examples).

Basically, the only way I can see round it is to use duplication in the prepare calls in each middleware, or add the most common uses to a global define call, both of which don't feel right.

I guess I just think that being able to queue up calls to callbacks for the prepares method would allow more than one entry point to prepare classes.

Danack commented 8 years ago

I'm trying to dynamically stick repository classes inside a collection based on the requirements of the route callable. .. So, I always need comments and pages for example, but not products, so I only want to add products when a product route is triggered.

I probably ought to write this out more clearly as a blog post, but it sounds very much like:

i) You ought to be using composition to wire stuff up rather than wiring stuff up by hand.

ii) You might need to be using 'context' objects rather than having anonymous (or at least, less semantically meaningful) generic collections.

It's not clear from the description of your problem if in the place in your code where you want to access those items, whether you know whether all 3 should be available or not. This code assumes that you can:

class CommentsAndPagesContext {
    public $comments;
    public $pages;
    public function __construct(Comments $comments, Pages $pages) {
        $this->comments = $comments;
        $this->pages = $pages;
    }
}

class CommentsPagesAndProductContext {

    public $comments;
    public $pages;
    public $product;

    public function __construct(Comments $comments, Pages $pages, Product $product) {
        $this->comments = $comments;
        $this->pages = $pages;
        $this->product = $product;
    }
}

// In your bootstrap (or whenever)
$injector->delegate(Comments::class, 'createComments');
$injector->delegate(Pages::class, 'createPages');

// In your controller
function fooController(Injector $injector) {
    $product = findProducts();
    $injector->share($products);
    //..other stuff.
}

And then use either CommentsAndPagesContext or CommentsPagesAndProductContext as appropriate.

If that doesn't apply to your code, feel free to clarify what you need.

And yes....this is probably another example where multiple dispatch is needed....hence https://github.com/danack/tier

elazar commented 4 years ago

One thing the present behavior does hamper is the ability to package Injector configuration into separate reusable units.

For example, if I have a central application class that can receive PSR-15 middleware, and I want to make each of two other classes add a middleware to the application class instance after it's created, I'm currently unable to do so using prepare() because whichever class makes its prepare() call last ends up being the one that "wins."

Danack commented 1 year ago

I added some more words about advanced patterns in the readme which probably suggests solutions to this type of problem.