Completed
Push — master ( f54537...394c45 )
by Mihail
02:09
created

Form::field()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 6
nc 2
nop 5
1
<?php
2
3
namespace Ffcms\Core\Helper\HTML;
4
5
use Ffcms\Core\App;
6
use Ffcms\Core\Arch\Model;
7
use Ffcms\Core\Exception\NativeException;
8
use Ffcms\Core\Exception\SyntaxException;
9
use Ffcms\Core\Helper\FileSystem\File;
10
use Ffcms\Core\Helper\HTML\Form\Constructor;
11
use Ffcms\Core\Helper\HTML\Form\FieldSelector;
12
use Ffcms\Core\Helper\HTML\System\NativeGenerator;
13
use Ffcms\Core\Helper\Type\Any;
14
use Ffcms\Core\Helper\Type\Arr;
15
use Ffcms\Core\Helper\Type\Obj;
16
use Ffcms\Core\Helper\Type\Str;
17
18
/**
19
 * Class Form. Simple HTML model generator for fast form building.
20
 * @package Ffcms\Core\Helper\HTML
21
 */
22
class Form extends NativeGenerator
23
{
24
    /** @var array */
25
    private static $structLayer = [
26
        'base' => 'native/form/base_layer',
27
        'checkbox' => 'native/form/checkbox_layer',
28
        'checkboxes' => 'native/form/checkboxes_layer',
29
        'radio' => 'native/form/radio_layer',
30
        'jsnotify' => 'native/form/jsnotify'
31
    ];
32
33
    /** @var string */
34
    private $name;
35
    private $formProperty = [];
36
    /** @var Model */
37
    private $model;
38
39
    /** @var FieldSelector */
40
    public $field;
41
42
43
    /**
44
     * Form constructor. Build form based on model properties
45
     * @param Model $model
46
     * @param array|null $property
47
     * @param array|null $layerFiles
48
     * @throws SyntaxException
49
     */
50
    public function __construct($model, array $property = null, array $layerFiles = null)
51
    {
52
        // prevent white-screen locks when model is not passed or passed wrong
53
        if (!$model instanceof Model)
54
            throw new SyntaxException('Bad model type passed in form builder. Check for init: new Form()');
55
56
        $this->model = $model;
57
        $this->name = $model->getFormName();
58
        
59
        // check if passed custom layer file
60
        if (Any::isArray($layerFiles) && count($layerFiles) > 0) {
61
            foreach (array_keys(static::$structLayer) as $type) {
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
62
                if (isset($layerFiles[$type]) && Any::isStr($layerFiles[$type]))
63
                    static::$structLayer[$type] = $layerFiles[$type];
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
64
            }
65
        }
66
        // initialize field selector helper
67
        $this->field = new FieldSelector($model, static::$structLayer);
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
68
69
        // set model submit method
70
        $property['method'] = $this->model->getSubmitMethod();
71
72
        $property['id'] = $this->name; // define form id
73
        // if action is not defined - define it
74
        if (!$property['action'])
75
            $property['action'] = App::$Request->getFullUrl();
76
77
        // set property global for this form
78
        $this->formProperty = $property;
79
    }
80
81
    /**
82
     * Open form tag with prepared properties
83
     * @return string
84
     */
85
    public function start()
86
    {
87
        $form = self::buildSingleTag('form', $this->formProperty, false);
88
        if ($this->model->_tokenRequired)
89
            $form .= PHP_EOL . $this->field->hidden('_csrf_token', ['value' => $this->model->_csrf_token]);
90
91
        return $form;
92
    }
93
94
    /**
95
     * Use $this->field->type() instead. Deprecated!
96
     * @param string $object
97
     * @param string $type
98
     * @param null|array $property
99
     * @param null|string $helper
100
     * @param null|string $layerFile
101
     * @return null|string
102
     * @deprecated
103
     */
104
    public function field(string $object, string $type, ?array $property = null, ?string $helper = null, ?string $layerFile = null)
105
    {
106
        $response = null;
107
        try {
108
            $response = $this->field->{$type}($object, $property, $helper, $layerFile);
109
        } catch (\Exception $e){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
110
        return $response;
111
    }
112
113
    /**
114
     * Display submit button for current form
115
     * @param string $title
116
     * @param array $property
117
     * @return string
118
     */
119
    public function submitButton($title, array $property = [])
120
    {
121
        $property['type'] = 'submit';
122
        $property['name'] = $this->name . '[submit]';
123
        $property['value'] = $title;
124
        return self::buildSingleTag('input', $property);
125
    }
126
127
    /**
128
     * Finish current form.
129
     * @param bool $validate
130
     * @return string
131
     */
132
    public function finish($validate = true)
133
    {
134
        // pre-validate form fields based on model rules and jquery.validation
135
        if ($validate) {
136
            App::$Alias->addPlainCode('js', '$().ready(function() { $("#' . $this->name . '").validate(); });');
137
            App::$Alias->setCustomLibrary('js', '/vendor/bower/jquery-validation/dist/jquery.validate.min.js');
138
            if (App::$Request->getLanguage() !== 'en') {
139
                $localeFile = '/vendor/bower/jquery-validation/src/localization/messages_' . App::$Request->getLanguage() . '.js';
140
                if (File::exist($localeFile)) {
141
                    App::$Alias->setCustomLibrary('js', $localeFile);
142
                }
143
            }
144
            // if model is not empty - add js error color notification
145
            if ($this->model !== null) {
146
                $badAttr = $this->model->getBadAttribute();
147
                $formName = $this->model->getFormName();
148
                if (Any::isArray($badAttr) && count($badAttr) > 0) {
149
                    foreach ($badAttr as $attr) {
0 ignored issues
show
Bug introduced by
The expression $badAttr of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
150
                        $itemId = $formName . '-' . $attr;
151
                        try {
152
                            $render = App::$View->render(static::$structLayer['jsnotify'], ['itemId' => $itemId]);
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
153
                            App::$Alias->addPlainCode('js', $render);
154
                        } catch (SyntaxException $e) {
155
                            if (App::$Debug)
156
                                App::$Debug->addException($e);
157
                        }
158
                    }
159
                }
160
            }
161
        }
162
        return '</form>';
163
    }
164
}