Given a Controller class and a View class, is it better for the controller to directly assign values to view properties or, is it better to assign values to properties in the controller and then copy those properties to the view when ready to display it?
Example Model Class
class Model
{
public $propertyA;
public $propertyB;
}
Example Controller class:
class Controller
{
protected $view;
protected $model;
public function __construct()
{
$this->model = new Model();
$this->view = new View();
$this->prepareData();
$this->initView();
}
protected function prepareData()
{
$this->model->propertyA = 'This is property A.';
$this->model->propertyB = 'This is property B.';
}
protected function initView()
{
$this->view->model = $this->model;
$this->view->display();
}
}
Example View class:
class View
{
public $model;
public function display()
{
echo "propertyA = $this->model->propertyA";
echo "propertyB = $this->model->propertyB";
}
}
Sorry, I was tired. I do use a model, so please reconsider your answers with this in mind.
The data should only be in one place. If not when things get complicated it is hard to sync the different places you have the data. In MVC you have a model and that is where the data should be. Pass the Model into the View and have the view display that.
Here is a simple explanation: http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller or here for those that do not like Wikipedia: http://ootips.org/mvc-pattern.html
The model can be as simple as a class with the properties in it.
The view shouldn't be setting up variables unless they are related to the presentation. It's best to put static variables in a config file anyway.
copy those properties to the view
Rather than setting variables in the view why don't you just construct the view with a reference to the controller. That should save you from writing a lot of boiler plate code.
Class Controller() {
$this->something = 'abc';
function __construct() {
$this->display();
}
function display() {
$this->view = new View($this);
}
}
Class View() {
function View(&$controller) {
$this->controller = $controller;
print $this->controller->something;
}
}
Edit: I like Romain Hippeau's answer a lot more than my own. You should pass the model into the view.
Related
I am new in learning MVC, I want to use private Model object stored inside View from outside class, like below example:
class Model{
private $data
}
class View{
private $model
public function __construct($model) {
$this->model = $model;
}
}
// outside
$m = New Model;
$v = New View($m);
echo $v->m->data; // How to get it
i know setter/getter method, but it can can much more bigger MVC code.please help.
You would probably want to access the view from within the controller like this:
class Controller
{
public function __construct($model, $view)
{
$this->model = $model;
$this->view = $view;
}
public function show()
{
return $this->view->render($this->model->getData());
}
}
$controller = new Controller();
$controller->show();
You want the controller to receive all of the dependencies that it has ideally in the constructor. That way it doesn't need to search for them. This is inversion of control or DI (dependency injection).
I have to different classes in my Slim PHP framework, named OrderController & AddressController. I want to access some function of AddressController inside OrderController to reduce code redundancy.
But can't get a way to do it, I got how to do it in pure PHP setup, but how to do it in Slim PHP framework?
The PHP way to do this is as follows:
class A {
private $xxx;
public function __construct() {
$this->xxx = 'Hello';
}
public function getXXX() {
return $this->xxx;
}
}
class B {
private $a;
public function __construct(A $a) {
$this->a = $a;
}
function getXXXOfA() {
return $this->a->getXXX();
}
}
$a = new A();
$b = new B($a);
$b->getXXXOfA();
How to achieve this dependancy injection in Slim?
Slim PHP Framework
Note: I am using Slim PHP v3
2 solutions come into mind:
-1-
You could also try to have the common functionality in a separate Trait.
-2-
I won't do the
new SecondController($container)
inside the constructor of the FirstController unless you need it at every controller-hit.
I like lazy loading, so it will load only when needed.
If your AddressController and OrderController has same parent class, than move these methods to parent:
class AddressContoller extends Controller {
public function test() {
$this->methodFromParent();
}
}
If not, create new object of that class and call method. Method must be public
class AddressContoller extends Controller {
public function test() {
$order = new OrderController();
$order->publicMethodInOrderClass();
}
}
If your OrderController wants to call a method foo from AccessController, you should think about moving foo somewhere else. That's an good indicator for wrong SRP
There are two possibilities
foo belongs to/is relevant for every Controller and has something to do with controlling: Just move it to the parent class.
foo is relevant to only a few classes: Move it to the class, it belongs to. This could be an helper class, some domain model class, or something else. Maybe you have to intruduce a new class to do this.
After a lot of reseach I finally manage to get a solution! Posting it here so if anyone in future might get help from it:
class FirstController
{
protected $container;
protected $db;
protected $view;
protected $second;
// constructor receives container instance
public function __construct(\Interop\Container\ContainerInterface $container) {
$this->second = new SecondController($container);
$this->container = $container;
$this->db = $this->container->db;
$this->view = $this->container->view;
}
public function LocalFunction(){
$this->second->otherFunction();
//call the functions in other classes as above
}
}
In my class, we made a simple application using MVC with the observer pattern in Java and it works. The view cannot call any methods from the model that are not included in the (Observable) interface and vice versa.
I am quite a fan of PHP and decided to make the same (simplified) example in PHP. I noticed that even though I am using an interface and passing the reference of the model as an interface, the view can still call every method inside the model, rendering the entire pattern useless.
Is there something I overlooked or is this not possible in PHP?
The PHP code (every reference, method, etc is the exact same as in the Java application) :
class App
{
public function __construct()
{
$model = new Model();
$controller = new Controller($model);
}
}
class Model implements Observable
{
private $view;
private $count = 1;
public function __construct()
{
echo 'Model created. <br>';
}
public function registrate(Observer $view)
{
$this->view = $view;
echo 'Model: view is registered. <br>';
}
public function addOne()
{
$this->count += 1;
$this->view->modelChanged($this);
}
public function getCounter()
{
return $this->count;
}
public function getMessage()
{
return 'The view should not be able to call this method.';
}
}
class Controller
{
private $view;
private $model;
public function __construct(Model $model)
{
echo 'Controller created. <br>';
$this->model = $model;
$this->view = new View($this->model);
$this->model->addOne();
}
}
class View implements Observer
{
public function __construct(Observable $model)
{
echo 'View created. <br>';
$model->registrate($this);
}
public function modelChanged(Observable $model)
{
// Should only be able to call method "getCounter()"
echo $model->getMessage();
}
}
interface Observable
{
public function registrate(Observer $view);
public function getCounter();
}
interface Observer
{
public function modelChanged(Observable $model);
}
The output, if you run this is:
Model created.
Controller created.
View created.
Model: view is registered.
The view should not be able to call this method. As you can see, the view can call a method of the model that is not declared inside the Observable interface.
How is this possible and why does this not work in PHP like it does in Java?
Well of course the view can call every method you've defined on the model: All the methods are public, which means they're callable from anywhere. Just define them as protected or private instead...
Of course, that'd limit the ways in which you can use the model in other components (such as the controller). To get around that problem, a simple fix would be to create a wrapper, which you can wrap around the model when you pass it to the view:
class View implements Observable
{
public function __construct(ViewObservable $model)
{
//do stuff here
}
}
//Wrapper:
class ViewObservable
{
/**
* #var Model
*/
protected $payload = null;
public class __construct(Observable $model)
{
$this->payload = $model;
}
public function getCounter()
{
return $this->payload->getCounter();
}
}
But really, you might want to rethink a thing or 2. It's good to use interfaces, but it doesn't make a lot of sense (to me at least) to have all components in an MVC architecture implement the same interface. All components have different jobs to perform, and therefore should have different interface requirements.
I am trying to make the jump towards object orientated php. I'm building my own MVC framework from the ground up because I like to have full control and I figure it's a good learning exercise to do.
This is my router which I use to route to different folders according to the name (I have an autoloader function in the index file):
class controller_router
{
public $controller;
public $action;
public $id;
public $freetext;
//set the url path variables
public function __construct($controller,$action,$id,$freetext)
{
$this->controller = $controller;
$this->action = $action;
$this->id = $id;
$this->freetext = $freetext;
}
//instantiate the main controller according to url
public function instantiateMainController()
{
$controller_name = 'controller_'.$this->controller;
$controller = new $controller_name;
}
}
My blog controller looks like this (just testing to see if I can get the action):
class controller_blog
{
public function __construct()
{
echo $this->action;
}
}
My question is - how do I get the action variable from the parent (the router)?
The command echo $this->action; does not appear to work!
Also, any other improvements you could suggest? I am a newbie!
You should pass your object to new object.
$controller = new $controller_name($this);
//...
class controller_blog
{
public function __construct($router)
{
echo $router->action;
}
}
There are several ways to do that in code - but first you need to do that in mind. That means - you need to realize, what is the structure of your application. What structure has each entity? How each entity is implemented in class? How entities are related and how that is implemented in code?
For example, one of options can be inherit your action from parent class. But first you'll need to make your controller_blog child of controller_router. This may have sense - but are they related in such way - is up to you to decide. You can do this with:
class controller_blog extends controller_router
{
public function __construct()
{
echo $this->action;
}
}
-so you'll have one class that will extends another.
But may be they're not related such way? Then you'll need to inject dependency into your blog controller. For example
class controller_blog
{
public $action;
public function __construct(controller_router $router)
{
$this->action = $router->action;
}
}
-since you've not specified your structure, you'll need to decide what is more correct by yourself.
Pass variables from the controller_router to the controller_blog as arguments of the constructor of controller_blog.
I am using CodeIgniter and have extended CI_Model. so all my models now extend MY_Model.
This works fine.
Issue is that all my models have a secondary associated object. basically a class that gets passed data from the model (usually from the database) and represents that row in the database.
so something like
class Product_Model extends MY_Model{
public function get($id){
//....
return new Product($query->row());
}
}
class Product{
public function __construct(stdClass $data){
//....
self::$ci =& get_instance();
self::$model = self::$ci->products;
}
}
Now I load the Product_Model with an alias $this->load->model('product_model', 'products');
Hence having self::$model = self::$ci->products;
But now I want to have a basic class that all the classes like Product will extend.
I want this to contain the logic to update self::$model.
But I need to know the models alias.
Something like
self::$model = self::$ci->{instantiator_variable_name($this)} which would be self::$model = self::$ci->products
Now obviously that function does not exist but it shows what I want to do.
I know I could for everywhere that I create the Product or similar have
$row = $query->row();
$row->model = $this->ci->products;
return new Product($row);
But I would rather automate it if I could.
It might help if you clarify the situation a bit. Post a bit more of your code please?
For example, Modals (in CodeIgniter) are generally used as singleton classes which (almost) explains using 'self::" but it looks like you want Product to be an Object. So why does that use
self::$model
instead of
$this->model
The fact that you're aliasing the products model makes me think you might be doing this on purpose (which is why I'm confused, why would you do this?). I think you should review the difference between "self::", "static::", and "$this->". Take a look at http://php.net/manual/en/language.oop5.late-static-bindings.php
rockstarz is correct, you need to use the Factory Pattern. Consider something like this:
class ItemFactory {
private $model;
public function __construct($model) {
$this->model = $model;
}
function create_product(stdClass $data) {
$product = new Product($data);
$product->set_model($this->model);
return $product
}
}
abstract class Item {
protected $model;
protected $ci = & get_instance();
public function __construct(stdClass $data) {
// whatever
}
public function set_model($model) {
$this->$model = $model;
}
public function get_model() {
return $this->model;
}
}
class Product extends Item {
// whatever
}
Then your model can just use it like
class Product_Model extends MY_Model {
private $item_factory;
public function __construct() {
$this->item_factory = new ItemFactory($this);
}
public function get($id){
return $this->item_factory->create_product($row);
}
}
Relevant reading materials:
http://en.wikipedia.org/wiki/Inversion_of_control#Implementation_techniques
http://en.wikipedia.org/wiki/Factory_method_pattern
http://en.wikipedia.org/wiki/Dependency_injection