Completed
Push — 2.0.x ( f85be6...e30486 )
by Andrew
02:32
created

AbstractViewModel   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 88
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 11
c 3
b 0
f 1
lcom 1
cbo 3
dl 0
loc 88
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A validateDisplayVariables() 0 18 4
A __construct() 0 5 1
A __get() 0 12 3
A __set() 0 4 1
A display() 0 8 2
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\Exception\InvalidDisplayVariablesException;
10
use Ingenerator\KohanaView\Exception\InvalidViewVarAssignmentException;
11
use Ingenerator\KohanaView\Exception\UndefinedViewVarException;
12
use Ingenerator\KohanaView\ViewModel;
13
14
/**
15
 * The AbstractViewModel can be used as a base for all ViewModels within the system. It supports providing values
16
 * to the template either by providing read-only access to the values in the variables array, or by magically calling
17
 * a var_{variable_name} method.
18
 *
19
 * Values in the variables array always take precedence, so custom getters can cache calculated values for subsequent
20
 * reuse by simply assigning the value once they have it. For example:
21
 *
22
 *   class ViewThatDoesWork extends AbstractViewModel {
23
 *
24
 *     protected $variables = [];
25
 *
26
 *     protected var_calculated_id()
27
 *     {
28
 *        // This custom getter will only be called once for each view rendering cycle. Note that calls
29
 *        // to the `::display()` method will wipe out all calculated values.
30
 *        $this->variables['calculated_id'] = uniqid();
31
 *        return $this->variables['calculated_id'];
32
 *     }
33
 *   }
34
 *
35
 * By default, values are provided as an array to the display method - which will throw if any values are missing
36
 * or any unexpected variables are provided. This ensures that for views that may be rendered in loops etc, a call
37
 * to display will fully reset the state of the view.
38
 *
39
 * You can of course implement custom setters for fields that you want to be individually changed.
40
 */
41
abstract class AbstractViewModel implements ViewModel
42
{
43
    /**
44
     * @var array The actual view data
45
     */
46
    protected $variables = [];
47
48
    /**
49
     * @var string[] The names of the valid set of fields that must be passed to the display() method
50
     */
51
    protected $expect_var_names = [];
52
53
    public function __construct()
54
    {
55
        // Assign the expect_var_names to ensure that we don't accidentally start requiring compiled fields
56
        $this->expect_var_names = array_keys($this->variables);
57
    }
58
59
    /**
60
     * Get field values
61
     *
62
     * @param string $name
63
     *
64
     * @return mixed
65
     */
66
    public function __get($name)
67
    {
68
        if (array_key_exists($name, $this->variables)) {
69
            return $this->variables[$name];
70
        } elseif (method_exists($this, 'var_'.$name)) {
71
            $method = 'var_'.$name;
72
73
            return $this->$method();
74
        } else {
75
            throw UndefinedViewVarException::forClassAndVar(static::class, $name);
76
        }
77
    }
78
79
    /**
80
     * @param string $name
81
     * @param mixed  $value
82
     *
83
     * @throws \BadMethodCallException values cannot be assigned except with the display method
84
     */
85
    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...
86
    {
87
        throw InvalidViewVarAssignmentException::forReadOnlyVar(static::class, $name);
88
    }
89
90
    /**
91
     * Set the data to be rendered in the view - note this does not actually render the view.
92
     *
93
     * @param array $variables
94
     */
95
    public function display(array $variables)
96
    {
97
        if ($errors = $this->validateDisplayVariables($variables)) {
98
            throw InvalidDisplayVariablesException::passedToDisplay(static::class, $errors);
0 ignored issues
show
Documentation introduced by
$errors is of type array<integer,string>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
99
        }
100
101
        $this->variables = $variables;
102
    }
103
104
    /**
105
     * @param array $variables
106
     *
107
     * @return string[] of errors
108
     */
109
    protected function validateDisplayVariables(array $variables)
110
    {
111
        $errors             = [];
112
        $provided_variables = array_keys($variables);
113
        foreach (array_diff($provided_variables, $this->expect_var_names) as $unexpected_var) {
114
            if (method_exists($this, 'var_'.$unexpected_var)) {
115
                $errors[] = "'$unexpected_var' conflicts with ::var_$unexpected_var()";
116
            } else {
117
                $errors[] = "'$unexpected_var' is not expected";
118
            }
119
        }
120
121
        foreach (array_diff($this->expect_var_names, $provided_variables) as $missing_var) {
122
            $errors[] = "'$missing_var' is missing";
123
        }
124
125
        return $errors;
126
    }
127
128
}
129