Completed
Push — develop ( 556e46...dfa7d2 )
by Paul
02:07
created

Form::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace GeminiLabs\Castor\Forms;
4
5
use Exception;
6
use GeminiLabs\Castor\Forms\Field;
7
use GeminiLabs\Castor\Services\Normalizer;
8
9
class Form
10
{
11
	/**
12
	 * @var array
13
	 */
14
	protected $args;
15
16
	/**
17
	 * @var array
18
	 */
19
	protected $dependencies;
20
21
	/**
22
	 * @var Field
23
	 */
24
	protected $field;
25
26
	/**
27
	 * @var array
28
	 */
29
	protected $fields;
30
31
	/**
32
	 * @var Normalizer
33
	 */
34
	protected $normalize;
35
36
	public function __construct( Field $field, Normalizer $normalize )
37
	{
38
		$this->args         = [];
39
		$this->dependencies = [];
40
		$this->field        = $field;
41
		$this->fields       = [];
42
		$this->normalize    = $normalize;
43
	}
44
45
	/**
46
	 * @param string $property
47
	 *
48
	 * @return mixed
49
	 * @throws Exception
50
	 */
51 View Code Duplication
	public function __get( $property )
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
52
	{
53
		switch( $property ) {
54
			case 'args':
55
			case 'dependencies':
56
			case 'fields':
57
				return $this->$property;
58
		}
59
		throw new Exception( sprintf( 'Invalid %s property: %s', __CLASS__, $property ));
60
	}
61
62
	/**
63
	 * @param string $property
64
	 * @param string $value
65
	 *
66
	 * @return void
67
	 * @throws Exception
68
	 */
69 View Code Duplication
	public function __set( $property, $value )
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
70
	{
71
		switch( $property ) {
72
			case 'args':
73
			case 'dependencies':
74
			case 'fields':
75
				$this->$property = $value;
76
				break;
77
			default:
78
				throw new Exception( sprintf( 'Invalid %s property: %s', __CLASS__, $property ));
79
		}
80
	}
81
82
	/**
83
	 * Add a field to the form
84
	 *
85
	 * @return Form
86
	 */
87
	public function addField( array $args = [] )
88
	{
89
		$field = $this->field->normalize( $args );
90
91
		if( $field->args['render'] !== false ) {
0 ignored issues
show
Documentation introduced by
The property $args is declared protected in GeminiLabs\Castor\Forms\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
92
			$this->dependencies = array_unique(
93
				array_merge( $field->dependencies, $this->dependencies )
0 ignored issues
show
Documentation introduced by
The property $dependencies is declared protected in GeminiLabs\Castor\Forms\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
94
			);
95
			$this->fields[] = $field;
96
		}
97
98
		return $this;
99
	}
100
101
	/**
102
	 * Normalize the form arguments
103
	 *
104
	 * @return Form
105
	 */
106
	public function normalize( array $args = [] )
107
	{
108
		$defaults = [
109
			'action'     => '',
110
			'attributes' => '',
111
			'id'         => '',
112
			'class'      => '',
113
			'enctype'    => 'multipart/form-data',
114
			'method'     => 'post',
115
			'nonce'      => '',
116
			'submit'     => __( 'Submit', 'site-reviews' ),
117
		];
118
119
		$this->args = array_merge( $defaults, $args );
120
121
		$attributes = $this->normalize->form( $this->args, 'implode' );
122
123
		$this->args['attributes'] = $attributes;
124
125
		return $this;
126
	}
127
128
	/**
129
	 * Render the form
130
	 *
131
	 * @param mixed $print
132
	 *
133
	 * @return string|void
134
	 */
135
	public function render( $print = true )
136
	{
137
		$rendered = sprintf( '<form %s>%s%s</form>',
138
			$this->args['attributes'],
139
			$this->generateFields(),
140
			$this->generateSubmitButton()
141
		);
142
143
		if( !!$print && $print !== 'return' ) {
144
			echo $rendered;
145
		}
146
147
		return $rendered;
148
	}
149
150
	/**
151
	 * Reset the Form
152
	 *
153
	 * @return Form
154
	 */
155
	public function reset()
156
	{
157
		$this->args         = [];
158
		$this->dependencies = [];
159
		$this->fields       = [];
160
		return $this;
161
	}
162
163
	/**
164
	 * Generate the form fields
165
	 *
166
	 * @return string
167
	 */
168
	protected function generateFields()
169
	{
170
		$hiddenFields = '';
171
172
		$fields = array_reduce( $this->fields, function( $carry, $formField ) use ( &$hiddenFields ) {
173
174
			$stringLegend    = '<legend class="screen-reader-text"><span>%s</span></legend>';
175
			$stringFieldset  = '<fieldset%s>%s%s</fieldset>';
176
			$stringRendered  = '<tr class="glsr-field %s"><th scope="row">%s</th><td>%s</td></tr>';
177
			$outsideRendered = '</tbody></table>%s<table class="form-table"><tbody>';
178
179
			// set field value only when rendering because we need to check the default setting
180
			// against the database
181
			$field = $formField->setValue()->getField();
182
183
			$multi    = $field->multi === true;
184
			$label    = $field->generateLabel();
185
			$rendered = $field->render();
186
187
			// render hidden inputs outside the table
188
			if( $field->args['type'] === 'hidden' ) {
189
				$hiddenFields .= $rendered;
190
				return $carry;
191
			}
192
193
			$hiddenClass = $this->isFieldHidden( $formField ) ? 'hidden' : '';
194
195
			if( $multi ) {
196
				if( $depends = $formField->getDataDepends() ) {
197
					$depends = sprintf( ' data-depends=\'%s\'', json_encode( $depends ));
198
				}
199
200
				$legend = $label ? sprintf( $stringLegend, $label ) : '';
201
				$rendered = sprintf( $stringFieldset, $depends, $legend, $rendered );
202
			}
203
204
			$renderedField = $field->outside
205
				? sprintf( $outsideRendered, $rendered )
206
				: sprintf( $stringRendered, $hiddenClass, $label, $rendered );
207
208
			return $carry . $renderedField;
209
		});
210
211
		return sprintf( '<table class="form-table"><tbody>%s</tbody></table>%s', $fields, $hiddenFields );
212
	}
213
214
	/**
215
	 * Generate the form submit button
216
	 *
217
	 * @return null|string
218
	 */
219
	protected function generateSubmitButton()
220
	{
221
		$args = $this->args['submit'];
222
223
		is_array( $args ) ?: $args = ['text' => $args ];
224
225
		$args = shortcode_atts([
226
			'text' => __( 'Save Changes', 'site-reviews' ),
227
			'type' => 'primary',
228
			'name' => 'submit',
229
			'wrap' => true,
230
			'other_attributes' => null,
231
		], $args );
232
233
		if( is_admin() ) {
234
			ob_start();
235
			submit_button( $args['text'], $args['type'], $args['name'], $args['wrap'], $args['other_attributes'] );
236
			return ob_get_clean();
237
		}
238
	}
239
240
	/**
241
	 * @param object $field GeminiLabs\Castor\Form\Fields\*
242
	 *
243
	 * @return bool|null
244
	 */
245
	protected function isFieldHidden( $field )
246
	{
247
		if( !( $dependsOn = $field->getDataDepends() ))return;
248
249
		foreach( $this->fields as $formField ) {
250
			if( $dependsOn['name'] !== $formField->args['name'] )continue;
251
252
			if( is_array( $dependsOn['value'] )) {
253
				return !in_array( $formField->args['value'], $dependsOn['value'] );
254
			}
255
256
			return $dependsOn['value'] != $formField->args['value'];
257
		}
258
	}
259
}
260