Completed
Pull Request — 2.0.x (#6)
by Andrew
03:37
created

AbstractViewModel::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
rs 9.4286
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * @author     Andrew Coulton <[email protected]>
4
 * @copyright  2015 inGenerator Ltd
5
 * @license    http://kohanaframework.org/license
6
 */
7
namespace Ingenerator\KohanaView\ViewModel;
8
9
use Ingenerator\KohanaView\ViewModel;
10
11
/**
12
 * The AbstractViewModel can be used as a base for all ViewModels within the system. It supports providing values
13
 * to the template either by providing read-only access to the values in the variables array, or by magically calling
14
 * a var_{variable_name} method.
15
 *
16
 * Values in the variables array always take precedence, so custom getters can cache calculated values for subsequent
17
 * reuse by simply assigning the value once they have it. For example:
18
 *
19
 *   class ViewThatDoesWork extends AbstractViewModel {
20
 *
21
 *     protected $variables = [];
22
 *
23
 *     protected var_calculated_id()
24
 *     {
25
 *        // This custom getter will only be called once for each view rendering cycle. Note that calls
26
 *        // to the `::display()` method will wipe out all calculated values.
27
 *        $this->variables['calculated_id'] = uniqid();
28
 *        return $this->variables['calculated_id'];
29
 *     }
30
 *   }
31
 *
32
 * By default, values are provided as an array to the display method - which will throw if any values are missing
33
 * or any unexpected variables are provided. This ensures that for views that may be rendered in loops etc, a call
34
 * to display will fully reset the state of the view.
35
 *
36
 * You can of course implement custom setters for fields that you want to be individually changed.
37
 */
38
abstract class AbstractViewModel implements ViewModel
39
{
40
    /**
41
     * @var array The actual view data
42
     */
43
    protected $variables = [];
44
45
    /**
46
     * @var string[] The names of the valid set of fields that must be passed to the display() method
47
     */
48
    protected $expect_var_names = [];
49
50
    public function __construct()
51
    {
52
        // Assign the expect_var_names to ensure that we don't accidentally start requiring compiled fields
53
        $this->expect_var_names = array_keys($this->variables);
54
    }
55
56
    /**
57
     * Get field values
58
     *
59
     * @param string $name
60
     *
61
     * @return mixed
62
     */
63
    public function __get($name)
64
    {
65
        if (array_key_exists($name, $this->variables)) {
66
            return $this->variables[$name];
67
        } elseif (method_exists($this, 'var_'.$name)) {
68
            $method = 'var_'.$name;
69
70
            return $this->$method();
71
        } else {
72
            throw new \BadMethodCallException(static::class." does not define a '$name' field");
73
        }
74
    }
75
76
    /**
77
     * @param string $name
78
     * @param mixed  $value
79
     *
80
     * @throws \BadMethodCallException values cannot be assigned except with the display method
81
     */
82
    public function __set($name, $value)
1 ignored issue
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
83
    {
84
        throw new \BadMethodCallException(static::class.' variables are read-only, cannot assign '.$name);
85
    }
86
87
    /**
88
     * Set the data to be rendered in the view - note this does not actually render the view.
89
     *
90
     * @param array $variables
91
     */
92
    public function display(array $variables)
93
    {
94
        $errors             = [];
95
        $provided_variables = array_keys($variables);
96
        foreach (array_diff($provided_variables, $this->expect_var_names) as $unexpected_var) {
97
            if (method_exists($this, 'var_'.$unexpected_var)) {
98
                $errors[] = "'$unexpected_var' conflicts with ::var_$unexpected_var()";
99
            } else {
100
                $errors[] = "'$unexpected_var' is not expected";
101
            }
102
        }
103
        foreach (array_diff($this->expect_var_names, $provided_variables) as $missing_var) {
104
            $errors[] = "'$missing_var' is missing";
105
        }
106
107
        if ( ! empty($errors)) {
108
            throw new \InvalidArgumentException(
109
                "Invalid variables provided to ".static::class."::display()"
110
                ."\n - ".implode("\n - ", $errors)
111
            );
112
        }
113
114
        $this->variables = $variables;
115
    }
116
117
}
118