2

Intro

I'm developing an MVC framework, and I've run into a problem. It seems what I was trying to accomplish is known as the Singleton Design method -- initializing classes only once. Remember that I'm trying to put as less code in the controller "acontroller" as possible.

With that said, a final question remains: how can I add objects to an object that has already been instantialized?

It may help to have or at least see actual source instead of just example source, so I have pushed my source to my github. You can find that here: https://github.com/derekmaciel/uMVC


Code explanation

What's happening "under the hood" is first,

  • The Controller class loads a controller located in /application/controller, in this case "acontroller".
  • After, the acontroller class loads a model (called "amodel") using the Load class, using $this->load->model("amodel"), which was instantialized in the Controller __construct.
  • The final outcome of $this->load->model("amodel") is: $controller->amodel =& new Amodel(), where $controller is the Controller instance (not acontroller, because the controller loading the model will vary).
  • Step 4: Allow acontroller access to models that were loaded (amodel).

Code result

A copy of the current output of these scripts can be found here: http://pastebin.com/EJxuXaki

The first thing you'll notice is that I'm given a warning for using a deprecated assignment. I'm going to focus on the error for now.

The second thing you'll notice is that I first print_r()'d the Controller instance. Inside there is an amodel object, which is want to add to acontroller.

After that, I print_r()'d the $this (acontroller) object. It has everything it got from __construct(), but not amodel.

If I can get acontroller to "see" amodel, then my problem will be solved.

Also: Is there anyway for me to remove "parent::init()" from the controller acontroller? I only did that so acontroller could have access to both the Load and Model class, but I'm trying to put as less code as possible in acontroller, so having the acontroller have access to Load and Model automatically would help a lot.

I hope I was clear. Thanks for any help

Derek Maciel
  • 1,275
  • 2
  • 12
  • 19
  • 2
    Your design is broken. There is no reason for a Controller to be a Singleton. In fact, [there is no reason to use Singletons in PHP at all](http://gooh.posterous.com/singletons-in-php). [And you should not use the `global` keyword either.](http://stackoverflow.com/questions/5166087/php-global-in-functions/5166527#5166527) – Gordon Mar 09 '11 at 21:10
  • @Gordon The Singleton design is the only way I can think of to do this, as the alternative is dependancy injection; my goal is to avoid putting "system" code in my application, and only allow the Core to do that. I need to find a way to allow my application access to the same stuff that my Core can. – Derek Maciel Mar 09 '11 at 21:24
  • 1
    Weeeell, I guess your framework is first and foremost meant as a learning exercise, so go ahead and do these mistakes :) Nothing wrong with realizing it's broken later. – Gordon Mar 09 '11 at 21:30

3 Answers3

2

I personally do not think that singleton methods belong within an MVC Framework, the reason for this is because the main objects that are loaded are Models,Libraries and controllers, everything else such as the Router is usually hard coded.

The structure that i would do is create the following classes:

  • ModelLoader
  • LibraryLoader

and have them included during system boot, then within your main controller do the following:

class Controller
{
    public $library;
    public $model;

    public function __construct()
    {
        $this->library = new LibraryLoader();
        $this->model = new ModelLoader();
    }
}

this would expose the 2 loaders to the child controller, your model/library should hold a private array storing the loaded objects, a little something like this:

class LibraryLoader extends ObjectLoader
{
     protected $_path = "/app/library/";
     protected $_ext = '.php';
}

class ModelLoader extends ObjectLoader
{
     protected $_path = "/app/models/";
     protected $_ext = '.php';
}

the object loader would look like so:

class ObjectLoader
{
     protected $_path = "/app/";
     protected $_ext = '.php';

     public function __get($item)
     {
          /*
                * Load item here, the paths above would be overwritten
                * store the object in an array, make sure you check if its already loaded
           */
     }
}

this is pretty basic, but within your child controllers such as index / home etc you can do the following:

class indexController extends Controller
{
    public function index()
    {
        $this->model->users->getUser(22);
        $this->library->security->validateInput("get","key");

        //As the objectLoader manages whats loaded, any further calls to the above would
        //use the same objects initiated as above.
    }
}

This should get you started, its more streamline them using the singleton approach.

RobertPitt
  • 56,863
  • 21
  • 114
  • 161
0

I guess you need to include Model.php in your controller.php to be able to use model class.

include 'Model.php';
include 'Load.php';
  • Model.php and Load.php works fine in Controller class, but not in acontroller. I need to find some way to be able to pass it to acontroller silently (without extra code in acontroller) – Derek Maciel Mar 09 '11 at 21:23
0

Since PHP 5.3 you can use the static keyword to instantiate a class

abstract class singleton
{
    /**
     * Holds an insance of self
     * @var $instance
     */
    protected static $instance = NULL;

    /**
     * Prevent direct object creation
     */
    final private function  __construct() { }

    /**
     * Prevent object cloning
     */
    final private function  __clone() { }

    final public static function getInstance()
    {
        if(null !== static::$instance){
            return static::$instance;
        }
        static::$instance = new static();
        return static::$instance;
    }
}

class myclass extends singleton
{
}

$myclass = myclass::getInstance();
Jon Skarpeteig
  • 4,118
  • 7
  • 34
  • 53
  • The problem isn't that I need to instantiate classes, I need to "add" an object to a class that has already been instantiated. Acontroller was loaded by Controller, but then Acontroller needed to load Amodel. I am able to load Amodel to Controller but I need it to be accessible by Acontroller – Derek Maciel Mar 09 '11 at 21:32