Completed
Push — master ( 4f8f5b...7f2e84 )
by Ben
16s
created
src/Former/Traits/Checkable.php 1 patch
Indentation   +595 added lines, -595 removed lines patch added patch discarded remove patch
@@ -14,599 +14,599 @@
 block discarded – undo
14 14
  */
15 15
 abstract class Checkable extends Field
16 16
 {
17
-	/**
18
-	 * Renders the checkables as inline
19
-	 *
20
-	 * @var boolean
21
-	 */
22
-	protected $inline = false;
23
-
24
-	/**
25
-	 * Add a text to a single element
26
-	 *
27
-	 * @var string
28
-	 */
29
-	protected $text = null;
30
-
31
-	/**
32
-	 * Renders the checkables as grouped
33
-	 *
34
-	 * @var boolean
35
-	 */
36
-	protected $grouped = false;
37
-
38
-	/**
39
-	 * The checkable items currently stored
40
-	 *
41
-	 * @var array
42
-	 */
43
-	protected $items = array();
44
-
45
-	/**
46
-	 * The type of checkable item
47
-	 *
48
-	 * @var string
49
-	 */
50
-	protected $checkable = null;
51
-
52
-	/**
53
-	 * An array of checked items
54
-	 *
55
-	 * @var array
56
-	 */
57
-	protected $checked = array();
58
-
59
-	/**
60
-	 * The checkable currently being focused on
61
-	 *
62
-	 * @var integer
63
-	 */
64
-	protected $focus = null;
65
-
66
-	/**
67
-	 * Whether this particular checkable is to be pushed
68
-	 *
69
-	 * @var boolean
70
-	 */
71
-	protected $isPushed = null;
72
-
73
-	////////////////////////////////////////////////////////////////////
74
-	//////////////////////////// CORE METHODS //////////////////////////
75
-	////////////////////////////////////////////////////////////////////
76
-
77
-	/**
78
-	 * Build a new checkable
79
-	 *
80
-	 * @param Container $app
81
-	 * @param string    $type
82
-	 * @param array     $name
83
-	 * @param           $label
84
-	 * @param           $value
85
-	 * @param           $attributes
86
-	 */
87
-	public function __construct(Container $app, $type, $name, $label, $value, $attributes)
88
-	{
89
-		// Unify auto and chained methods of grouping checkboxes
90
-		if (Str::endsWith($name, '[]')) {
91
-			$name = substr($name, 0, -2);
92
-			$this->grouped();
93
-		}
94
-		parent::__construct($app, $type, $name, $label, $value, $attributes);
95
-
96
-		if (is_array($this->value)) {
97
-			$this->items($this->value);
98
-		}
99
-	}
100
-
101
-	/**
102
-	 * Apply methods to focused checkable
103
-	 *
104
-	 * @param string $method
105
-	 * @param array  $parameters
106
-	 *
107
-	 * @return $this
108
-	 */
109
-	public function __call($method, $parameters)
110
-	{
111
-		$focused = $this->setOnFocused('attributes.'.$method, Arr::get($parameters, 0));
112
-		if ($focused) {
113
-			return $this;
114
-		}
115
-
116
-		return parent::__call($method, $parameters);
117
-	}
118
-
119
-	/**
120
-	 * Prints out the currently stored checkables
121
-	 */
122
-	public function render()
123
-	{
124
-		$html = null;
125
-
126
-		$this->setFieldClasses();
127
-
128
-		// Multiple items
129
-		if ($this->items) {
130
-			unset($this->app['former']->labels[array_search($this->name, $this->app['former']->labels)]);
131
-			foreach ($this->items as $key => $item) {
132
-				$value = $this->isCheckbox() && !$this->isGrouped() ? 1 : $key;
133
-				$html .= $this->createCheckable($item, $value);
134
-			}
135
-
136
-			return $html;
137
-		}
138
-
139
-		// Single item
140
-		return $this->createCheckable(array(
141
-			'name'  => $this->name,
142
-			'label' => $this->text,
143
-			'value' => $this->value,
144
-			'attributes' => $this->attributes,
145
-		));
146
-	}
147
-
148
-	////////////////////////////////////////////////////////////////////
149
-	////////////////////////// FIELD METHODS ///////////////////////////
150
-	////////////////////////////////////////////////////////////////////
151
-
152
-	/**
153
-	 * Focus on a particular checkable
154
-	 *
155
-	 * @param integer $on The checkable to focus on
156
-	 *
157
-	 * @return $this
158
-	 */
159
-	public function on($on)
160
-	{
161
-		if (!isset($this->items[$on])) {
162
-			return $this;
163
-		} else {
164
-			$this->focus = $on;
165
-		}
166
-
167
-		return $this;
168
-	}
169
-
170
-	/**
171
-	 * Set the checkables as inline
172
-	 */
173
-	public function inline($isInline = true)
174
-	{
175
-		$this->inline = $isInline;
176
-
177
-		return $this;
178
-	}
179
-
180
-	/**
181
-	 * Set the checkables as stacked
182
-	 */
183
-	public function stacked($isStacked = true)
184
-	{
185
-		$this->inline = !$isStacked;
186
-
187
-		return $this;
188
-	}
189
-
190
-	/**
191
-	 * Set the checkables as grouped
192
-	 */
193
-	public function grouped($isGrouped = true)
194
-	{
195
-		$this->grouped = $isGrouped;
196
-
197
-		return $this;
198
-	}
199
-
200
-	/**
201
-	 * Add text to a single checkable
202
-	 *
203
-	 * @param  string $text The checkable label
204
-	 *
205
-	 * @return $this
206
-	 */
207
-	public function text($text)
208
-	{
209
-		// Translate and format
210
-		$text = Helpers::translate($text);
211
-
212
-		// Apply on focused if any
213
-		$focused = $this->setOnFocused('label', $text);
214
-		if ($focused) {
215
-			return $this;
216
-		}
217
-
218
-		$this->text = $text;
219
-
220
-		return $this;
221
-	}
222
-
223
-	/**
224
-	 * Push this particular checkbox
225
-	 *
226
-	 * @param boolean $pushed
227
-	 *
228
-	 * @return $this
229
-	 */
230
-	public function push($pushed = true)
231
-	{
232
-		$this->isPushed = $pushed;
233
-
234
-		return $this;
235
-	}
236
-
237
-	/**
238
-	 * Check a specific item
239
-	 *
240
-	 * @param bool|string $checked The checkable to check, or an array of checked items
241
-	 *
242
-	 * @return $this
243
-	 */
244
-	public function check($checked = true)
245
-	{
246
-		// If we're setting all the checked items at once
247
-		if (is_array($checked)) {
248
-			$this->checked = $checked;
249
-			// Checking an item in particular
250
-		} elseif (is_string($checked) or is_int($checked)) {
251
-			$this->checked[$checked] = true;
252
-			// Only setting a single item
253
-		} else {
254
-			$this->checked[$this->name] = (bool) $checked;
255
-		}
256
-
257
-		return $this;
258
-	}
259
-
260
-	/**
261
-	 * Creates a series of checkable items from query with attributes
262
-	 *
263
-	 * @param mixed $query
264
-	 * @param mixed $text
265
-	 * @param mixed $attributes
266
-	 * @return $this
267
-	 */
268
-	public function fromQuery($query, $text = null, $attributes = null)
269
-	{
270
-		if ($this->isGrouped()) {
271
-			// Remove any possible items added by the Populator.
272
-			$this->items = array();
273
-		}
274
-		$this->items($query, $text, $attributes);
275
-
276
-		return $this;
277
-	}
278
-
279
-	/**
280
-	 * Check if the checkables are inline
281
-	 *
282
-	 * @return boolean
283
-	 */
284
-	public function isInline()
285
-	{
286
-		return $this->inline;
287
-	}
288
-
289
-	////////////////////////////////////////////////////////////////////
290
-	////////////////////////// INTERNAL METHODS ////////////////////////
291
-	////////////////////////////////////////////////////////////////////
292
-
293
-	/**
294
-	 * Creates a series of checkable items
295
-	 *
296
-	 * @param array|Collection $_items Items to create
297
-	 * @param string|function  $text The value to use as text
298
-	 * @param string|array     $attributes The data to use as attributes
299
-	 */
300
-	protected function items($_items, $text = null, $attributes = null)
301
-	{
302
-		// If passing an array
303
-		if (sizeof($_items) == 1 and
304
-			isset($_items[0]) and
305
-			is_array($_items[0])
306
-		) {
307
-			$_items = $_items[0];
308
-		}
309
-
310
-		// Fetch models if that's what we were passed
311
-		if ((isset($_items[0]) and is_object($_items[0])) or ($_items instanceof Collection)) {
312
-			$_items = Helpers::queryToArray($_items, $text, $attributes);
313
-			if (is_null($text) && is_null($attributes)) {
314
-				$_items = array_flip($_items);
315
-			}
316
-		}
317
-
318
-		// Iterate through items, assign a name and a label to each
319
-		$count = 0;
320
-		foreach ($_items as $label => $name) {
321
-
322
-			// Define a fallback name in case none is found
323
-			$fallback = $this->isCheckbox()
324
-				? $this->name.'_'.$count
325
-				: $this->name;
326
-
327
-			// Grouped fields
328
-			if ($this->isGrouped()) {
329
-				$attributes['id'] = str_replace('[]', '', $fallback);
330
-				$fallback         = str_replace('[]', '', $this->name).'[]';
331
-			}
332
-
333
-			// If we haven't any name defined for the checkable, try to compute some
334
-			if (!is_string($label) and !is_array($name)) {
335
-				$label = $name;
336
-				$name  = $fallback;
337
-			}
338
-
339
-			// If we gave custom information on the item, add them
340
-			if (is_array($name)) {
341
-				$attributes = $name;
342
-				$name       = Arr::get($attributes, 'name', $fallback);
343
-				unset($attributes['name']);
344
-			}
345
-
346
-			// Store all informations we have in an array
347
-			$item = array(
348
-				'name'  => $name,
349
-				'label' => Helpers::translate($label),
350
-				'count' => $count,
351
-			);
352
-			if (isset($attributes)) {
353
-				$item['attributes'] = $attributes;
354
-			}
355
-
356
-			$this->items[] = $item;
357
-			$count++;
358
-		}
359
-	}
360
-
361
-	/**
362
-	 * Renders a checkable
363
-	 *
364
-	 * @param string|array $item          A checkable item
365
-	 * @param integer      $fallbackValue A fallback value if none is set
366
-	 *
367
-	 * @return string
368
-	 */
369
-	protected function createCheckable($item, $fallbackValue = 1)
370
-	{
371
-		// Extract informations
372
-		extract($item);
373
-
374
-		// Set default values
375
-		if (!isset($attributes)) {
376
-			$attributes = array();
377
-		}
378
-		if (isset($attributes['value'])) {
379
-			$value = $attributes['value'];
380
-		}
381
-		if (!isset($value) or $value === $this->app['former']->getOption('unchecked_value')) {
382
-			$value = $fallbackValue;
383
-		}
384
-
385
-		// If inline items, add class
386
-		$isInline = $this->inline ? ' '.$this->app['former.framework']->getInlineLabelClass($this) : '';
387
-
388
-		// In Bootsrap 3 or 4 or 5, don't append the the checkable type (radio/checkbox) as a class if
389
-		// rendering inline.
390
-		$class =  in_array($this->app['former']->framework(), ['TwitterBootstrap3', 'TwitterBootstrap4', 'TwitterBootstrap5']) ? trim($isInline) : $this->checkable.$isInline;
391
-
392
-		// Merge custom attributes with global attributes
393
-		$attributes = array_merge($this->attributes, $attributes);
394
-		if (!isset($attributes['id'])) {
395
-			$attributes['id'] = $name.$this->unique($name);
396
-		}
397
-
398
-		// Create field
399
-		$field = Input::create($this->checkable, $name, Helpers::encode($value), $attributes);
400
-		if ($this->isChecked($item, $value)) {
401
-			$field->checked('checked');
402
-		}
403
-
404
-		// Add hidden checkbox if requested
405
-		if ($this->isOfType('checkbox', 'checkboxes', 'switch', 'switches')) {
406
-			if ($this->isPushed or ($this->app['former']->getOption('push_checkboxes') and $this->isPushed !== false)) {
407
-				$field = $this->app['former']->hidden($name)->forceValue($this->app['former']->getOption('unchecked_value')).$field->render();
408
-
409
-				// app['former.field'] was overwritten by Former::hidden() call in the line above, so here
410
-				// we reset it to $this to enable $this->app['former']->getErrors() to retrieve the correct object
411
-				$this->app->instance('former.field', $this);
412
-			}
413
-		}
414
-
415
-		// If no label to wrap, return plain checkable
416
-		if (!$label && !$this->isOfType('switch', 'switches')) {
417
-			$element = (is_object($field)) ? $field->render() : $field;
418
-		} elseif (in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5'])) {
419
-			$element = $field;
420
-			if ($label) {
421
-				// Revised for Bootstrap 4 and 5, move the 'input' outside of the 'label'
422
-				$labelClass = 'form-check-label';
423
-				$element .= Element::create('label', $label)->for($attributes['id'])->class($labelClass)->render();
424
-			}
425
-
426
-			$wrapperClass = $this->inline ? 'form-check form-check-inline' : 'form-check';
427
-			if ($this->app['former']->framework() === 'TwitterBootstrap5' &&
428
-				$this->isOfType('switch', 'switches')
429
-			) {
430
-				$wrapperClass.= ' form-switch';
431
-			}
432
-			$element = Element::create('div', $element)->class($wrapperClass)->render();
433
-		} else {
434
-			// Original way is to add the 'input' inside the 'label'
435
-			$element = Element::create('label', $field.$label)->for($attributes['id'])->class($class)->render();
436
-		}
437
-
438
-		// If BS3, if checkables are stacked, wrap them in a div with the checkable type
439
-		if (!$isInline && $this->app['former']->framework() == 'TwitterBootstrap3') {
440
-			$wrapper = Element::create('div', $element)->class($this->checkable);
441
-			if ($this->getAttribute('disabled')) {
442
-				$wrapper->addClass('disabled');
443
-			}
444
-			$element = $wrapper->render();
445
-		}
446
-
447
-		// Return the field
448
-		return $element;
449
-	}
450
-
451
-	////////////////////////////////////////////////////////////////////
452
-	///////////////////////////// HELPERS //////////////////////////////
453
-	////////////////////////////////////////////////////////////////////
454
-
455
-	/**
456
-	 * Generate an unique ID for a field
457
-	 *
458
-	 * @param string $name The field's name
459
-	 *
460
-	 * @return string A field number to use
461
-	 */
462
-	protected function unique($name)
463
-	{
464
-		$this->app['former']->labels[] = $name;
465
-
466
-		// Count number of fields with the same ID
467
-		$where  = array_filter($this->app['former']->labels, function ($label) use ($name) {
468
-			return $label == $name;
469
-		});
470
-		$unique = sizeof($where);
471
-
472
-		// In case the field doesn't need to be numbered
473
-		if ($unique < 2 or empty($this->items)) {
474
-			return false;
475
-		}
476
-
477
-		return $unique;
478
-	}
479
-
480
-	/**
481
-	 * Set something on the currently focused checkable
482
-	 *
483
-	 * @param string $attribute The key to set
484
-	 * @param string $value     Its value
485
-	 *
486
-	 * @return $this|bool
487
-	 */
488
-	protected function setOnFocused($attribute, $value)
489
-	{
490
-		if (is_null($this->focus)) {
491
-			return false;
492
-		}
493
-
494
-		$this->items[$this->focus] = Arr::set($this->items[$this->focus], $attribute, $value);
495
-
496
-		return $this;
497
-	}
498
-
499
-	/**
500
-	 * Check if a checkable is checked
501
-	 *
502
-	 * @return boolean Checked or not
503
-	 */
504
-	protected function isChecked($item = null, $value = null)
505
-	{
506
-		if (isset($item['name'])) {
507
-			$name = $item['name'];
508
-		}
509
-		if (empty($name)) {
510
-			$name = $this->name;
511
-		}
512
-
513
-		// If it's a checkbox, see if we marqued that one as checked in the array
514
-		// Or if it's a single radio, simply see if we called check
515
-		if ($this->isCheckbox() or
516
-			!$this->isCheckbox() and !$this->items
517
-		) {
518
-			$checked = Arr::get($this->checked, $name, false);
519
-
520
-			// If there are multiple, search for the value
521
-			// as the name are the same between radios
522
-		} else {
523
-			$checked = Arr::get($this->checked, $value, false);
524
-		}
525
-
526
-		// Check the values and POST array
527
-		if ($this->isGrouped()) {
528
-			// The group index. (e.g. 'bar' if the item name is foo[bar], or the item index for foo[])
529
-			$groupIndex = self::getGroupIndexFromItem($item);
530
-
531
-			// Search using the bare name, not the individual item name
532
-			$post   = $this->app['former']->getPost($this->name);
533
-			$static = $this->app['former']->getValue($this->bind ?: $this->name);
534
-
535
-			if (isset($post[$groupIndex])) {
536
-				$post = $post[$groupIndex];
537
-			}
538
-
539
-			/**
540
-			 * Support for Laravel Collection repopulating for grouped checkboxes. Note that the groupIndex must
541
-			 * match the value in order for the checkbox to be considered checked, e.g.:
542
-			 *
543
-			 *  array(
544
-			 *    'name' = 'roles[foo]',
545
-			 *    'value' => 'foo',
546
-			 *  )
547
-			 */
548
-			if ($static instanceof Collection) {
549
-				// If the repopulate value is a collection, search for an item matching the $groupIndex
550
-				foreach ($static as $staticItem) {
551
-					$staticItemValue = method_exists($staticItem, 'getKey') ? $staticItem->getKey() : $staticItem;
552
-					if ($staticItemValue == $groupIndex) {
553
-						$static = $staticItemValue;
554
-						break;
555
-					}
556
-				}
557
-			} else if (isset($static[$groupIndex])) {
558
-				$static = $static[$groupIndex];
559
-			}
560
-		} else {
561
-			$post   = $this->app['former']->getPost($name);
562
-			$static = $this->app['former']->getValue($this->bind ?: $name);
563
-		}
564
-
565
-		if (!is_null($post) and $post !== $this->app['former']->getOption('unchecked_value')) {
566
-			$isChecked = ($post == $value);
567
-		} elseif (!is_null($static) && !(is_array($static) && empty($static))) {
568
-			$isChecked = ($static == $value);
569
-		} else {
570
-			$isChecked = $checked;
571
-		}
572
-
573
-		return $isChecked ? true : false;
574
-	}
575
-
576
-	/**
577
-	 * Check if the current element is a checkbox
578
-	 *
579
-	 * @return boolean Checkbox or radio
580
-	 */
581
-	protected function isCheckbox()
582
-	{
583
-		return $this->checkable == 'checkbox';
584
-	}
585
-
586
-	/**
587
-	 * Check if the checkables are grouped or not
588
-	 *
589
-	 * @return boolean
590
-	 */
591
-	protected function isGrouped()
592
-	{
593
-		return
594
-			$this->grouped == true or
595
-			strpos($this->name, '[]') !== false;
596
-	}
597
-
598
-	/**
599
-	 * @param array $item The item array, containing at least name and count keys.
600
-	 *
601
-	 * @return mixed The group index. (e.g. returns bar if the item name is foo[bar], or the item count for foo[])
602
-	 */
603
-	public static function getGroupIndexFromItem($item)
604
-	{
605
-		$groupIndex = preg_replace('/^.*?\[(.*)\]$/', '$1', $item['name']);
606
-		if (empty($groupIndex) or $groupIndex == $item['name']) {
607
-			return $item['count'];
608
-		}
609
-
610
-		return $groupIndex;
611
-	}
17
+    /**
18
+     * Renders the checkables as inline
19
+     *
20
+     * @var boolean
21
+     */
22
+    protected $inline = false;
23
+
24
+    /**
25
+     * Add a text to a single element
26
+     *
27
+     * @var string
28
+     */
29
+    protected $text = null;
30
+
31
+    /**
32
+     * Renders the checkables as grouped
33
+     *
34
+     * @var boolean
35
+     */
36
+    protected $grouped = false;
37
+
38
+    /**
39
+     * The checkable items currently stored
40
+     *
41
+     * @var array
42
+     */
43
+    protected $items = array();
44
+
45
+    /**
46
+     * The type of checkable item
47
+     *
48
+     * @var string
49
+     */
50
+    protected $checkable = null;
51
+
52
+    /**
53
+     * An array of checked items
54
+     *
55
+     * @var array
56
+     */
57
+    protected $checked = array();
58
+
59
+    /**
60
+     * The checkable currently being focused on
61
+     *
62
+     * @var integer
63
+     */
64
+    protected $focus = null;
65
+
66
+    /**
67
+     * Whether this particular checkable is to be pushed
68
+     *
69
+     * @var boolean
70
+     */
71
+    protected $isPushed = null;
72
+
73
+    ////////////////////////////////////////////////////////////////////
74
+    //////////////////////////// CORE METHODS //////////////////////////
75
+    ////////////////////////////////////////////////////////////////////
76
+
77
+    /**
78
+     * Build a new checkable
79
+     *
80
+     * @param Container $app
81
+     * @param string    $type
82
+     * @param array     $name
83
+     * @param           $label
84
+     * @param           $value
85
+     * @param           $attributes
86
+     */
87
+    public function __construct(Container $app, $type, $name, $label, $value, $attributes)
88
+    {
89
+        // Unify auto and chained methods of grouping checkboxes
90
+        if (Str::endsWith($name, '[]')) {
91
+            $name = substr($name, 0, -2);
92
+            $this->grouped();
93
+        }
94
+        parent::__construct($app, $type, $name, $label, $value, $attributes);
95
+
96
+        if (is_array($this->value)) {
97
+            $this->items($this->value);
98
+        }
99
+    }
100
+
101
+    /**
102
+     * Apply methods to focused checkable
103
+     *
104
+     * @param string $method
105
+     * @param array  $parameters
106
+     *
107
+     * @return $this
108
+     */
109
+    public function __call($method, $parameters)
110
+    {
111
+        $focused = $this->setOnFocused('attributes.'.$method, Arr::get($parameters, 0));
112
+        if ($focused) {
113
+            return $this;
114
+        }
115
+
116
+        return parent::__call($method, $parameters);
117
+    }
118
+
119
+    /**
120
+     * Prints out the currently stored checkables
121
+     */
122
+    public function render()
123
+    {
124
+        $html = null;
125
+
126
+        $this->setFieldClasses();
127
+
128
+        // Multiple items
129
+        if ($this->items) {
130
+            unset($this->app['former']->labels[array_search($this->name, $this->app['former']->labels)]);
131
+            foreach ($this->items as $key => $item) {
132
+                $value = $this->isCheckbox() && !$this->isGrouped() ? 1 : $key;
133
+                $html .= $this->createCheckable($item, $value);
134
+            }
135
+
136
+            return $html;
137
+        }
138
+
139
+        // Single item
140
+        return $this->createCheckable(array(
141
+            'name'  => $this->name,
142
+            'label' => $this->text,
143
+            'value' => $this->value,
144
+            'attributes' => $this->attributes,
145
+        ));
146
+    }
147
+
148
+    ////////////////////////////////////////////////////////////////////
149
+    ////////////////////////// FIELD METHODS ///////////////////////////
150
+    ////////////////////////////////////////////////////////////////////
151
+
152
+    /**
153
+     * Focus on a particular checkable
154
+     *
155
+     * @param integer $on The checkable to focus on
156
+     *
157
+     * @return $this
158
+     */
159
+    public function on($on)
160
+    {
161
+        if (!isset($this->items[$on])) {
162
+            return $this;
163
+        } else {
164
+            $this->focus = $on;
165
+        }
166
+
167
+        return $this;
168
+    }
169
+
170
+    /**
171
+     * Set the checkables as inline
172
+     */
173
+    public function inline($isInline = true)
174
+    {
175
+        $this->inline = $isInline;
176
+
177
+        return $this;
178
+    }
179
+
180
+    /**
181
+     * Set the checkables as stacked
182
+     */
183
+    public function stacked($isStacked = true)
184
+    {
185
+        $this->inline = !$isStacked;
186
+
187
+        return $this;
188
+    }
189
+
190
+    /**
191
+     * Set the checkables as grouped
192
+     */
193
+    public function grouped($isGrouped = true)
194
+    {
195
+        $this->grouped = $isGrouped;
196
+
197
+        return $this;
198
+    }
199
+
200
+    /**
201
+     * Add text to a single checkable
202
+     *
203
+     * @param  string $text The checkable label
204
+     *
205
+     * @return $this
206
+     */
207
+    public function text($text)
208
+    {
209
+        // Translate and format
210
+        $text = Helpers::translate($text);
211
+
212
+        // Apply on focused if any
213
+        $focused = $this->setOnFocused('label', $text);
214
+        if ($focused) {
215
+            return $this;
216
+        }
217
+
218
+        $this->text = $text;
219
+
220
+        return $this;
221
+    }
222
+
223
+    /**
224
+     * Push this particular checkbox
225
+     *
226
+     * @param boolean $pushed
227
+     *
228
+     * @return $this
229
+     */
230
+    public function push($pushed = true)
231
+    {
232
+        $this->isPushed = $pushed;
233
+
234
+        return $this;
235
+    }
236
+
237
+    /**
238
+     * Check a specific item
239
+     *
240
+     * @param bool|string $checked The checkable to check, or an array of checked items
241
+     *
242
+     * @return $this
243
+     */
244
+    public function check($checked = true)
245
+    {
246
+        // If we're setting all the checked items at once
247
+        if (is_array($checked)) {
248
+            $this->checked = $checked;
249
+            // Checking an item in particular
250
+        } elseif (is_string($checked) or is_int($checked)) {
251
+            $this->checked[$checked] = true;
252
+            // Only setting a single item
253
+        } else {
254
+            $this->checked[$this->name] = (bool) $checked;
255
+        }
256
+
257
+        return $this;
258
+    }
259
+
260
+    /**
261
+     * Creates a series of checkable items from query with attributes
262
+     *
263
+     * @param mixed $query
264
+     * @param mixed $text
265
+     * @param mixed $attributes
266
+     * @return $this
267
+     */
268
+    public function fromQuery($query, $text = null, $attributes = null)
269
+    {
270
+        if ($this->isGrouped()) {
271
+            // Remove any possible items added by the Populator.
272
+            $this->items = array();
273
+        }
274
+        $this->items($query, $text, $attributes);
275
+
276
+        return $this;
277
+    }
278
+
279
+    /**
280
+     * Check if the checkables are inline
281
+     *
282
+     * @return boolean
283
+     */
284
+    public function isInline()
285
+    {
286
+        return $this->inline;
287
+    }
288
+
289
+    ////////////////////////////////////////////////////////////////////
290
+    ////////////////////////// INTERNAL METHODS ////////////////////////
291
+    ////////////////////////////////////////////////////////////////////
292
+
293
+    /**
294
+     * Creates a series of checkable items
295
+     *
296
+     * @param array|Collection $_items Items to create
297
+     * @param string|function  $text The value to use as text
298
+     * @param string|array     $attributes The data to use as attributes
299
+     */
300
+    protected function items($_items, $text = null, $attributes = null)
301
+    {
302
+        // If passing an array
303
+        if (sizeof($_items) == 1 and
304
+            isset($_items[0]) and
305
+            is_array($_items[0])
306
+        ) {
307
+            $_items = $_items[0];
308
+        }
309
+
310
+        // Fetch models if that's what we were passed
311
+        if ((isset($_items[0]) and is_object($_items[0])) or ($_items instanceof Collection)) {
312
+            $_items = Helpers::queryToArray($_items, $text, $attributes);
313
+            if (is_null($text) && is_null($attributes)) {
314
+                $_items = array_flip($_items);
315
+            }
316
+        }
317
+
318
+        // Iterate through items, assign a name and a label to each
319
+        $count = 0;
320
+        foreach ($_items as $label => $name) {
321
+
322
+            // Define a fallback name in case none is found
323
+            $fallback = $this->isCheckbox()
324
+                ? $this->name.'_'.$count
325
+                : $this->name;
326
+
327
+            // Grouped fields
328
+            if ($this->isGrouped()) {
329
+                $attributes['id'] = str_replace('[]', '', $fallback);
330
+                $fallback         = str_replace('[]', '', $this->name).'[]';
331
+            }
332
+
333
+            // If we haven't any name defined for the checkable, try to compute some
334
+            if (!is_string($label) and !is_array($name)) {
335
+                $label = $name;
336
+                $name  = $fallback;
337
+            }
338
+
339
+            // If we gave custom information on the item, add them
340
+            if (is_array($name)) {
341
+                $attributes = $name;
342
+                $name       = Arr::get($attributes, 'name', $fallback);
343
+                unset($attributes['name']);
344
+            }
345
+
346
+            // Store all informations we have in an array
347
+            $item = array(
348
+                'name'  => $name,
349
+                'label' => Helpers::translate($label),
350
+                'count' => $count,
351
+            );
352
+            if (isset($attributes)) {
353
+                $item['attributes'] = $attributes;
354
+            }
355
+
356
+            $this->items[] = $item;
357
+            $count++;
358
+        }
359
+    }
360
+
361
+    /**
362
+     * Renders a checkable
363
+     *
364
+     * @param string|array $item          A checkable item
365
+     * @param integer      $fallbackValue A fallback value if none is set
366
+     *
367
+     * @return string
368
+     */
369
+    protected function createCheckable($item, $fallbackValue = 1)
370
+    {
371
+        // Extract informations
372
+        extract($item);
373
+
374
+        // Set default values
375
+        if (!isset($attributes)) {
376
+            $attributes = array();
377
+        }
378
+        if (isset($attributes['value'])) {
379
+            $value = $attributes['value'];
380
+        }
381
+        if (!isset($value) or $value === $this->app['former']->getOption('unchecked_value')) {
382
+            $value = $fallbackValue;
383
+        }
384
+
385
+        // If inline items, add class
386
+        $isInline = $this->inline ? ' '.$this->app['former.framework']->getInlineLabelClass($this) : '';
387
+
388
+        // In Bootsrap 3 or 4 or 5, don't append the the checkable type (radio/checkbox) as a class if
389
+        // rendering inline.
390
+        $class =  in_array($this->app['former']->framework(), ['TwitterBootstrap3', 'TwitterBootstrap4', 'TwitterBootstrap5']) ? trim($isInline) : $this->checkable.$isInline;
391
+
392
+        // Merge custom attributes with global attributes
393
+        $attributes = array_merge($this->attributes, $attributes);
394
+        if (!isset($attributes['id'])) {
395
+            $attributes['id'] = $name.$this->unique($name);
396
+        }
397
+
398
+        // Create field
399
+        $field = Input::create($this->checkable, $name, Helpers::encode($value), $attributes);
400
+        if ($this->isChecked($item, $value)) {
401
+            $field->checked('checked');
402
+        }
403
+
404
+        // Add hidden checkbox if requested
405
+        if ($this->isOfType('checkbox', 'checkboxes', 'switch', 'switches')) {
406
+            if ($this->isPushed or ($this->app['former']->getOption('push_checkboxes') and $this->isPushed !== false)) {
407
+                $field = $this->app['former']->hidden($name)->forceValue($this->app['former']->getOption('unchecked_value')).$field->render();
408
+
409
+                // app['former.field'] was overwritten by Former::hidden() call in the line above, so here
410
+                // we reset it to $this to enable $this->app['former']->getErrors() to retrieve the correct object
411
+                $this->app->instance('former.field', $this);
412
+            }
413
+        }
414
+
415
+        // If no label to wrap, return plain checkable
416
+        if (!$label && !$this->isOfType('switch', 'switches')) {
417
+            $element = (is_object($field)) ? $field->render() : $field;
418
+        } elseif (in_array($this->app['former']->framework(), ['TwitterBootstrap4', 'TwitterBootstrap5'])) {
419
+            $element = $field;
420
+            if ($label) {
421
+                // Revised for Bootstrap 4 and 5, move the 'input' outside of the 'label'
422
+                $labelClass = 'form-check-label';
423
+                $element .= Element::create('label', $label)->for($attributes['id'])->class($labelClass)->render();
424
+            }
425
+
426
+            $wrapperClass = $this->inline ? 'form-check form-check-inline' : 'form-check';
427
+            if ($this->app['former']->framework() === 'TwitterBootstrap5' &&
428
+                $this->isOfType('switch', 'switches')
429
+            ) {
430
+                $wrapperClass.= ' form-switch';
431
+            }
432
+            $element = Element::create('div', $element)->class($wrapperClass)->render();
433
+        } else {
434
+            // Original way is to add the 'input' inside the 'label'
435
+            $element = Element::create('label', $field.$label)->for($attributes['id'])->class($class)->render();
436
+        }
437
+
438
+        // If BS3, if checkables are stacked, wrap them in a div with the checkable type
439
+        if (!$isInline && $this->app['former']->framework() == 'TwitterBootstrap3') {
440
+            $wrapper = Element::create('div', $element)->class($this->checkable);
441
+            if ($this->getAttribute('disabled')) {
442
+                $wrapper->addClass('disabled');
443
+            }
444
+            $element = $wrapper->render();
445
+        }
446
+
447
+        // Return the field
448
+        return $element;
449
+    }
450
+
451
+    ////////////////////////////////////////////////////////////////////
452
+    ///////////////////////////// HELPERS //////////////////////////////
453
+    ////////////////////////////////////////////////////////////////////
454
+
455
+    /**
456
+     * Generate an unique ID for a field
457
+     *
458
+     * @param string $name The field's name
459
+     *
460
+     * @return string A field number to use
461
+     */
462
+    protected function unique($name)
463
+    {
464
+        $this->app['former']->labels[] = $name;
465
+
466
+        // Count number of fields with the same ID
467
+        $where  = array_filter($this->app['former']->labels, function ($label) use ($name) {
468
+            return $label == $name;
469
+        });
470
+        $unique = sizeof($where);
471
+
472
+        // In case the field doesn't need to be numbered
473
+        if ($unique < 2 or empty($this->items)) {
474
+            return false;
475
+        }
476
+
477
+        return $unique;
478
+    }
479
+
480
+    /**
481
+     * Set something on the currently focused checkable
482
+     *
483
+     * @param string $attribute The key to set
484
+     * @param string $value     Its value
485
+     *
486
+     * @return $this|bool
487
+     */
488
+    protected function setOnFocused($attribute, $value)
489
+    {
490
+        if (is_null($this->focus)) {
491
+            return false;
492
+        }
493
+
494
+        $this->items[$this->focus] = Arr::set($this->items[$this->focus], $attribute, $value);
495
+
496
+        return $this;
497
+    }
498
+
499
+    /**
500
+     * Check if a checkable is checked
501
+     *
502
+     * @return boolean Checked or not
503
+     */
504
+    protected function isChecked($item = null, $value = null)
505
+    {
506
+        if (isset($item['name'])) {
507
+            $name = $item['name'];
508
+        }
509
+        if (empty($name)) {
510
+            $name = $this->name;
511
+        }
512
+
513
+        // If it's a checkbox, see if we marqued that one as checked in the array
514
+        // Or if it's a single radio, simply see if we called check
515
+        if ($this->isCheckbox() or
516
+            !$this->isCheckbox() and !$this->items
517
+        ) {
518
+            $checked = Arr::get($this->checked, $name, false);
519
+
520
+            // If there are multiple, search for the value
521
+            // as the name are the same between radios
522
+        } else {
523
+            $checked = Arr::get($this->checked, $value, false);
524
+        }
525
+
526
+        // Check the values and POST array
527
+        if ($this->isGrouped()) {
528
+            // The group index. (e.g. 'bar' if the item name is foo[bar], or the item index for foo[])
529
+            $groupIndex = self::getGroupIndexFromItem($item);
530
+
531
+            // Search using the bare name, not the individual item name
532
+            $post   = $this->app['former']->getPost($this->name);
533
+            $static = $this->app['former']->getValue($this->bind ?: $this->name);
534
+
535
+            if (isset($post[$groupIndex])) {
536
+                $post = $post[$groupIndex];
537
+            }
538
+
539
+            /**
540
+             * Support for Laravel Collection repopulating for grouped checkboxes. Note that the groupIndex must
541
+             * match the value in order for the checkbox to be considered checked, e.g.:
542
+             *
543
+             *  array(
544
+             *    'name' = 'roles[foo]',
545
+             *    'value' => 'foo',
546
+             *  )
547
+             */
548
+            if ($static instanceof Collection) {
549
+                // If the repopulate value is a collection, search for an item matching the $groupIndex
550
+                foreach ($static as $staticItem) {
551
+                    $staticItemValue = method_exists($staticItem, 'getKey') ? $staticItem->getKey() : $staticItem;
552
+                    if ($staticItemValue == $groupIndex) {
553
+                        $static = $staticItemValue;
554
+                        break;
555
+                    }
556
+                }
557
+            } else if (isset($static[$groupIndex])) {
558
+                $static = $static[$groupIndex];
559
+            }
560
+        } else {
561
+            $post   = $this->app['former']->getPost($name);
562
+            $static = $this->app['former']->getValue($this->bind ?: $name);
563
+        }
564
+
565
+        if (!is_null($post) and $post !== $this->app['former']->getOption('unchecked_value')) {
566
+            $isChecked = ($post == $value);
567
+        } elseif (!is_null($static) && !(is_array($static) && empty($static))) {
568
+            $isChecked = ($static == $value);
569
+        } else {
570
+            $isChecked = $checked;
571
+        }
572
+
573
+        return $isChecked ? true : false;
574
+    }
575
+
576
+    /**
577
+     * Check if the current element is a checkbox
578
+     *
579
+     * @return boolean Checkbox or radio
580
+     */
581
+    protected function isCheckbox()
582
+    {
583
+        return $this->checkable == 'checkbox';
584
+    }
585
+
586
+    /**
587
+     * Check if the checkables are grouped or not
588
+     *
589
+     * @return boolean
590
+     */
591
+    protected function isGrouped()
592
+    {
593
+        return
594
+            $this->grouped == true or
595
+            strpos($this->name, '[]') !== false;
596
+    }
597
+
598
+    /**
599
+     * @param array $item The item array, containing at least name and count keys.
600
+     *
601
+     * @return mixed The group index. (e.g. returns bar if the item name is foo[bar], or the item count for foo[])
602
+     */
603
+    public static function getGroupIndexFromItem($item)
604
+    {
605
+        $groupIndex = preg_replace('/^.*?\[(.*)\]$/', '$1', $item['name']);
606
+        if (empty($groupIndex) or $groupIndex == $item['name']) {
607
+            return $item['count'];
608
+        }
609
+
610
+        return $groupIndex;
611
+    }
612 612
 }
Please login to merge, or discard this patch.
src/Former/Helpers.php 1 patch
Indentation   +195 added lines, -195 removed lines patch added patch discarded remove patch
@@ -9,199 +9,199 @@
 block discarded – undo
9 9
  */
10 10
 class Helpers
11 11
 {
12
-	/**
13
-	 * The IoC Container
14
-	 *
15
-	 * @var Container
16
-	 */
17
-	protected static $app;
18
-
19
-	/**
20
-	 * Bind a Container to the Helpers class
21
-	 *
22
-	 * @param Container $app
23
-	 */
24
-	public static function setApp(Container $app)
25
-	{
26
-		static::$app = $app;
27
-	}
28
-
29
-	/**
30
-	 * Encodes HTML
31
-	 *
32
-	 * @param string|array|null $value The string to encode
33
-	 *
34
-	 * @return string
35
-	 */
36
-	public static function encode($value)
37
-	{
38
-		if ($value === null) {
39
-			return '';
40
-		}
41
-
42
-		if (is_array($value)) {
43
-			$value = '';
44
-		}
45
-
46
-		return htmlentities($value, ENT_QUOTES, 'UTF-8', true);
47
-	}
48
-
49
-	////////////////////////////////////////////////////////////////////
50
-	///////////////////////// LOCALIZATION HELPERS /////////////////////
51
-	////////////////////////////////////////////////////////////////////
52
-
53
-	/**
54
-	 * Translates a string by trying several fallbacks
55
-	 *
56
-	 * @param  string $key      The key to translate
57
-	 * @param  string $fallback The ultimate fallback
58
-	 *
59
-	 * @return string           A translated string
60
-	 */
61
-	public static function translate($key, $fallback = null)
62
-	{
63
-		// If nothing was given, return nothing, bitch
64
-		if (!$key) {
65
-			return $fallback;
66
-		}
67
-
68
-		// If no fallback, use the key
69
-		if (!$fallback) {
70
-			$fallback = $key;
71
-		}
72
-
73
-		// Assure we don't already have a Lang object
74
-		if (is_object($key) and method_exists($key, 'get')) {
75
-			return $key->get();
76
-		}
77
-
78
-		$translation   = null;
79
-		$translateFrom = static::$app['former']->getOption('translate_from');
80
-		if (substr($translateFrom, -1) !== '/') {
81
-			$translateFrom .= '.';
82
-		}
83
-		$translateFrom .= $key;
84
-
85
-		// Convert a[b[c]] to a.b.c for nested translations [a => [b => 'B!']]
86
-		if (strpos($translateFrom, ']') !== false) {
87
-			$translateFrom = str_replace(array(']', '['), array('', '.'), $translateFrom);
88
-		}
89
-
90
-		// Search for the key itself, if it is valid
91
-		$validKey = preg_match("/^[a-z0-9_-]+([.][a-z0-9 _-]+)+$/i", $key) === 1;
92
-		if ($validKey && static::$app['translator']->has($key)) {
93
-			$translation = static::$app['translator']->get($key);
94
-		} elseif (static::$app['translator']->has($translateFrom)) {
95
-			$translation = static::$app['translator']->get($translateFrom);
96
-		}
97
-
98
-		// Replace by fallback if invalid
99
-		if (!$translation or is_array($translation)) {
100
-			$translation = $fallback;
101
-		}
102
-
103
-		// Capitalize
104
-		$capitalize = static::$app['former']->getOption('capitalize_translations');
105
-
106
-		return $capitalize ? ucfirst($translation) : $translation;
107
-	}
108
-
109
-	////////////////////////////////////////////////////////////////////
110
-	////////////////////////// DATABASE HELPERS ////////////////////////
111
-	////////////////////////////////////////////////////////////////////
112
-
113
-	/**
114
-	 * Transforms an array of models into an associative array
115
-	 *
116
-	 * @param  array|object    $query      The array of results
117
-	 * @param  string|function $text       The value to use as text
118
-	 * @param  string|array    $attributes The data to use as attributes
119
-	 *
120
-	 * @return array               A data array
121
-	 */
122
-	public static function queryToArray($query, $text = null, $attributes = null)
123
-	{
124
-		// Automatically fetch Lang objects for people who store translated options lists
125
-		// Same of unfetched queries
126
-		if (!$query instanceof Collection) {
127
-			if ((is_object($query) || is_string($query)) && method_exists($query, 'get')) {
128
-				$query = $query->get();
129
-			}
130
-			if (!is_array($query)) {
131
-				$query = (array) $query;
132
-			}
133
-		}
134
-
135
-		//Convert parametrs of old format to new format
136
-		if (!is_callable($text)) {
137
-			$optionTextValue = $text;
138
-			$text = function ($model) use ($optionTextValue) {
139
-				if ($optionTextValue and isset($model->$optionTextValue)) {
140
-					return $model->$optionTextValue;
141
-				} elseif ((is_object($model) || is_string($model)) && method_exists($model, '__toString')) {
142
-					return  $model->__toString();
143
-				} else {
144
-					return null;
145
-				}
146
-			};
147
-		}
148
-
149
-		if (!is_array($attributes)) {
150
-			if (is_string($attributes)) {
151
-				$attributes = ['value' => $attributes];
152
-			} else {
153
-				$attributes = ['value' => null];
154
-			}
155
-		}
156
-
157
-		if (!isset($attributes['value'])) {
158
-			$attributes['value'] = null;
159
-		}
160
-		//-------------------------------------------------
161
-
162
-		// Populates the new options
163
-		foreach ($query as $model) {
164
-			// If it's an array, convert to object
165
-			if (is_array($model)) {
166
-				$model = (object) $model;
167
-			}
168
-
169
-			// Calculate option text
170
-			$optionText = $text($model);
171
-
172
-			// Skip if no text value found
173
-			if (!$optionText) {
174
-				continue;
175
-			}
176
-
177
-			//Collect option attributes
178
-			foreach ($attributes as $optionAttributeName => $modelAttributeName) {
179
-				if (is_callable($modelAttributeName)) {
180
-					$optionAttributeValue = $modelAttributeName($model);
181
-				} elseif ($modelAttributeName and isset($model->$modelAttributeName)) {
182
-					$optionAttributeValue = $model->$modelAttributeName;
183
-				} elseif($optionAttributeName === 'value') {
184
-					//For backward compatibility
185
-					if (method_exists($model, 'getKey')) {
186
-						$optionAttributeValue = $model->getKey();
187
-					} elseif (isset($model->id)) {
188
-						$optionAttributeValue = $model->id;
189
-					} else {
190
-						$optionAttributeValue = $optionText;
191
-					}
192
-				} else {
193
-					$optionAttributeValue = '';
194
-				}
195
-
196
-				//For backward compatibility
197
-				if (count($attributes) === 1) {
198
-					$array[$optionAttributeValue] = (string) $optionText;
199
-				} else {
200
-					$array[$optionText][$optionAttributeName] = (string) $optionAttributeValue;
201
-				}
202
-			}
203
-		}
204
-
205
-		return !empty($array) ? $array : $query;
206
-	}
12
+    /**
13
+     * The IoC Container
14
+     *
15
+     * @var Container
16
+     */
17
+    protected static $app;
18
+
19
+    /**
20
+     * Bind a Container to the Helpers class
21
+     *
22
+     * @param Container $app
23
+     */
24
+    public static function setApp(Container $app)
25
+    {
26
+        static::$app = $app;
27
+    }
28
+
29
+    /**
30
+     * Encodes HTML
31
+     *
32
+     * @param string|array|null $value The string to encode
33
+     *
34
+     * @return string
35
+     */
36
+    public static function encode($value)
37
+    {
38
+        if ($value === null) {
39
+            return '';
40
+        }
41
+
42
+        if (is_array($value)) {
43
+            $value = '';
44
+        }
45
+
46
+        return htmlentities($value, ENT_QUOTES, 'UTF-8', true);
47
+    }
48
+
49
+    ////////////////////////////////////////////////////////////////////
50
+    ///////////////////////// LOCALIZATION HELPERS /////////////////////
51
+    ////////////////////////////////////////////////////////////////////
52
+
53
+    /**
54
+     * Translates a string by trying several fallbacks
55
+     *
56
+     * @param  string $key      The key to translate
57
+     * @param  string $fallback The ultimate fallback
58
+     *
59
+     * @return string           A translated string
60
+     */
61
+    public static function translate($key, $fallback = null)
62
+    {
63
+        // If nothing was given, return nothing, bitch
64
+        if (!$key) {
65
+            return $fallback;
66
+        }
67
+
68
+        // If no fallback, use the key
69
+        if (!$fallback) {
70
+            $fallback = $key;
71
+        }
72
+
73
+        // Assure we don't already have a Lang object
74
+        if (is_object($key) and method_exists($key, 'get')) {
75
+            return $key->get();
76
+        }
77
+
78
+        $translation   = null;
79
+        $translateFrom = static::$app['former']->getOption('translate_from');
80
+        if (substr($translateFrom, -1) !== '/') {
81
+            $translateFrom .= '.';
82
+        }
83
+        $translateFrom .= $key;
84
+
85
+        // Convert a[b[c]] to a.b.c for nested translations [a => [b => 'B!']]
86
+        if (strpos($translateFrom, ']') !== false) {
87
+            $translateFrom = str_replace(array(']', '['), array('', '.'), $translateFrom);
88
+        }
89
+
90
+        // Search for the key itself, if it is valid
91
+        $validKey = preg_match("/^[a-z0-9_-]+([.][a-z0-9 _-]+)+$/i", $key) === 1;
92
+        if ($validKey && static::$app['translator']->has($key)) {
93
+            $translation = static::$app['translator']->get($key);
94
+        } elseif (static::$app['translator']->has($translateFrom)) {
95
+            $translation = static::$app['translator']->get($translateFrom);
96
+        }
97
+
98
+        // Replace by fallback if invalid
99
+        if (!$translation or is_array($translation)) {
100
+            $translation = $fallback;
101
+        }
102
+
103
+        // Capitalize
104
+        $capitalize = static::$app['former']->getOption('capitalize_translations');
105
+
106
+        return $capitalize ? ucfirst($translation) : $translation;
107
+    }
108
+
109
+    ////////////////////////////////////////////////////////////////////
110
+    ////////////////////////// DATABASE HELPERS ////////////////////////
111
+    ////////////////////////////////////////////////////////////////////
112
+
113
+    /**
114
+     * Transforms an array of models into an associative array
115
+     *
116
+     * @param  array|object    $query      The array of results
117
+     * @param  string|function $text       The value to use as text
118
+     * @param  string|array    $attributes The data to use as attributes
119
+     *
120
+     * @return array               A data array
121
+     */
122
+    public static function queryToArray($query, $text = null, $attributes = null)
123
+    {
124
+        // Automatically fetch Lang objects for people who store translated options lists
125
+        // Same of unfetched queries
126
+        if (!$query instanceof Collection) {
127
+            if ((is_object($query) || is_string($query)) && method_exists($query, 'get')) {
128
+                $query = $query->get();
129
+            }
130
+            if (!is_array($query)) {
131
+                $query = (array) $query;
132
+            }
133
+        }
134
+
135
+        //Convert parametrs of old format to new format
136
+        if (!is_callable($text)) {
137
+            $optionTextValue = $text;
138
+            $text = function ($model) use ($optionTextValue) {
139
+                if ($optionTextValue and isset($model->$optionTextValue)) {
140
+                    return $model->$optionTextValue;
141
+                } elseif ((is_object($model) || is_string($model)) && method_exists($model, '__toString')) {
142
+                    return  $model->__toString();
143
+                } else {
144
+                    return null;
145
+                }
146
+            };
147
+        }
148
+
149
+        if (!is_array($attributes)) {
150
+            if (is_string($attributes)) {
151
+                $attributes = ['value' => $attributes];
152
+            } else {
153
+                $attributes = ['value' => null];
154
+            }
155
+        }
156
+
157
+        if (!isset($attributes['value'])) {
158
+            $attributes['value'] = null;
159
+        }
160
+        //-------------------------------------------------
161
+
162
+        // Populates the new options
163
+        foreach ($query as $model) {
164
+            // If it's an array, convert to object
165
+            if (is_array($model)) {
166
+                $model = (object) $model;
167
+            }
168
+
169
+            // Calculate option text
170
+            $optionText = $text($model);
171
+
172
+            // Skip if no text value found
173
+            if (!$optionText) {
174
+                continue;
175
+            }
176
+
177
+            //Collect option attributes
178
+            foreach ($attributes as $optionAttributeName => $modelAttributeName) {
179
+                if (is_callable($modelAttributeName)) {
180
+                    $optionAttributeValue = $modelAttributeName($model);
181
+                } elseif ($modelAttributeName and isset($model->$modelAttributeName)) {
182
+                    $optionAttributeValue = $model->$modelAttributeName;
183
+                } elseif($optionAttributeName === 'value') {
184
+                    //For backward compatibility
185
+                    if (method_exists($model, 'getKey')) {
186
+                        $optionAttributeValue = $model->getKey();
187
+                    } elseif (isset($model->id)) {
188
+                        $optionAttributeValue = $model->id;
189
+                    } else {
190
+                        $optionAttributeValue = $optionText;
191
+                    }
192
+                } else {
193
+                    $optionAttributeValue = '';
194
+                }
195
+
196
+                //For backward compatibility
197
+                if (count($attributes) === 1) {
198
+                    $array[$optionAttributeValue] = (string) $optionText;
199
+                } else {
200
+                    $array[$optionText][$optionAttributeName] = (string) $optionAttributeValue;
201
+                }
202
+            }
203
+        }
204
+
205
+        return !empty($array) ? $array : $query;
206
+    }
207 207
 }
Please login to merge, or discard this patch.
src/Former/Form/Fields/Select.php 1 patch
Indentation   +322 added lines, -322 removed lines patch added patch discarded remove patch
@@ -13,326 +13,326 @@
 block discarded – undo
13 13
 class Select extends Field
14 14
 {
15 15
 
16
-	/**
17
-	 * The select's placeholder
18
-	 *
19
-	 * @var string
20
-	 */
21
-	private $placeholder = null;
22
-
23
-	/**
24
-	 * The Select's options
25
-	 *
26
-	 * @var array
27
-	 */
28
-	protected $options;
29
-
30
-	/**
31
-	 * The select's element
32
-	 *
33
-	 * @var string
34
-	 */
35
-	protected $element = 'select';
36
-
37
-	/**
38
-	 * The select's self-closing state
39
-	 *
40
-	 * @var boolean
41
-	 */
42
-	protected $isSelfClosing = false;
43
-
44
-	////////////////////////////////////////////////////////////////////
45
-	/////////////////////////// CORE METHODS ///////////////////////////
46
-	////////////////////////////////////////////////////////////////////
47
-
48
-	/**
49
-	 * Easier arguments order for selects
50
-	 *
51
-	 * @param Container $app        The Container instance
52
-	 * @param string    $type       select
53
-	 * @param string    $name       Field name
54
-	 * @param string    $label      Its label
55
-	 * @param array     $options    The select's options
56
-	 * @param string    $selected   The selected option
57
-	 * @param array     $attributes Attributes
58
-	 */
59
-	public function __construct(Container $app, $type, $name, $label, $options, $selected, $attributes)
60
-	{
61
-		if ($selected) {
62
-			$this->value = $selected;
63
-		}
64
-		if ($options) {
65
-			$this->options($options);
66
-		}
67
-
68
-		parent::__construct($app, $type, $name, $label, $selected, $attributes);
69
-
70
-		// Nested models population
71
-		if (Str::contains($this->name, '.') and is_array($this->value) and !empty($this->value) and is_string($this->value[key($this->value)])) {
72
-			$this->fromQuery($this->value);
73
-			$this->value = $selected ?: null;
74
-		}
75
-	}
76
-
77
-	/**
78
-	 * Renders the select
79
-	 *
80
-	 * @return string A <select> tag
81
-	 */
82
-	public function render()
83
-	{
84
-		// Multiselects
85
-		if ($this->isOfType('multiselect')) {
86
-			if (!isset($this->attributes['id'])) {
87
-				$this->setAttribute('id', $this->name);
88
-			}
89
-
90
-			$this->multiple();
91
-			$this->name .= '[]';
92
-		}
93
-
94
-		if ( ! $this->value instanceOf \ArrayAccess) {
95
-			$this->value = (array) $this->value;
96
-		}
97
-
98
-		$this->clearSelected();
99
-
100
-		// Mark selected values as selected
101
-		if ($this->hasChildren() and !empty($this->value)) {
102
-			foreach ($this->value as $value) {
103
-				if (is_object($value) && method_exists($value, 'getKey')) {
104
-					$value = $value->getKey();
105
-				}
106
-				$this->selectValue($value);
107
-			}
108
-		}
109
-
110
-		// Add placeholder text if any
111
-		if ($placeholder = $this->getPlaceholder()) {
112
-			array_unshift($this->children, $placeholder);
113
-		}
114
-
115
-		$this->value = null;
116
-
117
-		return parent::render();
118
-	}
119
-
120
-	/**
121
-	 * Select a value in the field's children
122
-	 *
123
-	 * @param mixed   $value
124
-	 * @param Element $parent
125
-	 *
126
-	 * @return void
127
-	 */
128
-	protected function selectValue($value, $parent = null)
129
-	{
130
-		// If no parent element defined, use direct children
131
-		if (!$parent) {
132
-			$parent = $this;
133
-		}
134
-
135
-		foreach ($parent->getChildren() as $child) {
136
-			$optionValue = $child->getAttribute('value');
137
-
138
-			if ($optionValue === $value || (is_numeric($value) && is_numeric($optionValue) && (int)$optionValue === (int)$value)) {
139
-				$child->selected('selected');
140
-			}
141
-
142
-			// Else iterate over subchilds
143
-			if ($child->hasChildren()) {
144
-				$this->selectValue($value, $child);
145
-			}
146
-		}
147
-	}
148
-
149
-	/**
150
-	 * Get the Select's placeholder
151
-	 *
152
-	 * @return Element
153
-	 */
154
-	protected function getPlaceholder()
155
-	{
156
-		if (!$this->placeholder) {
157
-			return false;
158
-		}
159
-
160
-		$attributes = array('value' => '', 'disabled' => 'disabled');
161
-		if (!$this->value) {
162
-			$attributes['selected'] = 'selected';
163
-		}
164
-
165
-		return Element::create('option', $this->placeholder, $attributes);
166
-	}
167
-
168
-	////////////////////////////////////////////////////////////////////
169
-	////////////////////////// FIELD METHODS ///////////////////////////
170
-	////////////////////////////////////////////////////////////////////
171
-
172
-	/**
173
-	 * Set the select options
174
-	 *
175
-	 * @param  array   $_options     The options as an array
176
-	 * @param  mixed   $selected     Facultative selected entry
177
-	 * @param  boolean $valuesAsKeys Whether the array's values should be used as
178
-	 *                               the option's values instead of the array's keys
179
-	 */
180
-	public function options($_options, $selected = null, $valuesAsKeys = false)
181
-	{
182
-		$options = array();
183
-
184
-		// If valuesAsKeys is true, use the values as keys
185
-		if ($valuesAsKeys) {
186
-			foreach ($_options as $v) {
187
-				$options[$v] = $v;
188
-			}
189
-		} else {
190
-			$options = $_options;
191
-		}
192
-
193
-		// Add the various options
194
-		foreach ($options as $value => $text) {
195
-			if (is_array($text) and isset($text['value'])) {
196
-				$attributes = $text;
197
-				$text       = $value;
198
-				$value      = null;
199
-			} else {
200
-				$attributes = array();
201
-			}
202
-			$this->addOption($text, $value, $attributes);
203
-		}
204
-
205
-		// Set the selected value
206
-		if (!is_null($selected)) {
207
-			$this->select($selected);
208
-		}
209
-
210
-		return $this;
211
-	}
212
-
213
-	/**
214
-	 * Creates a list of options from a range
215
-	 *
216
-	 * @param  integer $from
217
-	 * @param  integer $to
218
-	 * @param  integer $step
219
-	 */
220
-	public function range($from, $to, $step = 1)
221
-	{
222
-		$range = range($from, $to, $step);
223
-		$this->options($range, null, true);
224
-
225
-		return $this;
226
-	}
227
-
228
-	/**
229
-	 * Add an option to the Select's options
230
-	 *
231
-	 * @param array|string $text       It's value or an array of values
232
-	 * @param string       $value      It's text
233
-	 * @param array        $attributes The option's attributes
234
-	 */
235
-	public function addOption($text = null, $value = null, $attributes = array())
236
-	{
237
-		// Get the option's value
238
-		$childrenKey = !is_null($value) ? $value : sizeof($this->children);
239
-
240
-		// If we passed an options group
241
-		if (is_array($text)) {
242
-			$this->children[$childrenKey] = Element::create('optgroup')->label($value);
243
-			foreach ($text as $key => $value) {
244
-				if (is_array($value)) {
245
-					$option = Element::create('option', $key)->setAttributes($value);
246
-				} else {
247
-					$option = Element::create('option', $value)->setAttribute('value', $key);
248
-				}
249
-				$this->children[$childrenKey]->nest($option);
250
-			}
251
-			// Else if it's a simple option
252
-		} else {
253
-			if (!isset($attributes['value'])) {
254
-				$attributes['value'] = $value;
255
-			}
256
-
257
-			$this->children[$attributes['value']] = Element::create('option', $text)->setAttributes($attributes);
258
-		}
259
-
260
-		return $this;
261
-	}
262
-
263
-	/**
264
-	 * Clear selected attribute for select options
265
-	 *
266
-	 * @param Element $parent
267
-	 *
268
-	 * @return void
269
-	 */
270
-	public function clearSelected($parent = null)
271
-	{
272
-		// If no parent element defined, use direct children
273
-		if (!$parent) {
274
-			$parent = $this;
275
-		}
276
-
277
-		foreach ($parent->getChildren() as $child) {
278
-			$child->removeAttribute('selected');
279
-
280
-			if ($child->hasChildren()) {
281
-				$this->clearSelected($child);
282
-			}
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * Use the results from a Fluent/Eloquent query as options
288
-	 *
289
-	 * @param  array           $results    An array of Eloquent models
290
-	 * @param  string|function $text       The value to use as text
291
-	 * @param  string|array    $attributes The data to use as attributes
292
-	 * @param  string	   $selected   The default selected item
293
-	 */
294
-	public function fromQuery($results, $text = null, $attributes = null, $selected = null)
295
-	{
296
-		$this->options(Helpers::queryToArray($results, $text, $attributes), $selected);
297
-
298
-		return $this;
299
-	}
300
-
301
-	/**
302
-	 * Select a particular list item
303
-	 *
304
-	 * @param  mixed $selected Selected item
305
-	 */
306
-	public function select($selected)
307
-	{
308
-		$this->value = $selected;
309
-
310
-		return $this;
311
-	}
312
-
313
-	/**
314
-	 * Add a placeholder to the current select
315
-	 *
316
-	 * @param  string $placeholder The placeholder text
317
-	 */
318
-	public function placeholder($placeholder)
319
-	{
320
-		$this->placeholder = Helpers::translate($placeholder);
321
-
322
-		return $this;
323
-	}
324
-
325
-	////////////////////////////////////////////////////////////////////
326
-	////////////////////////////// HELPERS /////////////////////////////
327
-	////////////////////////////////////////////////////////////////////
328
-
329
-	/**
330
-	 * Returns the current options in memory for manipulations
331
-	 *
332
-	 * @return array The current options array
333
-	 */
334
-	public function getOptions()
335
-	{
336
-		return $this->children;
337
-	}
16
+    /**
17
+     * The select's placeholder
18
+     *
19
+     * @var string
20
+     */
21
+    private $placeholder = null;
22
+
23
+    /**
24
+     * The Select's options
25
+     *
26
+     * @var array
27
+     */
28
+    protected $options;
29
+
30
+    /**
31
+     * The select's element
32
+     *
33
+     * @var string
34
+     */
35
+    protected $element = 'select';
36
+
37
+    /**
38
+     * The select's self-closing state
39
+     *
40
+     * @var boolean
41
+     */
42
+    protected $isSelfClosing = false;
43
+
44
+    ////////////////////////////////////////////////////////////////////
45
+    /////////////////////////// CORE METHODS ///////////////////////////
46
+    ////////////////////////////////////////////////////////////////////
47
+
48
+    /**
49
+     * Easier arguments order for selects
50
+     *
51
+     * @param Container $app        The Container instance
52
+     * @param string    $type       select
53
+     * @param string    $name       Field name
54
+     * @param string    $label      Its label
55
+     * @param array     $options    The select's options
56
+     * @param string    $selected   The selected option
57
+     * @param array     $attributes Attributes
58
+     */
59
+    public function __construct(Container $app, $type, $name, $label, $options, $selected, $attributes)
60
+    {
61
+        if ($selected) {
62
+            $this->value = $selected;
63
+        }
64
+        if ($options) {
65
+            $this->options($options);
66
+        }
67
+
68
+        parent::__construct($app, $type, $name, $label, $selected, $attributes);
69
+
70
+        // Nested models population
71
+        if (Str::contains($this->name, '.') and is_array($this->value) and !empty($this->value) and is_string($this->value[key($this->value)])) {
72
+            $this->fromQuery($this->value);
73
+            $this->value = $selected ?: null;
74
+        }
75
+    }
76
+
77
+    /**
78
+     * Renders the select
79
+     *
80
+     * @return string A <select> tag
81
+     */
82
+    public function render()
83
+    {
84
+        // Multiselects
85
+        if ($this->isOfType('multiselect')) {
86
+            if (!isset($this->attributes['id'])) {
87
+                $this->setAttribute('id', $this->name);
88
+            }
89
+
90
+            $this->multiple();
91
+            $this->name .= '[]';
92
+        }
93
+
94
+        if ( ! $this->value instanceOf \ArrayAccess) {
95
+            $this->value = (array) $this->value;
96
+        }
97
+
98
+        $this->clearSelected();
99
+
100
+        // Mark selected values as selected
101
+        if ($this->hasChildren() and !empty($this->value)) {
102
+            foreach ($this->value as $value) {
103
+                if (is_object($value) && method_exists($value, 'getKey')) {
104
+                    $value = $value->getKey();
105
+                }
106
+                $this->selectValue($value);
107
+            }
108
+        }
109
+
110
+        // Add placeholder text if any
111
+        if ($placeholder = $this->getPlaceholder()) {
112
+            array_unshift($this->children, $placeholder);
113
+        }
114
+
115
+        $this->value = null;
116
+
117
+        return parent::render();
118
+    }
119
+
120
+    /**
121
+     * Select a value in the field's children
122
+     *
123
+     * @param mixed   $value
124
+     * @param Element $parent
125
+     *
126
+     * @return void
127
+     */
128
+    protected function selectValue($value, $parent = null)
129
+    {
130
+        // If no parent element defined, use direct children
131
+        if (!$parent) {
132
+            $parent = $this;
133
+        }
134
+
135
+        foreach ($parent->getChildren() as $child) {
136
+            $optionValue = $child->getAttribute('value');
137
+
138
+            if ($optionValue === $value || (is_numeric($value) && is_numeric($optionValue) && (int)$optionValue === (int)$value)) {
139
+                $child->selected('selected');
140
+            }
141
+
142
+            // Else iterate over subchilds
143
+            if ($child->hasChildren()) {
144
+                $this->selectValue($value, $child);
145
+            }
146
+        }
147
+    }
148
+
149
+    /**
150
+     * Get the Select's placeholder
151
+     *
152
+     * @return Element
153
+     */
154
+    protected function getPlaceholder()
155
+    {
156
+        if (!$this->placeholder) {
157
+            return false;
158
+        }
159
+
160
+        $attributes = array('value' => '', 'disabled' => 'disabled');
161
+        if (!$this->value) {
162
+            $attributes['selected'] = 'selected';
163
+        }
164
+
165
+        return Element::create('option', $this->placeholder, $attributes);
166
+    }
167
+
168
+    ////////////////////////////////////////////////////////////////////
169
+    ////////////////////////// FIELD METHODS ///////////////////////////
170
+    ////////////////////////////////////////////////////////////////////
171
+
172
+    /**
173
+     * Set the select options
174
+     *
175
+     * @param  array   $_options     The options as an array
176
+     * @param  mixed   $selected     Facultative selected entry
177
+     * @param  boolean $valuesAsKeys Whether the array's values should be used as
178
+     *                               the option's values instead of the array's keys
179
+     */
180
+    public function options($_options, $selected = null, $valuesAsKeys = false)
181
+    {
182
+        $options = array();
183
+
184
+        // If valuesAsKeys is true, use the values as keys
185
+        if ($valuesAsKeys) {
186
+            foreach ($_options as $v) {
187
+                $options[$v] = $v;
188
+            }
189
+        } else {
190
+            $options = $_options;
191
+        }
192
+
193
+        // Add the various options
194
+        foreach ($options as $value => $text) {
195
+            if (is_array($text) and isset($text['value'])) {
196
+                $attributes = $text;
197
+                $text       = $value;
198
+                $value      = null;
199
+            } else {
200
+                $attributes = array();
201
+            }
202
+            $this->addOption($text, $value, $attributes);
203
+        }
204
+
205
+        // Set the selected value
206
+        if (!is_null($selected)) {
207
+            $this->select($selected);
208
+        }
209
+
210
+        return $this;
211
+    }
212
+
213
+    /**
214
+     * Creates a list of options from a range
215
+     *
216
+     * @param  integer $from
217
+     * @param  integer $to
218
+     * @param  integer $step
219
+     */
220
+    public function range($from, $to, $step = 1)
221
+    {
222
+        $range = range($from, $to, $step);
223
+        $this->options($range, null, true);
224
+
225
+        return $this;
226
+    }
227
+
228
+    /**
229
+     * Add an option to the Select's options
230
+     *
231
+     * @param array|string $text       It's value or an array of values
232
+     * @param string       $value      It's text
233
+     * @param array        $attributes The option's attributes
234
+     */
235
+    public function addOption($text = null, $value = null, $attributes = array())
236
+    {
237
+        // Get the option's value
238
+        $childrenKey = !is_null($value) ? $value : sizeof($this->children);
239
+
240
+        // If we passed an options group
241
+        if (is_array($text)) {
242
+            $this->children[$childrenKey] = Element::create('optgroup')->label($value);
243
+            foreach ($text as $key => $value) {
244
+                if (is_array($value)) {
245
+                    $option = Element::create('option', $key)->setAttributes($value);
246
+                } else {
247
+                    $option = Element::create('option', $value)->setAttribute('value', $key);
248
+                }
249
+                $this->children[$childrenKey]->nest($option);
250
+            }
251
+            // Else if it's a simple option
252
+        } else {
253
+            if (!isset($attributes['value'])) {
254
+                $attributes['value'] = $value;
255
+            }
256
+
257
+            $this->children[$attributes['value']] = Element::create('option', $text)->setAttributes($attributes);
258
+        }
259
+
260
+        return $this;
261
+    }
262
+
263
+    /**
264
+     * Clear selected attribute for select options
265
+     *
266
+     * @param Element $parent
267
+     *
268
+     * @return void
269
+     */
270
+    public function clearSelected($parent = null)
271
+    {
272
+        // If no parent element defined, use direct children
273
+        if (!$parent) {
274
+            $parent = $this;
275
+        }
276
+
277
+        foreach ($parent->getChildren() as $child) {
278
+            $child->removeAttribute('selected');
279
+
280
+            if ($child->hasChildren()) {
281
+                $this->clearSelected($child);
282
+            }
283
+        }
284
+    }
285
+
286
+    /**
287
+     * Use the results from a Fluent/Eloquent query as options
288
+     *
289
+     * @param  array           $results    An array of Eloquent models
290
+     * @param  string|function $text       The value to use as text
291
+     * @param  string|array    $attributes The data to use as attributes
292
+     * @param  string	   $selected   The default selected item
293
+     */
294
+    public function fromQuery($results, $text = null, $attributes = null, $selected = null)
295
+    {
296
+        $this->options(Helpers::queryToArray($results, $text, $attributes), $selected);
297
+
298
+        return $this;
299
+    }
300
+
301
+    /**
302
+     * Select a particular list item
303
+     *
304
+     * @param  mixed $selected Selected item
305
+     */
306
+    public function select($selected)
307
+    {
308
+        $this->value = $selected;
309
+
310
+        return $this;
311
+    }
312
+
313
+    /**
314
+     * Add a placeholder to the current select
315
+     *
316
+     * @param  string $placeholder The placeholder text
317
+     */
318
+    public function placeholder($placeholder)
319
+    {
320
+        $this->placeholder = Helpers::translate($placeholder);
321
+
322
+        return $this;
323
+    }
324
+
325
+    ////////////////////////////////////////////////////////////////////
326
+    ////////////////////////////// HELPERS /////////////////////////////
327
+    ////////////////////////////////////////////////////////////////////
328
+
329
+    /**
330
+     * Returns the current options in memory for manipulations
331
+     *
332
+     * @return array The current options array
333
+     */
334
+    public function getOptions()
335
+    {
336
+        return $this->children;
337
+    }
338 338
 }
Please login to merge, or discard this patch.