4
\$\begingroup\$

I have a very simple PHP MVC library for my auto-didactic pedagogical purposes. I'm having a little trouble figuring out how to add in the observer patterns as show in this UML diagram from Wikipedia.

model is simply contains an array and acts as a key-value store. contrlr accepts input, ostensibly from $_POST, to update a model.

So then the observation relationships between the three are where my understanding breaks down. I have contrlr implementing SplOberserver and SplSubject, but how exactly should I handle communication between them? Concatenate strings to strHtml?

Please note that I want to have these classes conform to the UML diagram linked from Wikipedia above.

class model {

        private $arrValues = array();

        public function __get($var) {
                return $this->arrValues[$var];
        }

        public function __set($var, $val) {
                $this->arrValues[$var] = $val;
        }

}

class contrlr implements SplOberver, SplSubject {

        private $objModel;

        public function __construct( model $objModel ) {
                $this->objModel = $objModel;
        }

        public function parsePost( array $arrPost ) {
                foreach ( $arrPost as $key => $value ) {
                        $this->objModel->$key = $value;
                }
        }

        public function update() {
        }

        public function attach() {
        }

        public function detach() {
        }

}

class view implements SplObserver {

    private $strHtml;

    public function getHtml() {
        return $this->strHtml;
    }
}
\$\endgroup\$
  • \$\begingroup\$ @Abbas I've rolled back you last edit, see this meta \$\endgroup\$ – Ludisposed Dec 15 '17 at 9:54
  • \$\begingroup\$ @Ludisposed ok, I understand, I (mistakenly) thought I was helping :) \$\endgroup\$ – Abbas Dec 15 '17 at 10:07
  • 1
    \$\begingroup\$ @Abbas No problem, I understand the good intentions, it's just that that can be aprt of the review :) \$\endgroup\$ – Ludisposed Dec 15 '17 at 10:10
1
\$\begingroup\$

Unlike other SPL interfaces, I'm not finding that this pair of built-in interfaces has any intrinsic value other than to provide a template. It would be nice if Observer can automatically trigger its update() method any time Subject properties are modified in any way, but this isn't the case. You have to set it up manually.

So to start, you want to separate contrlr into separate SplSubject and SplObserver classes. Otherwise, for all intents and purposes, it's only notifying itself. model can actually be the one that implements SplObserver in your case. But really, which class implements which interface would be up to you. Here's just an example of a basic usage...

class Observer implements SplObserver {

    function update(SplSubject $subject) {
        echo "notified: " . $subject->status . "\n";
        }

    }

class Subject implements SplSubject {
    private $status;
    private $observer;

    function __construct(SplObserver $observer) {
        $this->attach($observer);
        }

    function __destruct() {
        $this->detach($this->observer);
        }

    function __get($prop) {
        return $this->$prop;
        }

    function __set($prop, $val) {
        $this->$prop = $val;
        $this->notify();
        }

    function notify() {
        $this->observer->update($this);
        }

    function attach(SplObserver $observer) {
        $this->observer = $observer;
        $this->__set("status", "attaching");
        }

    function detach(SplObserver $observer) {
        $this->__set("status", "detaching");
        $this->observer = null;
        }

    }


$observer = new Observer();
$sub1 = new Subject($observer);
$sub1->status = "testing";
$sub2 = new Subject($observer);
unset($sub2);
$sub1->status = "testing again";

Edit: You could have separate "model", "view", and "controller" classes all extending an abstract base class which implements SplSubject, SplObserver. This way they can all communicate with each other and complete your chart. But, the problem with that all centers around the notify() method, where without the option to select which observer to notify, everyone gets the update message. The solution to allow communication between only one observer at a time would be to rip out the reference to the SPL interfaces entirely, and having your notify() method take in a specific observer argument to send to.

Take the following example, which although has basically the same methods as the SPL interfaces blended together require, it doesn't implement those interfaces at all. Instead, each method takes in a variable number of arguments (expected to be other SubjectObserver objects)...

abstract class SubjectObserver {
    protected $observer_list = array(); // assoc array of classname=>object
    public $status;

    function update(SubjectObserver $subject) {
        echo get_class($this) . " notified by " . get_class($subject) . ": " . $subject->status . "\n";
        }

    function __destruct() {
        foreach ($this->observer_list as $observer)
            { $this->detach($observer); }
        }

    function notify() {
        $args = func_get_args();
        foreach ($args as $arg)
            { $this->observer_list[get_class($arg)]->update($this); }
        }

    function attach() {
        $args = func_get_args();
        foreach ($args as $arg) {
            $this->observer_list[get_class($arg)] = $arg;
            $this->status = "attaching " . get_class($arg);
            $this->notify($arg);
            }
        }

    function detach() {
        $args = func_get_args();
        foreach ($args as $arg) {
            $this->status = "detaching " . get_class($arg);
            $this->notify($arg);
            unset($this->observer_list[get_class($arg)]);
            }
        }

    }

class model extends SubjectObserver {}

class view extends SubjectObserver {}

class controller extends SubjectObserver {}

// initialize
$model = new model();
$view = new view();
$controller = new controller();

$model->attach($controller, $view);
$view->attach($controller, $model);
$controller->attach($model, $view);


// process some input
$controller->status = "input received";
$controller->notify($model);
$model->status = "data filtered";
$model->notify($controller, $view);
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Maybe it's just me, but I find the banner indent style very confusing in your last code block. \$\endgroup\$ – scribu Jan 31 '11 at 3:50
  • \$\begingroup\$ @scribu: I suppose good thing the question didn't ask for points on style then :). Maybe it was the lack of brackets on the one-liner foreach loops or the fact that these are just extremely short demo functions? Just going for brevity here, so folks can pick out just the concepts. \$\endgroup\$ – bob-the-destroyer Jan 31 '11 at 6:38
  • \$\begingroup\$ Yeah. This pair of interfaces is most disappointing. \$\endgroup\$ – Mchl Jan 31 '11 at 14:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.