Can I read input from GET inside a Controller Factory? - php

This question is not explicitly about ZF2, but I often take ques from ZF2 for my code. That said, most ZF2 examples I have seen process input inside a Controller Action.
Example:
class YourController extends AbstractActionController
{
public function doStuffAction()
{
// ZF2's way to get input from $_GET variable
$product = $this->getEvent()->getRouteMatch()->getParam('product');
// Process
$processor = (new ProcessorFactory())->getProcessor($product);
$output = $processor->processInput($data);
}
}
Now, I would like to inject a Processor into my Controller. Not create it inside the controller like I am doing above. But since Processor depends on knowing the $product, which is only gotten from $_GET, I do not see any other way.
If I want to inject Processor into Controller, I have to move the line that populates $product variable outside of the Controller as well.
How can I do so without breaking OOP, ZF2, design patterns badly? As in, I am under the impression that anything to do with $_GET is to be done inside a Controller, and not inside a ControllerFactory. Unless perhaps I can break this pattern?

If you just want to apply the Dependency Inversion principle. Applying the D of SOLID acronym, only a few changes are needed.
class YourController
{
/**
* #var ProcessorFactory
*/
protected $processorFactory;
public function __construct(ProcessorFactory $processorFactory)
{
$this->processorFactory = $processorFactory;
}
public function doStuffAction()
{
$product = $this->getEvent()->getRouteMatch()->getParam('product');
$processor = $this->processorFactory->getProcessor($product);
}
}
You could improve by typehinting to an Interface (SOLID)
class YourController
{
/**
* #var ProcessorFactoryInterface
*/
protected $processorFactory;
public function __construct(ProcessorFactoryInterface $processorFactory)
{
$this->processorFactory = $processorFactory;
}
public function doStuffAction()
{
$product = $this->getEvent()->getRouteMatch()->getParam('product');
$processor = $this->processorFactory->getProcessor($product);
}
}
Now, if you want don't want your Controller to be responsible of initiating the creating process (SOLID), you can split it up some more.
class YourController
{
/**
* #var ProcessorInterface
*/
protected $processor;
public function __construct(ProcessorInterface $processor)
{
$this->processor = $processor;
}
public function doStuffAction()
{
$processor = $this->processor;
}
}
class ControllerFactory
{
/**
* #var ProcessorFactory
*/
protected $processorFactory;
public function __construct(ProcessorFactory $processorFactory)
{
$this->processorFactory = $processorFactory;
}
public function create()
{
return new YourController($this->processorFactory->getProcessor());
}
}
class ProcessorFactory
{
/**
* #var RouteMatch
*/
protected $routeMatch;
public function __construct(RouteMatch $routeMatch)
{
$this->routeMatch = $routeMatch;
}
public function getProcessor()
{
$processor = $this->createProcessor();
// do stuff
return $processor;
}
protected function createProcessor()
{
$product = $this->routeMatch->getParam('product');
// create processor
return $processor;
}
}
The following code would get you your controller.
$controllerFactory = new ControllerFactory(new ProcessorFactory(new RouteMatch()));
$yourController = $controllerFactory->create();
Now above code is more general code and not adapted for ZF2. A good move would then to involve the ZF2's servicemanager.
class YourController extends AbstractActionController
{
/**
* #var ProcessorInterface
*/
protected $processor;
public function __construct(ProcessorInterface $processor)
{
$this->processor = $processor;
}
public function doStuffAction()
{
$processor = $this->processor;
}
}
class YourControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllers)
{
$services = $controllers->getServiceLocator();
$processorFactory = $services->get('ProcessorFactory');
return new YourController($processorFactory->getProcessor());
}
}
class ProcessorFactory
{
/**
* #var RouteMatch
*/
protected $routeMatch;
public function __construct(RouteMatch $routeMatch)
{
$this->routeMatch = $routeMatch;
}
public function getProcessor()
{
$processor = $this->createProcessor();
// do stuff
return $processor;
}
protected function createProcessor()
{
$product = $this->routeMatch->getParam('product');
// create processor
return $processor;
}
}
class ProcessorFactoryFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $services)
{
return new ProcessorFactory($services->get('RouteMatch'));
}
}
Above services/controllers and their factories should be registered with their ServiceManager/ControllerManager
$config = [
'controllers' = [
'factories' [
'YourController' => 'YourControllerFactory',
],
],
'service_manager' = [
'factories' [
'ProcessorFactory' => 'ProcessorFactoryFactory',
],
],
];
When a request gets dispatch to YourController, the ControllerManager returns a YourController instance with a Processor injected. Which Processor it gets depends on the request (a parameter inside RouteMatch).

Related

Php override construction error in extends controller mvc structure

In php mvc structure I have this base controller class and add the constructor like this :
namespace App\Core;
/**
* Controller class
*/
class Controller
{
/** #var View View The view object */
public $View;
public $templates;
public $app;
/**
* Construct the (base) controller. This happens when a real controller is constructed, like in
* the constructor of IndexController when it says: parent::__construct();
*/
public function __construct()
{
$this->app = \App\Core\System\App::instance();
$this->Language = new Language('en-gb');
$this->templates = new \League\Plates\Engine(Config::get('PATH_VIEW'));
$this->Url = new \App\Core\Url(Config::get('URL'),Config::get('URL'));
}
public function loadModel($name) {
$path = '\App\Catalog\Model\\'.$name;
$this->model = new $path;
return $this->model;
}
public function loadController($name) {
$path = '\App\Catalog\Controller\\'.$name;
$this->controller = new $path;
return $this->controller;
}
}
Now in action (ie edit account) controller i have :
namespace App\Catalog\Controller\Account;
use App\Core\Config;
use App\Core\Csrf;
use App\Core\Response;
use App\Core\Session;
class EditAccount extends \App\Core\Controller
{
public function __construct()
{
parent::__construct();
//Auth::checkAuthentication();
}
public function index()
{
}
public function action()
{
}
}
Now, I work in PhpStorm and see this override error:
How do can in Fix this error?
Note: If I remove extends \App\Core\Controller from EditAccount class, error fixed But I need to extends \App\Core\Controller.

Proper way of initializing models in MVC by Dependency Injection? How to not break pattern

I am using http://container.thephpleague.com as my Dependency Injection Container.
I am making project in MVC and I am trying to not break MVC pattern.
I am loading Database, Json, etc. classes from DI Container (defined before routing, etc.), but I need models for every controller. Is this right way how to load Models with their own dependencies inside Controller?
Controller:
abstract class Controller
{
protected $di;
protected $db;
protected $latte;
protected $json;
//etc...
public function __construct(League\Container\Container $container)
{
$this->di = $container;
$this->db = $container->get('database');
$this->json = $container->get('json');
//etc...
$this->setup();
}
}
class ApiController extends Controller
{
function setup()
{
$this->model = new ApiModel($this->di);
}
public function showGames()
{
$this->latte->render("random-view.latte", $this->model-getGames());
}
}
Model:
class Model
{
protected $db;
protected $json;
public function __construct(League\Container\Container $container)
{
$this->json = $container->get('json');
$this->db = $container->get('database');
}
}
class ApiModel extends Model
{
/**
* Get games
* #return mixed
*/
public function getGames()
{
//db queries etc...
}
}
Something saying me, that this is not right way.

Slim3 Container good practice?

Hello im learning PHP and i'am Building a REST API with the Slim3 Framework. I Create Routes Like this:
$container['HomeController'] = function () {
return new HomeController();
};
$currentContainer = CurrentContainer::getInstance();
$currentContainer->setContainer($container);
$app->get('/', 'HomeController:index')->setName("index");
My Problem was i had to pass the $container to every Single Controller Class iv'e created, because i need the container context in the Controller for routing etc.
then im build a Singleton Container Class like this:
class CurrentContainer
{
private static $instance;
private $container;
private function __construct()
{
}
private function __clone()
{
}
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new CurrentContainer();
}
return self::$instance;
}
public function setContainer($container)
{
$this->container = $container;
}
/**
* #return mixed
*/
public function getContainer()
{
return $this->container;
}
}
so now its possible to create a "MainController" like this:
class Controller
{
/**
* #var mixed
*/
protected $view;
/**
* #var
*/
protected $router;
public function __construct()
{
$container = CurrentContainer::getInstance()->getContainer();
$this->view = $container->view;
$this->router = $container->router;
}
}
now all of my Controllers extends from the Controller class...
my question is now ... its that a good idea or is there a reason to not do it like that?
im thankful for every input
I've built some APIs with Slim Framework, and also tried so many method to get it done (of course in right way). I implemented MVC pattern on Slim Framework. The code example below:
For the controller, I created a base controller that injected with container. So the code:
<?php
namespace App\Controller;
use Slim\Container;
class Controller
{
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function __get($name)
{
return $this->container->get($name);
}
}
I loaded the base controller on dependencies container.
<?php
// controller
$container['controller'] = function ($c) {
return new App\Controller\Controller($c);
};
So I can get the container from the controller.
<?php
namespace App\Controller;
use App\Controller\Controller;
use Slim\Http\Request;
use Slim\Http\Response;
class HomeController extends Controller
{
public function __invoke(Request $request, Response $response, $args)
{
return $this->renderer->render($response, 'home');
}
}
I hope it helps.

How to create view/renderer for the polymorphic model

I need to render polymorphic models in different situations.
Model's base class:
abstract class BaseDiscount {
abstract public function createRenderer(IDiscountRendererFactory $factory);
}
IDiscountRendererFactory:
interface IDiscountRendererFactory {
/**
* #return IDiscountRenderer
*/
public function createDiscountRenderer(Discount $model);
/**
* #return IDiscountRenderer
*/
public function createActionRenderer(Action $model);
}
Discount model class:
class Discount extends BaseDiscount {
public function createRenderer(IDiscountRendererFactory $factory) {
return $factory->createDiscountRenderer($this);
}
}
Action model class:
class Action extends BaseDiscount {
public function createRenderer(IDiscountRendererFactory $factory) {
return $factory->createActionRenderer($this);
}
}
IDiscountRenderer:
interface IDiscountRenderer {
public function render();
}
In the client module I have:
class ConcreteDiscountRenderer implements IDiscountRenderer {
public function __construct(Discount $model) {
//do something
}
public function render() {
//do something
}
}
class ConcreteActionRenderer implements IDiscountRenderer {
public function __construct(Action $model) {
//do something
}
public function render() {
//do something
}
}
class ConcreateDiscountRendererFactory implements IDiscountRendererFactory {
public function createDiscountRenderer(Discount $model) {
return new ConcreteDiscountRenderer($model);
}
public function createActionRenderer(Action $model) {
return new ConcreteActionRenderer($model);
}
}
$repository = new DiscountRepository();
/** #var BaseDiscount[] $discounts */
$discounts = $repository->findAll();
$factory = new ConcreateDiscountRendererFactory();
foreach ($discounts as $discount) {
$discount->createRenderer();
$renderer = $discount->createRenderer($factory);
$renderer->render();
}
In other application parts may be other implementatations.
I think I got some combination of Visitor and AbstractFactory patterns.
Is it a right approach or is there a better solution?
UPD
If I add new model class, like DiscountProgramm extends BaseDiscout, I have to refactor IDiscountFactoryInterface and all it's realizations. Is there an approach which allows to avoid that?

Laravel: how to organize BaseContoller

I'm going to have a method in my BaseController to detect the city that user is from.
What is the best way to do that? I also want to make use of Automatic Resolution and pass to BaseController's constuctor some kind of LocationService.
<?php
use Andrew\Services\LocationService;
class BaseController extends Controller {
protected $locationService;
public function __construct( LocationService $locationService )
{
$this->locationService = $locationService;
$this->detectLocation();
}
/**
* Setup the layout used by the controller.
*
* #return void
*/
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$this->layout = View::make($this->layout);
}
}
public function detectLocation()
{
$this->locationService->detect();
}
}
If I take this approach then I'll have to pass an instance of LocationService from every sub-controller to parent's cunstructor
// Sub-controller
public function __construct( CalculatorValidator $validator, TotalCostService $totalCost)
{
parent::__construct(new LocationService);
$this->validator = $validator;
$this->totalCost = $totalCost;
}
Is it appropriate way to accomplish what I need? Doesn't it seems like a lot of work?

Categories