Skip to content

Commit

Permalink
Merge branch '1.4' into fix-FRAM-97-rebuild-inner-form-on-custom-form…
Browse files Browse the repository at this point in the history
…-set-container
  • Loading branch information
vincent4vx committed May 2, 2023
2 parents 585f164 + d47ad99 commit 2231fab
Show file tree
Hide file tree
Showing 17 changed files with 785 additions and 50 deletions.
34 changes: 32 additions & 2 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [ master ]
branches: [ '[1-9].[0-9]+' ]
pull_request:
branches: [ master ]
branches: [ '[1-9].[0-9]+' ]

jobs:
build:
Expand Down Expand Up @@ -83,3 +83,33 @@ jobs:
run: |
git fetch --depth=1 origin $GITHUB_BASE_REF
./infection.phar --logger-github --git-diff-filter=AM --min-msi=90
coverage:
name: Test coverage
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set Timezone
uses: szenius/set-timezone@v1.0
with:
timezoneLinux: "Europe/Paris"

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
extensions: json
ini-values: date.timezone=Europe/Paris

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run test coverage
run: ./vendor/bin/phpunit --coverage-clover=coverage.xml

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: coverage.xml
12 changes: 0 additions & 12 deletions .scrutinizer.yml

This file was deleted.

3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
Library for handle form, and request validation.

[![build](https://github.com/b2pweb/bdf-form/actions/workflows/php.yml/badge.svg)](https://github.com/b2pweb/bdf-form/actions/workflows/php.yml)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/b2pweb/bdf-form/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/b2pweb/bdf-form/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/b2pweb/bdf-form/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/b2pweb/bdf-form/?branch=master)
[![codecov](https://codecov.io/github/b2pweb/bdf-form/branch/1.4/graph/badge.svg?token=VOFSPEWYKX)](https://app.codecov.io/github/b2pweb/bdf-form)
[![Packagist Version](https://img.shields.io/packagist/v/b2pweb/bdf-form.svg)](https://packagist.org/packages/b2pweb/bdf-form)
[![Total Downloads](https://img.shields.io/packagist/dt/b2pweb/bdf-form.svg)](https://packagist.org/packages/b2pweb/bdf-form)
[![Type Coverage](https://shepherd.dev/github/b2pweb/bdf-form/coverage.svg)](https://shepherd.dev/github/b2pweb/bdf-form)
Expand Down
15 changes: 15 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
codecov:
require_ci_to_pass: false
notify:
wait_for_ci: false

coverage:
status:
project:
default:
target: auto
threshold: 1%
patch:
default:
# basic
target: 95%
5 changes: 0 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@
"symfony/security-csrf": "For enable CSRF element",
"giggsey/libphonenumber-for-php": "Required to use phone type (~8.0)"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"scripts": {
"tests": "phpunit",
"tests-with-coverage": "phpunit --coverage-clover coverage.xml",
Expand Down
59 changes: 58 additions & 1 deletion src/Aggregate/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ final class Form implements FormInterface
*/
private $generator;

/**
* Does the form is optional ?
* An optional form will not be validated if the form is empty, and its value will be null
*
* @var bool
*/
private $optional;

/**
* @var RootElementInterface|null
*/
Expand All @@ -98,22 +106,32 @@ final class Form implements FormInterface
*/
private $value;

/**
* Does the form has been submitted ?
* In case of optional form, the form is not submitted if the value is empty
*
* @var bool
*/
private $submitted = false;

/**
* Form constructor.
*
* @param ChildrenCollectionInterface $children
* @param ValueValidatorInterface<T>|null $validator
* @param TransformerInterface|null $transformer
* @param ValueGeneratorInterface<T>|null $generator
* @param bool $optional
*/
public function __construct(ChildrenCollectionInterface $children, ?ValueValidatorInterface $validator = null, ?TransformerInterface $transformer = null, ?ValueGeneratorInterface $generator = null)
public function __construct(ChildrenCollectionInterface $children, ?ValueValidatorInterface $validator = null, ?TransformerInterface $transformer = null, ?ValueGeneratorInterface $generator = null, bool $optional = false)
{
$this->children = $children->duplicate($this);
$this->validator = $validator ?? ConstraintValueValidator::empty();
$this->transformer = $transformer ?: NullTransformer::instance();
$this->error = FormError::null();
/** @var ValueGeneratorInterface<T> */
$this->generator = $generator ?: new ValueGenerator();
$this->optional = $optional;
}

/**
Expand All @@ -124,9 +142,14 @@ public function submit($data): ElementInterface
$this->valid = true;
$this->value = null;

if ($this->handleOptional($data)) {
return $this;
}

$data = $this->transformHttpValue($data);

$this->submitToChildrenAndValidate($data, 'submit');
$this->submitted = true;

return $this;
}
Expand All @@ -139,11 +162,16 @@ public function patch($data): ElementInterface
$this->valid = true;
$this->value = null;

if ($this->handleOptional($data)) {
return $this;
}

if ($data !== null) {
$data = $this->transformHttpValue($data);
}

$this->submitToChildrenAndValidate($data, 'patch');
$this->submitted = true;

return $this;
}
Expand Down Expand Up @@ -196,6 +224,10 @@ public function value()
return $this->value;
}

if (!$this->submitted && $this->optional) {
return null;
}

$this->value = $this->generator->generate($this);

foreach ($this->children->reverseIterator() as $child) {
Expand Down Expand Up @@ -401,4 +433,29 @@ private function submitToChildren($data, string $method): bool

return true;
}

/**
* Handle the optional case
* If the form is optional and the data is empty, the form will be considered as not submitted and validation will be ignored
*
* @param mixed $data Submitted HTTP data
*
* @return bool true if the form is optional and not submitted, false otherwise
*/
private function handleOptional($data): bool
{
if (!$this->optional || $data !== null && $data !== [] && $data !== '') {
return false;
}

$this->submitted = false;
$this->error = FormError::null();

// Reset children value
foreach ($this->children as $child) {
$child->element()->import(null);
}

return true;
}
}
17 changes: 16 additions & 1 deletion src/Aggregate/FormBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class FormBuilder extends AbstractElementBuilder implements FormBuilderInterface
*/
private $generator;

/**
* @var bool
*/
private $optional = false;


/**
* FormBuilder constructor.
Expand Down Expand Up @@ -353,6 +358,16 @@ public function generates($entity): FormBuilderInterface
return $this->generator(new ValueGenerator($entity));
}

/**
* {@inheritdoc}
*/
public function optional(bool $flag = true): FormBuilderInterface
{
$this->optional = $flag;

return $this;
}

/**
* {@inheritdoc}
*/
Expand All @@ -364,7 +379,7 @@ final protected function createElement(ValueValidatorInterface $validator, Trans
$children->add($child->buildChild());
}

$form = new Form($children, $validator, $transformer, $this->generator);
$form = new Form($children, $validator, $transformer, $this->generator, $this->optional);

// The root form is configured by the builder : set into the form
if ($this->hasRootFormConfiguration()) {
Expand Down
23 changes: 23 additions & 0 deletions src/Aggregate/FormBuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,29 @@ public function generator(ValueGeneratorInterface $generator): FormBuilderInterf
*/
public function generates($entity): FormBuilderInterface;

/**
* Mark the form as optional
* An optional form will not be validated if the form is empty, and its value will be null
*
* <code>
* $builder->string('firstName')->required()->setter();
* $builder->string('lastName')->required()->setter();
* $builder->optional();
*
* $form = $builder->buildElement();
*
* $form->submit([])->valid(); // true
* $form->value(); // null
*
* $form->submit(['firstName' => 'John'])->valid(); // false : lastName is required
* </code>
*
* @param bool $flag true to mark the form as optional, false otherwise
*
* @return $this
*/
public function optional(bool $flag = true): FormBuilderInterface;

/**
* {@inheritdoc}
*
Expand Down
36 changes: 36 additions & 0 deletions src/Leaf/AbstractBooleanElement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Bdf\Form\Leaf;

use TypeError;

use function is_scalar;

/**
* Base element for boolean value
*
* @see BooleanElementBuilder for build the element
*
* @method bool|null value()
* @extends LeafElement<bool>
*/
abstract class AbstractBooleanElement extends LeafElement
{
/**
* {@inheritdoc}
*
* @return bool|null
*/
final protected function tryCast($value): ?bool
{
if ($value === null) {
return null;
}

if (!is_scalar($value)) {
throw new TypeError('The import()\'ed value of a '.static::class.' must be a scalar value or null');
}

return (bool) $value;
}
}
25 changes: 1 addition & 24 deletions src/Leaf/BooleanElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,20 @@

namespace Bdf\Form\Leaf;

use BadMethodCallException;
use Bdf\Form\Child\Http\HttpFieldPath;
use Bdf\Form\Leaf\View\BooleanElementView;
use Bdf\Form\Transformer\TransformerInterface;
use Bdf\Form\Validator\ValueValidatorInterface;
use Bdf\Form\View\ElementViewInterface;
use Bdf\Form\View\FieldViewInterface;
use LogicException;

/**
* Handle a boolean value, like with checkbox input
* A value is considered as true when a value is present, and equals to the defined value
*
* @see BooleanElementBuilder for build the element
*
* @method bool value()
* @extends LeafElement<bool>
*/
class BooleanElement extends LeafElement
class BooleanElement extends AbstractBooleanElement
{
/**
* @var string
Expand Down Expand Up @@ -57,24 +52,6 @@ protected function toHttp($phpValue)
return $phpValue ? $this->httpValue : null;
}

/**
* {@inheritdoc}
*
* @return bool|null
*/
protected function tryCast($value): ?bool
{
if ($value === null) {
return null;
}

if (!is_scalar($value)) {
throw new \TypeError('The import()\'ed value of a '.static::class.' must be a scalar value or null');
}

return (bool) $value;
}

/**
* {@inheritdoc}
*
Expand Down

0 comments on commit 2231fab

Please sign in to comment.