0

I'm working on my PHP (H)MVC project in which I separated the views from the controllers - as in the answer How should a model be structured in MVC?. Their relationship is 1:1, so they have the same "actions". Therefore, in bootstrap.php, after I instantiate them I call:

// ... Controller and view are already instantiated.
call_user_func_array(array($controller, $actionName), $actionParameters);
call_user_func_array(array($view, $actionName), $actionParameters);

Let's say that a controller and a view become, each of them, a model (domain object) as constructor parameter. Using Auryn dependency injection container, I try to share the same instance of the model between the controller and the view, without instantiating it beforehand. E.g. before controller and view instantiation takes place in bootstrap.php.

In his answer, tereško describes the use of a model/service factory. But as a "Note" he says:

...A much better implementation would have the DI container (like Auryn) to create controllers and views, with only the required services, instead of using a factory.

My question is: Can I implement this functionality without a model factory using the dependency injection container? I'm kind of stuck in this task and I don't really know if this is somehow possible. Thank you.

1 Answers1

1

Yes, you can.

But it's kinda fiddly. You basically need to set up the service as "shared":

<?php
$injector->define('MailerService', [
    ':server' => 'fak.it',
    ':port' => '443',
]);
$injector->share('MailerService');

$controller = $injector->make('FooBarController');

This assumes that you controller was defined kinda like this:

<?php
class FooBarController 
{
    public function __construct(MailerService $service) 
    {
        // ...
    }
}

In this aspect the Symfony's standalone DI component is a bit easier to use, because you can put this kinda of configuration in a json or yaml file.

P.S. You probably should abstract your user input as some sort of Request object ant pass that in your controller on each method-call.

Kinda like this:

<?php
$request = new Request( .. something here maybe .. );
$controller->action($request);

Make for a bit nicer looking code :)

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Thanks, @tereško. Your solution is the one! I tested it, it's working very good. As for **Auryn**-injecting **other** objects (like `Request`) into methods, **along** with the action-parameters passed by routing: it's not as simple as it appears to be - if not impossible in the context of automatic calling the C/V-actions and of absence of route definitions. I tooked my project a step further with a view-models layer (MVVM+C) and I'll share all my experiences with it, somehow, on my board. PS: Your presentations on MVC principles make all MVC-developers better ones ;-) –  May 12 '17 at 15:11
  • You misunderstood a bit. There is only point in passing exact action parameters **only**, if you are using a "dispatching router" (like [FastRoute](https://github.com/nikic/FastRoute), where you can precisely define which parameters and in what order are being passed. Otherwise you loose all control over the order, in which the parameters are handed over. (cont.) – tereško May 12 '17 at 15:20
  • If you are **not using** a "dispatching router" (like [Symfony's routing component](http://symfony.com/doc/current/components/routing.html)), it is a lot better to pass an abstraction in form of `Request` instance. And then use it inside your controller's method to call `$request->get('parameter_name')`. – tereško May 12 '17 at 15:22
  • To @tereško, for completion: A GET-request has by me the form: `Module/Controller/action/[arg1]/.../[argN]?oper=read&page=7&...` The 'action' accepts `arg1,...,argN` as arguments and reads `oper` and `page` values from `Request`, which is - until now at least - injected in the C/V-constructor. –  May 12 '17 at 15:39
  • I see. So, @tereško, assuming the absence of a routing dispatcher, are you suggesting to pass **JUST** a `Request` instance as action argument, without passing along the url values too? Like: **`search(Request $request)`** instead of **`search(Request $request, $arg1, $arg2)`** and of **`search($arg1, $arg2)`**. Right? –  May 12 '17 at 16:20
  • Yes, because you can just read those parameters from the `Request` instance. – tereško May 12 '17 at 16:21
  • Of course passing the parameters in the precise variables, like `getUsers($group, $page)` is a lot cleaner way, **but** then you need an ability to map your routed values directly to those parameters (which is where the routing-dispatcher shines). But that again comes with other issues. Alternative is the approach that full symfony framework (not the independent components) does - it calls controller's methods via "AppKernel", that uses reflections to know what routing values map to which parameters, but that's a really "bulky solution". – tereško May 12 '17 at 16:27
  • Great, @teresko. Exactly this type of solution, resp. of answer I searched for! Thank you. –  May 12 '17 at 16:34