MVC pattern from scratch
Tags: php, mvc, pattern, fromscratch
Published: 2017-11-06 17:35:30

---
title: MVC pattern from scratch
published: true
description: A study about the implementation of MVC pattern in PHP.
tags: php, MVC, pattern, from scratch
---

Before starting, the reader considers this my personal opinion, how I see the things and not the only way for implement or for use the MVC Pattern.

On internet, it's possible find a lot of implementations and MVC frameworks ready to use, I decided to start from scratch because this is the best method for know deeply a thing.

After hours of theorical and pratical study, for understand what a *Model*, a *View*, a *Controller* and what they do, questions multiply.

Often, I saw that the Pattern components' workload is unbalanced, *Controller* do the most of the job: receive and filter user input data, manipulate the *Model*, call the *View* etc.

Why if the Pattern has three components, the *Controller* should do the most of the job? *Controller* means that this component control others or the Pattern lifecycle? It not was better call it Controller Pattern?

After others studies, a theoretical MVC Pattern where components have responsibility equally divided, begins to take shape in my mind.

I have the components:

- *Model* -> Business Logic; 
- *View* -> Show Informations;
- *Controller* -> Take User Input;

How to create a relation between thems without overload the *Controller*?

Considering that the *Model* should receive input data from *Controller*, process it, do all action needed; The *View* should be informed by the *Model* if data was updated for generate new well formatted output; The *Controller* should filter input data and activate the *Model*;

A possible solution could be...

- *Model* and *View* can speak through the [Observer Pattern](https://en.wikipedia.org/wiki/Observer_pattern);
- *Controller* can pass data to the *Model*, invoking directly the proper *Model*' method;
- *Controller* and *Model* could also linked through the Observer Pattern, but this way is a bit more complicated because *Model* should be *Subject* and *Observer* at the same time.

And the *View* compared to *Controller*?

- Interaction between *View* and *Controller* could be unnecessary!

It's time, with previous "axioms" in mind, to write a lot of PHP code. :)

## The Code
Is organized in three parents classes: *Model*, *View*, *Controller* that define the role and the relation of every component. After three classes for the concrete example: *CalulatorModel*, *CalculatorView*, *CalculatorController*. They inherit from parents classes. Finally the MVC pattern running.

### Parents classes
Could be also abstract classes but not today.

#### Model
Contains methods for the *Subject* role (remind Observer Pattern), notify state change to the *Observer*.

```
<?php

/**
 * Parent class for custom model classes.
 *
 * This class was implemented like part of Observer pattern
 * https://en.wikipedia.org/wiki/Observer_pattern
 * http://php.net/manual/en/class.splsubject.php
 */
class Model implements SplSubject {

    /**
     * @var object List of attached observerer
     */
    private $observers;

    /**
     * @var array Data for notify to observerer
     */
    private $updates = [];

    /**
     * Class Constructor.
     */
    public function __construct() {
        $this->observers = new SplObjectStorage();
    }

    /**
     * Attach an Observer class to this Subject for updates
     * when occour a subject state change.
     *
     * @param SplObserver $observer
     */
    public function attach(SplObserver $observer) {
        if ($observer instanceof View) {
            $this->observers->attach($observer);
        }
    }

    /**
     * Detach an Observer class from this Subject.
     *
     * @param SplObserver $observer
     */
    public function detach(SplObserver $observer) {
        if ($observer instanceof View) {
            $this->observers->detach($observer);
        }
    }

    /**
     * Notify a state change of Subject to all registered Observeres.
     */
    public function notify() {
        foreach ($this->observers as $value) {
            $value->update($this);
        }
    }

    /**
     * Set the data to notify to all registered Observeres.
     *
     * @param array $data
     */
    public function set(array $data) {
        $this->updates = array_merge_recursive($this->updates, $data);
    }

    /**
     * Get the data to notify to all registered Observeres.
     *
     * @return array
     */
    public function get(): array {
        return $this->updates;
    }

}
```

#### View
Contains methods for the *Observer* role (remind Observer Pattern), retrive changes from the *Subject*. 

```
<?php

/**
 * Parent class for custom view classes.
 *
 * This class was implemented like part of Observer pattern
 * https://en.wikipedia.org/wiki/Observer_pattern
 * http://php.net/manual/en/class.splobserver.php
 */
class View implements SplObserver {

    /**
     * @var array Data for the dynamic view
     */
    protected $data = [];

    /**
     * @var string Output data
     */
    protected $output = '';

    /**
     * @var Model Model for access data
     */
    protected $model;

    /**
     * Class Constructor.
     *
     * @param Model $model
     */
    public function __construct(Model $model) {
        $this->model = $model;
    }

    /**
     * Render a template.
     */
    public function render(): string {
        return $this->output;
    }

    /**
     * Update Observer data.
     *
     * @param SplSubject $subject
     */
    public function update(SplSubject $subject) {
        if ($subject instanceof Model) {
            $this->data = array_merge($this->data, $subject->get());
        }
    }

}
```

#### Controller
On Controller there isn't a lot of say.

```
<?php

/**
 * Parent class for custom controller classes.
 */
class Controller {

    /**
     * @var object The model object for current controller
     */
    protected $model = null;

    /**
     * Class Constructor.
     *
     * @param object $model
     */
    public function __construct(Model $model) {
        $this->model = $model;
    }

}
```

### Concrete example classes, a basic calculator
I chose as example a basic calculator, it provide the possibility to add, subtract, multiply and divide, one or more number. I have prefered write a method for every math operation because is simpler follow the code.

Comparing this concrete example with the reasoning at the start of the article, I can say:

- *Model*: Perform mathematical operation and notify result to the *View*;
- *View*: Show operation and result as well formatted output;
- *Controller*: Filter user provided numbers and invoke the *Model*;


#### Calculator Model
```
<?php

/**
 * Our Custom Model
 */
class CalculatorModel extends Model {

    /**
     * Class Constructor.
     */
    public function __construct() {
        parent::__construct();
    }

    /**
     * Mutiply business logic.
     * 
     * @param array $numbers
     */
    public function multiply(array $numbers) {
        $this->set([
            'result' => $this->operation('*', $numbers),
            'operands' => $numbers,
        ]);
    }

    /**
     * Divide business logic.
     * 
     * @param array $numbers
     */
    public function divide(array $numbers) {
        $this->set([
            'result' => $this->operation('/', $numbers),
            'operands' => $numbers
        ]);
    }

    /**
     * Subtraction business logic.
     * 
     * @param array $numbers
     */
    public function sub(array $numbers) {
        $this->set([
            'result' => $this->operation('-', $numbers),
            'operands' => $numbers
        ]);
    }

    /**
     * Addition business logic.
     * 
     * @param array $numbers
     */
    public function add(array $numbers) {
        $this->set([
            'result' => $this->operation('+', $numbers),
            'operands' => $numbers
        ]);
    }

    /**
     * Do math operation.
     * This is an example, division by 0 and other problematic things not checked.
     * Danger - Very Horrible Code - need refactor.
     *
     * @param string $operator
     * @param array $numbers
     * @return int|float
     */
    private function operation(string $operator, array $numbers) {
        $temp = null;
        foreach ($numbers as $n) {
            if ($temp === null) {
                $temp = $n;
                continue;
            }

            //example, division by 0 and other not checked
            switch ($operator) {
                case '*':
                    $temp = $temp * $n;
                    break;
                case '/':
                    $temp = $temp / $n;
                    break;
                case '-':
                    $temp = $temp - $n;
                    break;
                case '+':
                    $temp = $temp + $n;
                    break;
            }
        }

        return $temp;
    }

}
```

#### Calculator View
```
<?php

/**
 * Our Custom Model
 */
class CalculatorView extends View {

    /**
     * Class Constructor.
     * 
     * @param CalculatorModel $model
     */
    public function __construct(CalculatorModel $model) {
        parent::__construct($model);
    }

    /**
     * Build output for multiply.
     */
    public function multiply() {

        $this->output = $this->generateOutput(
                'Multiplication', 
                $this->data['operands'], 
                $this->data['result']
        );
    }

    /**
     * Build output for divide.
     */
    public function divide() {
        $this->output = $this->generateOutput(
                'Division', 
                $this->data['operands'], 
                $this->data['result']
        );
    }

    /**
     * Build output for add.
     */
    public function add() {
        $this->output = $this->generateOutput(
                'Addiction', 
                $this->data['operands'], 
                $this->data['result']
        );
    }

    /**
     * Build output for sub.
     */
    public function sub() {
        $this->output = $this->generateOutput(
                'Subtraction', 
                $this->data['operands'], 
                $this->data['result']
        );
    }

    /**
     * Generate the output.
     * Danger - Very Horrible Code - need refactor.
     *
     * @param string $operation
     * @param array $operand
     * @param type $result
     * 
     * @return string
     */
    private function generateOutput(string $operation, array $operands, $result): string {
        $string = $operation . ': ';

        foreach ($operands as $value) {
            $string .= $value . ' * ';
        }

        $string = substr($string, 0, strlen($string) - 2);
        $string .= '= ' . $result;

        return $string;
    }

}
```

#### Calculator Controller
```
<?php

/**
 * Our Custom Controller
 */
class CalculatorController extends Controller {

    /**
     * Class Constructor.
     * 
     * @param CalculatorModel $model
     */
    public function __construct(CalculatorModel $model) {
        parent::__construct($model);
    }

    /**
     * Multiply input point.
     * 
     * @param mixed $numbers
     */
    public function multiply(... $numbers) {

        //check user input
        $this->checkOperands($numbers);
        $this->filter($numbers);

        //manipulate the model
        $this->model->multiply($numbers);
    }

    /**
     * Divide input point.
     * 
     * @param mixed $numbers
     */
    public function divide(... $numbers) {

        //check user input
        $this->checkOperands($numbers);
        $this->filter($numbers);

        //manipulate the model
        $this->model->divide($numbers);
    }

    /**
     * Sub input point.
     * 
     * @param mixed $numbers
     */
    public function sub(... $numbers) {

        //check user input
        $this->checkOperands($numbers);
        $this->filter($numbers);

        //manipulate the model
        $this->model->sub($numbers);
    }

    /**
     * Add input point.
     *
     * @param mixed $numbers
     */
    public function add(... $numbers) {

        //check user input
        $this->checkOperands($numbers);
        $this->filter($numbers);

        //manipulate the model
        $this->model->add($numbers);
    }

    /**
     * Filter user supplied numbers.
     * 
     * @param mixed $numbers
     * @throws InvalidArgumentException
     */
    private function filter(&$numbers) {
        foreach ($numbers as $key => $number) {
            switch (gettype($number)) {
                case 'string':
                    $number[$key] = strtonum($number);
                    break;
                case 'integer':
                    break;
                case 'double':
                    break;
                default:
                    throw new InvalidArgumentException('Not a number');
            }
        }
    }

    /**
     * Convert a number given as string in the proper type (int or float).
     * https://secure.php.net/manual/en/language.types.type-juggling.php
     *
     * @param string $string Number as string ex '1.0', '0.9' etc
     * @return int|float
     */
    private function strtonum(string $string) {
        if (fmod((float) $string, 1.0) === 0.0) {
            return (int) $string;
        }

        return (float) $string;
    }

    /**
     * Check minimum numbers required for operations.
     *
     * @param array $numbers
     * @throws ArgumentCountError
     */
    private function checkOperands(array &$numbers) {
        if (count($numbers) < 2) {
            throw new ArgumentCountError('At least two numbers needed for operation');
        }
    }

}
```

## Running
I wish multiply two numbers (2 and 3) with the basic calculator.
```
<?php

//create components
$model = new CalculatorModel();
$view = new CalculatorView($model);
$controller = new CalculatorController($model);

//attach the observer to the subject
$model->attach($view);

//call controller
$controller->multiply(2, 3);

//notify changes to observer
$model->notify();

//call view
$view->multiply();

//get the output
echo $view->render();
```

This code show as output:
```
Multiplication: 2 * 3 = 6
```

## Finally
It was not easy, the Pattern seem very complicate and there isn't only one way for implement it, but after a bit of practice everything becomes simple, it is very flexible, is a great aids for the organization of code. It's Worth!

A final consideration about the runnig, at first look, code should not work because the *View*, after notify, is called outside the *Model*.

How the *View* (outside the *Model*) receive notifies if the scope of the object is different?

*Model* notify in local scope inside the instance, *View* calls method ($view->multiply()) in global scope. I think the solution could be found in "how php pass the objects to a function", according to the official documentation [objects are passed by references by default](http://php.net/manual/en/language.oop5.references.php) but I'm not still sure it works for this.

If you wish try the code [3v4l.org can help you](https://3v4l.org/6Rprp), code is on my [Gist Account](https://gist.github.com/s3b4stian/09c303576cea9197b547a27684ea4421) and require PHP7 for run.