Coder Social home page Coder Social logo

yiisoft / form Goto Github PK

View Code? Open in Web Editor NEW
40.0 18.0 20.0 923 KB

The package helps with implementing data entry forms

Home Page: https://www.yiiframework.com/

License: BSD 3-Clause "New" or "Revised" License

PHP 99.91% Makefile 0.04% Shell 0.05%
form forms yii3 hacktoberfest

form's Introduction

Yii Form


Latest Stable Version Total Downloads Build status Code Coverage Mutation testing badge static analysis type-coverage

The package helps with implementing data entry forms.

Requirements

  • PHP 8.1 or higher.
  • mbstring PHP extension.

Installation

The package could be installed with composer:

composer require yiisoft/form --prefer-dist

General usage

General topics:

Fields available out of the box:

Field parts:

Testing

Unit testing

The package is tested with PHPUnit. To run tests:

./vendor/bin/phpunit

Mutation testing

The package tests are checked with Infection mutation framework with Infection Static Analysis Plugin. To run it:

./vendor/bin/roave-infection-static-analysis-plugin

Static analysis

The code is statically analyzed with Psalm. To run static analysis:

./vendor/bin/psalm

License

The Yii Form is free software. It is released under the terms of the BSD License. Please see LICENSE for more information.

Maintained by Yii Software.

Support the project

Open Collective

Follow updates

Official website Twitter Telegram Facebook Slack

form's People

Contributors

armpogart avatar arogachev avatar dependabot[bot] avatar devanych avatar dood- avatar fantom409 avatar filsh avatar gerych1984 avatar githubjeka avatar iamsaint avatar mister-42 avatar roxblnfk avatar rustamwin avatar samdark avatar sankaest avatar stylecibot avatar terabytesoftw avatar thenotsoft avatar tomaszkane avatar viktorprogger avatar vjik avatar xepozz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

form's Issues

Virtual property

I think would be great to have ability to use virtual property.
By virtual property I mean ability to Form to have getter without having property with similar name.
For example:

class Form {
  private string $_requestId;

  public function getRequestId() {
    return $this->_requestId;
  }
  // or
  private string $request_id;

  public function getRequestId() {
    return $this->request_id;
  }
  // or
  public function getRequestId() {
    return $this->myCoolProperty;
  }
}

Discussion was started here

Form validation scenarios

Do we need something similar to scenarios from yii2?

I'm all for specialized DTO projections, such as UserCreateDto, UserUpdateDto, but maybe someone will find it more productive to have one model with conditional validation for such cases. I see it as simply different groups of validators for each named scenario.

Form fields are not customizable

I like in which direction this library goes, but I still don't like some aspects of it, will post each problem in a separate issue. This one concerning \Yiisoft\Yii\Form\Widget\Field class extendibility.

In yii2 all widgets were just not final classes and our own classes could easily extend them to customize the behavior wanted. I'm aware of the issues that not final classes pose, such as it becomes more difficult to maintain/support/change them later as each change can cause BC break, so here comes final classes. And I always encourage to use final classes where possible. But Meyer's open-closed principle (from SOLID) still states that the class must be open for extension (while still being closed for modification). DI in yii3 partly solves the problem, I say partly as the classes must be still designed carefully to allow that extendibility. And now comes an example.

I want to customize the fields for usage with bootstrap 4, here is an example of a typical bootstrap 4 form:

<form>
  <div class="form-group">
    <label for="inputLogin">Login</label>
    <div class="input-group">
      <div class="input-group-prepend">
        <span class="input-group-text" id="inputGroupPrepend">@</span>
      </div>
      <input type="text" class="form-control" id="inputLogin" placeholder="Example input placeholder">
    </div>
  </div>
  <div class="form-group">
    <label for="inputPassword">Another label</label>
    <input type="text" class="form-control" id="inputPassword" placeholder="Another input placeholder">
    <small class="form-text text-muted">
      Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
    </small>
  </div>
  <div class="form-group">
    <label for="inputSelect">Select Label</label>
    <select id="inputSelect" class="form-control">
        <option selected>Choose...</option>
        <option>...</option>
    </select>
  </div>
</form>

Now the issues with current implementation:

  • I want to remove default label class (\Yiisoft\Yii\Form\Widget\Field::DEFAULT_LABEL_OPTIONS) from all fields, but I can't do that currently as even though Field is a widget and is configured through factory, but I can't set private constant through factory.
  • I want to use form-text or text-muted or both classes for instead of default hint-block, and also use small tag instead of div by default, but again I don't see any way to configure private property ((\Yiisoft\Yii\Form\Widget\Field::DEFAULT_HINT_OPTIONS) or options through factory.
  • I want to apply custom-select class to all dropdown lists (it is important for bootstrap 4 select validation styling) and again I don't see how.
  • I want to use input groups where I want easily without using template each time, and the field can even have both input-group-append and input-group-prepend, I understand that current implementation is not targeted for boostrap 4, but I don't see any way to incorporate input groups functionality with this implementation.
  • The other issue concerns bootstrap 4 form validation, I will open separate issue for that as it is not only about extendibility.

[Feature Request] Adjust input following rules

What steps will reproduce the problem?

    public function rules(): array
    {
        return [
            'amount' => [(new Number())->integer()->min(1)->max(250)],
        ];
    }
echo $field->config($form, 'amount')->input('number');

Below is just 1 example, there are probably more validators that can be integrated into the fields.

What is the expected result?

<input type="number" id="form-amount" class="form-control" name="Form[amount]" max="250" min="1" placeholder="Amount">

What do you get instead?

<input type="number" id="form-amount" class="form-control" name="Form[amount]" placeholder="Amount">

Additional info

Q A
Version dev
PHP version 8.0.0
Operating system Debian 10

Adding more form adaptions from rules

Creating new issue so this won't get lost.

textarea can have minlength and maxlength
Required is also available for select, textarea
Besides number and email, input can also be url (among other things, but those do not have a validator in Yii. Not sure if that would be viable to implement).


Need more research:

pattern vs RegularExpression Already taken care of, I totally missed that :)
step - not a validation option in Yii

Originally posted by @Mister-42 in #65 (comment)

Html::errorSummary of specific attributes

What steps will reproduce the problem?

for best UX, in a secction of my form I want to show the error summary of certain attributes, not all of them, but form->errorsummary($model) always show all attribute erros

What is the expected result?

// fieldset (with custom css style)
// errorSummary of field1 and field2
// field1
// field2

// fieldset (with custom css style)
// errorSummary of field3 and field4
// field3
// field4

What do you get instead?

$form->errorsummary($model, [
  'attributes' => [
    'attr_1',
    'attr_2',
  ]
]);

Additional info

In case of multiple models for error summary may be

$form->errorsummary([
  $model1 => [
    'attr_1',
    'attr_2',
  ],
  $model2 => [
    'attr_1',
    'attr_2',
  ],
  $model3 // all attributes
]);

Add `$content` parameter to `Field::*Button()` methods

In most cases of using buttons we added label via method content(). For example:

Field::submitButton()->content('Create');

Suggest change signature of methods button(), resetButton() and submitButton() to (?string $content = null, array $config = []).

Using in most cases will be so:

Field::submitButton('Create');

But in very rare cases when pass config only will be:

Field::submitButton(null, $config);

Allow null as value of fields widget

Good day. In this time Text::widget() and Number::widget() (maybe others) do not allow null as value. But usually initial value of property equals null. Maybe allow null as value of fields widget? Thanks

Input field name array

Need example php code to generate for cases like this:

<input " type="text" name="LocationForm[coords][lng]">

Additional info

Q A
Version 1.0.x-dev
PHP version 8.0
Operating system

Ability to prevent encoding of label in Yiisoft\Form\Widget\Label

Currently it is hard to prevent encoding of label by Label widget.
For example if your label contains entity for non-breaking space:
In model class:

 public function getAttributeLabels(): array
{
    return [
        'myField' => 'My&nbsp;Field',
    ];
}

Then when you use it like this it will be double encoded:

Label::widget()->config($model, 'myField');

Currently, I've found a workaround to prevent double encoding like this:

class NoEncodeString implements \Yiisoft\Html\NoEncodeStringableInterface
{
    private string $value = '';

    public function __construct(string $value)
    {
        $this->value = $value;
    }

    public function __toString(): string
    {
        return $this->value;
    }
}

Label::widget()->config($model, 'myField', ['label' => new NoEncodeString($model->getAttributeLabel('myField'))]);

Problem with this workaround is, that the label must be passed into widget only this way. The instance of NoEncodeString cannot be used inside model's getAttributeLabels() because the getAttributeLabel() method return type is string.
It also cannot be passed to widget's label() method because its parameter is hinted as string type so it's converted when the method is called.

The easiest solution would be to add a method/option to Label that would prevent encoding the label when building the html tag.
The more generic solution might be to make \Yiisoft\Html\NoEncodeStringableInterface extend the \Stringable interface and use string|Stringable instead of string type whenever string is passing through method to be used later.

How work with Checkbox Widget?

Good day. After remove Boolean Widget i replace it with Yiisoft\Form\Widget\Checkbox, but i don't understant one thing with it.

So, my case:

  1. FormModel with boolean attribute is_active, default True
  2. After checkox uncheck and form save - is_active === False
  3. But - attribute value of checkbox equals 0, because in the widget we have that code
    $value = HtmlForm::getAttributeValue($new->getFormModel(), $new->attribute);
  4. So - if i check it again and send form, it sended 0 and it will be converted to False again

Checkbox don't accept label parameters

Label not render given label and class

What steps will reproduce the problem?

// @var FormModel $form  
echo Field::checkbox($form, 'remember_me')
   ->label("Remember me please!!!!")
   ->labelClass('form-check-label mb-0 ms-3')

What is the expected result?

What do you get instead?

<div>
    <input type="hidden" name="EmailForm[remember_me]" value="0">
    <label>
        <input type="checkbox" id="emailform-remember_me" name="EmailForm[remember_me]" value="1">
        Remember Me
    </label>
</div>

Additional info

Q A
Version dev-master
PHP version 8.0
Operating system

Type casting at load, why?

If you do not specify the type in the form, then we will get an exception, if you specify the type, for example, a number, and a string comes from the user, then with $form->load() we lose raw data.
We lose meaning in validation, because before validation, there is a type conversion

Use array_key_exists instead of isset on check value

Good day. Thanks for last update - i try update my project for this now. I have some case and possible solution for value attribute/method.

Case - inputs range "from - to" as array attrbute on some FormModel.

<div class="input-group">
            <?= Label::widget()->for($searchForm, 'price[0]')->attributes([
                    'class' => 'input-group-text'
                ])->label('ะพั‚') .
                Number::widget()->for($searchForm, 'price[0]')->value(null)->attributes([
                    'min' => 1,
                    'step' => 1,
                    ':max' => "maxPrice",
                    'v-model.number' => "minPrice"
                ])
            ?>
            <?=
            Label::widget()->for($searchForm, 'price[1]')->attributes([
                'class' => 'input-group-text'
            ])->label('ะดะพ')
            ?>
            <input type="number" class="form-control" placeholder="<?= $data['max_price'] ?>" id="<?= HtmlForm::getInputId($searchForm, 'price[1]') ?>" name="<?= HtmlForm::getInputName($searchForm, 'price[1]') ?>" min="1" step="1" :min="minPrice" v-model.number="maxPrice">
        </div>

But - value(null) doesn't effect and WidgetAttributes::getAttributeValue return array (ignored [0] suffix).

So - maybe check value using isset and array_key_exists instead of just isset like in ArrayHelper::keyExists? Something like

if (isset($attributes['value']) || array_key_exists('value', $attributes)) {
    $value = $attributes['value'];
} else {
     $value =  $this->getAttributeValue();
}

unset($attributes['value']);

****

Implement a simple mapper in the package

It would be helpful if the package will also provide simple mapper/automapper for mapping FormModel attributes to Entity and vice-versa. This can be useful for both API models and Html form models.

Rename package

Since there are no Yii-specific dependencies, it can be removed to just "form".

Allow null value for input id and label for attributes

Good day. After widgets has own separate config (and it's are great thing) - i rewriting my forms to single style using that config. So - i have that main config for Fielf::widget()

Field::class => [
        'template()' => [
            '{label}<div class="col-sm-10">{input}{error}</div>'
        ],
        'containerClass()' => [
            'row justify-content-center mb-3'
        ],
        'inputClass()' => [
            'form-control'
        ],
        'labelClass()' => [
            'col-sm-2 col-form-label text-start text-lg-end'
        ],
        'errorClass()' => [
            'text-danger small'
        ]
],

And in my case all fields block has same template/containerClass/inputClass etc, including form as vue component. But for vue component i can't use Field::widget() without input id and label for attribute, because it autogenerated. So - that code give me are field, but it's not work as Vue component, because Vue not replace exists attribute if his position before it :** analog

<?= $field->config($formModel, 'title')
                    ->label([
                         'for' => null, //will be autogenerated. But wo this it's work, because ":for" will be prepended before "for"
                        ':for' => "'source-url-title-' + suffix"
                    ])->text([
                        'id' => null, //will be autogenerated. And i don't know how prepend ":id" attribute
                        ':id' => "'source-url-title-' + suffix",
                        'v-model.trim' => 'data.title'
                    ])
?>

So - maybe allow render field without id/for attributes for same cases? Thanks

Radio & RadioList Widgets casting value to int

Steps to reproduce problem

In a view:

config($form, 'os')->radioList([], ['android' => 'Android', 'ios' => 'iOS', 'pureos' => 'PureOS']) ?>

Where $form::os = 'ios'

Expected result

<div>
    <label for="myform-os">OS</label>
    <div id="myform-os">
        <label><input type="radio" name="MyForm[os]" value="android"> Android</label>
        <label><input type="radio" name="MyForm[os]" value="ios" checked> iOS</label>
        <label><input type="radio" name="MyForm[os]" value="pureos"> PureOS</label>
    </div>
</div>

Actual result

The iOS radio input is not checked

<div>
    <label for="myform-os">OS</label>
    <div id="myform-os">
        <label><input type="radio" name="MyForm[os]" value="android"> Android</label>
        <label><input type="radio" name="MyForm[os]" value="ios"> iOS</label>
        <label><input type="radio" name="MyForm[os]" value="pureos"> PureOS</label>
    </div>
</div>

Cause

Yiisoft\Form\Widget\RadioList line 227 casts the existing value to int (the same is done at Yiisoft\Form\Widget\Radio line 110).

Fix

Remove casting to int

Additional info

Q A
Version dev-master
PHP version 8.0.11
Operating system Ubuntu 20.04

split package

Split the package into 2 packages, place widgets in a separate package, for example form-widgets, because when using RestAPI widgets are not needed

Change input type $csrf in Form::csrf method

Good day. After last release of yiisoft/yii-view $csrf sending as object extends of string. So maybe change input typeYiisoft\Form\Widget::csrf for backward compatibility like this

public function csrf($csrfToken, string $csrfName = '_csrf'): self
{
    $new = clone $this;
    $new->csrfName = $csrfName;

    if (is_object($csrfToken) && method_exists($csrfToken, '__toString') {
         $new->csrfToken = $csrfToken->__toString();
    } elseif (is_string($csrfToken)) {
         $new->csrfToken = $csrfToken;
    } else {
         throw new InvalidArgumentException('****');
    } 

    return $new;
}

Thanks

PHP 8 and ReflectionUnionType

Good day. I have many search forms extended from FormModel and when i using union attribute type (like public int|array|null $source_ids = null;) it fails, because type of this property is instance of ReflectionUnionType not ReflectionType. Maybe property attributes do as attributes[name] = [...types]?

Dependency update/adjust to yiisoft/html

What steps will reproduce the problem?

My composer use yiisoft/html v3.

What is the expected result?

Composer update is success.

What do you get instead?

Composer dependency problem:
- yiisoft/form[dev-master, 1.0.x-dev] require yiisoft/html ^2.4 -> satisfiable by yiisoft/html[2.4.0, 2.5.0].

TypeError for float properties

class PriceForm extends FormModel {
    private ?float $price;
    
    public function getPrice(): ?float
    {
        return $this->price;
    }

    public function price(float $price): void
    {
        $this->price = $price;
    }
}

input widget

 echo (new TextInput())->config($formModel, 'price')->run();

When call $form Model->load([...]) i got TypeError exception for property "price". It's happened because Form Model has no case for float typed properties in method "setAttributes"

Add is_scalar condition extends of is_string on some fiels

Good day. After last release null values are allowed and it's great. But i have some litle improvement. In my case phone numbers stored in DB on bigint column, so that failed with telephone::widget(). So maybe do that conditions in widget:

  • Email/Url - is_string or null
  • Number - is_numeric or null (as now)
  • Date*** - maybe allow DateTime class?
  • Others - is_scalar or null

Thanks

Fix load to remove hiddenInput from fileInput

As described here, a hidden input is added to support cases where fileInput is the only field of the form. I'm not sure how this works, but I will take that information for granted.
Since most forms with a file input will also have other fields, wouldn't it make more sense to have it default to NOT adding a hidden input and enable it explicitely if required?

Nested array form with translations

Hi. I want to create nested array form with translation array.
Currently i can make input like AttributeValue['name.en'] (code below)
I want to create input name like AttributeValue[{value_Id}]['name.en']
But the problem is that the TranslatableAttributeForm is not array, he contain only one value.

Is this possible to create two levels array form there?

class ProductGroupValueEditForm extends BaseFormModel
{
    public ?string $system_name = null;

    public ?int $sequence = null;

    public TranslatableAttributeForm $name;


    public function getRules(): array
    {
        $rules = array_merge(parent::getRules(), [
            'system_name' => [new HasLength(max: 128, skipOnEmpty: true)],
        ]);

        foreach (LocaleHelper::getLanguages() as $language) {
            $rules['name.' . $language] = [new Required, new HasLength(max: 128)];
        }

        return $rules;
    }

    public function getAttributeLabels(): array
    {
        $translationLabels = (new ProductGroupValueTranslation())->attributeLabels();
        foreach (LocaleHelper::getLanguages() as $language) {
            foreach ($translationLabels as $attribute => $label) {
                $translationLabels["$attribute.$language"] = $label;
            }
        }

        return array_merge($translationLabels, (new ProductGroupValue())->attributeLabels());
    }
}

Wrong html input pattern for Url::rule

Good day. When i set Url::rule to FormModel it also generate input pattern attribute equals ^(http|https):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(?::\d{1,5})?(?:$|[?\/#]). But it's wrong for browser if input value has only domain. Nevertheless - if i remove pattern attribute using browser console and send form, validation will be valid. Thanks

2021-11-08 19 04 13 localhost efe762fb827e
2021-11-08 19 04 52 localhost 4fa8c3965d78

Incorrect form fields configuration

There is no check for the existence of a attribute when configuring a widget

For example this code should throw exception

(new TextInput)->config($formModel, 'undefined_form_attribute')

Change access of collectAttributes to protected

Good day. I write class class ActiveFormModel extends FormModel to simple using with ActiveRecord.
I have that idea

public function __construct(ActiveRecordInterface $model)
{
        $this->model = $model;
        
        parent::__construct();
}

protected function collectAttributes(): array 
{
        return $this->model->attributes;
}

But i can't because FormModel::collectAttributes have private access. Can you change to protected or explain why not?

FormModel trait

What about adding a FormModel trait and leaving the abstract class also there (which will use the trait). I'm usually for using composition over inheritance.

Also this will allow us to decouple some parts: FormTrait and FormValidationTrait for use cases when I don't need validation for the form or I want to use some other library/logic for the validation.

Behavior of the Validator on the forms.

The validator should not change the attributes of the forms, i propose that the attributes of HTML can be added independently of the validator, since they are two different validations, one by the browser, and the other with FormModel::class, they must be independent one of the other, using both for the same validation does not make sense.

Yiisoft\Validator\DataSet\AttributeDataSet not found

After validator refactor form needs update.

What steps will reproduce the problem?

Do composer u and try to use yiisoft/form with yiisoft/validator.

What is the expected result?

No errors :-)

What do you get instead?

Exception: Class "Yiisoft\Validator\DataSet\AttributeDataSet" not found in /vendor/yiisoft/form/src/FormModel.php:213

Additional info

Q A
Version dev-master
PHP version 8.0
Operating system KDE Neon

Change FormModel::attributes as protected or add method like getAttributeNames

Good day. In my case i have AbstractSearchForm extends from FormModel. Today i need write base method toArray that returns array of filters like [name => value, ....] to store it in database. But i can't do it without reflection. So - as we have protected collectAttributes maybe set property attributes also protected? Or add method that return array of attribute names. Thanks

HiddenInput is not always completely hidden

What steps will reproduce the problem?

Have enclosedByContainer defined, like this

echo $field->config($form, 'example')->hiddenInput();

What is the expected result?

<input type="hidden" id="form-example" class="form-control" name="Form[example]">

What do you get instead?

<div class="form-group field-form-example mb-3">

<input type="hidden" id="form-example" class="form-control" name="Form[example]">

</div>

Additional info

Q A
Version dev
PHP version 8.0.2
Operating system Debian 10

Yiisoft\Form\Widget\Validator\FieldValidator::getValidatorAttributes() shouldn't apply required attribute for Required rule indiscriminately

What steps will reproduce the problem?

  • Create a form model with 2 attributes.
  • Add Required for both attributes with when callback set that only one of them needs to be filled.
public function getRules(): array
{
    return [
        'attr1' => [new Required(when: fn() => empty($this->attr2))],
        'attr2' => [new Required(when: fn() => empty($this->attr1)),
    ];
}
  • Create form with inputs for both attributes.

What is the expected result?

The form can be send with only one field filled.
If no field is filled the validation won't pass.

What do you get instead?

The form can't be submitted with one field filled because both inputs have required attribute.

Additional info

This is caused by FieldValidator adding required for each attribute that has Required validation rule indiscriminately.

if ($rule instanceof Required) {
$attributes['required'] = true;
}

IMHO the required attribute should not be added if the Required rule have when callback set because it's impossible to predict if the field can or cannot be empty.

Unfortunately the same issue is also present in big-ref-2 branch.

form/src/Field/Text.php

Lines 214 to 216 in 28018db

if ($rule instanceof Required) {
$this->inputAttributes['required'] = true;
}

Q A
Version dev-master (4c4f3fa)
PHP version 8.1
Operating system Windows 10

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.