Code Igniter get name of model instance that created object? - php

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

Related

Parent and child classes not referencing correctly within a function PHP

I am building an MVC component and I'm getting stuck with an issue with a parent and child model. I have a few methods in the parent Model and they're not working with the database_class object
the constructor works fine
but when I use that object in the methods its like the constructor doesn't exist?
Class Controlller
{
public function __construct()
{
$this->childModel = $this->model('childModel');
} // end construct
// methods go here
}
Here are the models:
class childModel extends parentModel {
private $dbo;
public function __construct()
{
$dbobj = new Database_class;
$this->dbo = $dbobj;
}
//methods
}
class parentModel {
private $dbom;
public function __construct()
{
$dbombj = new Database_class;
$this->dbom = $dbombj;
var_dump($this->dbom); //working perfectly as database object
}
public function methodName()
{
var_dump($this->dbom); //not showing up as database object
}
}
I don't think this code is doing what you think it's doing. In childModel, you are overwriting the __construct method of the parentModel, so the __construct in the parentModel never gets called. Therefore $this->dbom should be null. Furthermore if you wish to use $this->dbom from the childModel, you should probably change the scope from private $dbom to protected $dbom. See this page for more info on that: http://php.net/manual/en/language.oop5.visibility.php

PHP MVC: How to use private Model object stored inside View from outside class

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).

Call a class method as soon as an object it extends is created

Assume we have the following class (simplified):
class SuperConfig {
public $mainDir;
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
This class is supposed to be extended in EVERY other class in the project, and I do need the setDir() function of the parent to be executed. Obviously, I could do it like this:
class A extends SuperConfig() {
public function __construct() {
parent::setDir();
}
// ... other stuff is about to be done ...
}
and I could access the properties in the child class like this:
class A extends SuperConfig {
public function doSomething() {
SuperConfig::mainDir;
}
}
This is a viable solution, but I got multiple hundreds of classes and doing this in every single one seems tedious. So, is there a way to do something like this:
class SuperConfig {
public $mainDir;
public function __extend() {
$this->setDir();
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
__extend() obviously doesn't work like that, but I'm wondering is there is a trick how I could make this work.
class SuperConfig {
public $mainDir;
public function __construct() {
$this->setDir(); // consider moving setDir's code here as well,
// unless you have a good reason for it to be a method
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
You simply do this, and then you expect all subclasses to call the parent constructor if they're overriding the constructor:
public function __construct() {
parent::__construct();
// more code
}
It's perfectly reasonable to expect children to call their parent constructor, unless they deliberately want to leave the instance in an unknown and potentially broken state.
Put the constructor in the class that is being extended.
class SuperConfig {
public $mainDir;
public function __construct() {
$this->setDir();
}
public function setDir() {
$this->mainDir = "path/to/dir";
}
}
In any class that extends SuperConfig, if they have a constructor also, be sure to include parent::__construct(); so that setDir is called.
Read deceze's answer for an actual solution to your problem.
I would like to point out tho, that you should not extend every class in your project from a Config class. There are several ways how you could improve that.
1.) Create a static config class which you simply can call everywhere without the need of creation
class SuperConfig {
protected static $mainDir = null;
public static function setMainDir($dir) {
self::$mainDir = $dir;
}
}
2.) Create a trait rather then a parenting class.
trait SuperConfig {
protected $mainDir = null;
public function setMainDir($dir) {
$this->mainDir = $dir;
}
}
which you then can use inside your classes:
class XYZ {
use SuperConfig;
public function doSomething() {
$this->setMainDir('path/to/your/dir/');
}
}
Note that you can do that in the constructor too (which is kinda what you want).
Im not saying those two solutions are the best, but I dont like the thought of extending all classes from a config class. Just does not make much sence. Just imagine that you can only extend from one class per time, while you can use as many traits as you wish (and also have as many static classes as you need).
Well, in this particular case you just need:
class SuperConfig {
public $mainDir = "path/to/dir";
}

OO, MVC and Observer pattern not working as expected

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.

PHP MVC question

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.

Categories