Giter VIP home page Giter VIP logo

laravel-form-bridge's Introduction

Laravel Form Bridge

See http://symfony.com/doc/current/forms.html

Laravel integration:

  • Pre-set old input
  • Add validation errors
  • Translate field names

Install

  • composer require barryvdh/laravel-form-bridge
  • Add Barryvdh\Form\ServiceProvider::class, to you ServiceProviders.
  • (optional) Add 'FormFactory' => Barryvdh\Form\Facade\FormFactory::class, to your Facades.
  • (optional) Add 'FormRenderer' => Barryvdh\Form\Facade\FormRenderer::class, to your Facades.

Basic example

You can use the FormFactory to create a form. You can supply a Model as data, so it will fill the values.

You can use $form->handleRequest($request); to update the values in the user, or you can just use $request object or Input facade like usual. However, by default, the form is grouped under a form key, so you have to use $request->get('form') to get the form values. Or you can create a Named form, with an empty name.

If you need to set more options, use the createBuilder function instead of create, to be able to use setAction() etc. You need to call ->getForm() to get the actual form instance again.

use FormFactory;
use Illuminate\Http\Request;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

Route::any('create', function()
{
    $user = App\User::first();
    
    $form = FormFactory::create(FormType::class, $user)
        ->add('name', TextType::class)
        ->add('email', EmailType::class, [
            'rules' => 'unique:users,email',
        ])
        ->add('save', SubmitType::class, ['label' => 'Save user']);

    $form->handleRequest();

    if ($form->isSubmitted() && $form->isValid()) {
        // Save the user with the new mapped data
        $user->save();
        
        return redirect('home')->withStatus('User saved!');
    }

    return view('user.create', compact('form'));
});

Use the following in your Blade templates:

@formStart($form)
@formWidget($form)
@formEnd($form)

Other directives are: @form, @formLabel, @formErrors, @formRest and @formRow

@form($form)
@formStart($form)

<h2>Name</h2>
@formLabel($form['name'], 'Your name')
@formWidget($form['name'], ['attr' => ['class' => 'name-input']])

<h2>Rest</h2>
@formRest($form)

@formEnd($form)

Or use the following in your Twig templates to render the view:

{{ formStart(form) }}
{{ formWidget(form) }}
{{ formEnd(form) }}

See http://symfony.com/doc/current/book/forms.html#form-rendering-template for more options.

Traits

To make it easier to use in a Controller, you can use 2 traits:

ValidatesForms: Adds a validation method, similar to the ValidatesRequests trait: $this->validateForm($form, $request, $rules)

CreatesForms: Create a Form or FormBuilder:

  • createForm($type, $data, $options) -> Form for a type (form or a Type class)
  • createNamed($name, $type, $data, $options) -> Form with a given name
  • createFormBuilder($data, $options) -> FormBuilder with an empty name
  • createNamedFormBuilder($name, $data, $options) -> FormBuilder with a given name
use Barryvdh\Form\ValidatesForms;
use Barryvdh\Form\CreatesForms;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

class UserController extends Controller{

    use ValidatesForms, CreatesForms;
    
    public function anyIndex(Request $request)
    {
        $user = User::first();

        $form = $this->createFormBuilder($user)
            ->add('name', TextType::class)
            ->add('email', EmailType::class)
            ->add('save', SubmitType::class, ['label' => 'Save user'])
            ->getForm();

        $form->handleRequest($request);

        if ($form->isSubmitted()) {
            $this->validateForm($form, $request, [
                'name' => 'required',
                'email' => 'required|email',
            ]);

            $user->save();
        }

        return view('user', ['form' => $form->createView()]);
    }
}

Creating a named form:

use Symfony\Component\Form\Extension\Core\Type\FormType;

$form = $this->createNamed('user', FormType::class, $user) 
    ->add('name', TextType::class)
    ->add('email', EmailType::class)
    ->add('save', SubmitType::class, ['label' => 'Save user']);

See http://symfony.com/doc/current/book/forms.html for more information.

BelongsToMany relations

BelongsToMany behaves differently, because it isn't an actual attribute on your model. Instead, we can set the mapped option to false and sync it manually.

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

$builder->add('users', ChoiceType::class, [
    'choices' => \App\User::pluck('name', 'id'),
    'multiple' => true,
    'mapped' => false,
    'expanded' => true, // true=checkboxes, false=multi select
]);
$form->handleRequest($request);
if ($form->isSubmitted()) {
    $this->validate($request, $rules);

    $item->save();
    $item->users()->sync($form->get('users')->getData());

    return redirect()->back();
}

See for more options the choice type documentation.

Note: The BelongsToManyType is deprecated in favor of the ChoiceType from Symfony.

Translation labels

If you want to translate your labels automatically, just pass the translation key as the label attribute. It will run throught Twig's trans filter.

->add('name', TextType::class, ['label' => 'fields.name'])

Uploading Files

You can use the file type in the FormBuilder, and use the magic getFile() and setFile() method on your Model or mark it as not mapped, so you can handle it yourself. See http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html

Class User extends Model {

    /** @var UploadedFile  */
    private $file;

    public function getFile()
    {
        return $this->file;
    }

    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
    }

    public function upload()
    {
        // the file property can be empty if the field is not required
        if (null === $this->getFile()) {
            return;
        }

        // use the original file name here but you should
        // sanitize it at least to avoid any security issues

        // move takes the target directory and then the
        // target filename to move to
        $this->getFile()->move(
            $this->getUploadRootDir(),
            $this->getFile()->getClientOriginalName()
        );

        // set the path property to the filename where you've saved the file
        $this->path = $this->getFile()->getClientOriginalName();

        // clean up the file property as you won't need it anymore
        $this->file = null;
    }
}
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

$user = User::first();

$form = $this->createFormBuilder($user)
    ->add('name', TextType::class)
    ->add('file', FileType::class)
    ->add('save', SubmitType::class, ['label' => 'Save user'])
    ->getForm();
    
$form->handleRequest($request);

if ($form->isValid()) {
    $user->upload();
    $user->save();
}

Extending

You can extend some of the arrays in the ServiceProvider, eg. to add Types, add this to the register() method in your own ServiceProvider:

$this->app->extend('form.types', function($types, $app){
    $types[] = new CustomType();
    return $types;
});

laravel-form-bridge's People

Contributors

barryvdh avatar bonroyage avatar causamortis avatar kasparsklavins avatar laravel-shift avatar luukschakenraad avatar martintern avatar mmp2p avatar motammem avatar patrickbussmann avatar pinodex avatar robinvalk avatar robotomarvin avatar rojtjo avatar sailingdeveloper avatar sergiy-petrov avatar swenvanzanten avatar vv12131415 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  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  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

laravel-form-bridge's Issues

The option "constraints" does not exist.

Is there any way to use Symfony's validator?

I tried adding this in an extension as a test, and the "constraints" does not exist. error seems to be gone, but validations aren't working. It just proceed ignoring any constraints.

protected function loadTypeExtensions()
{
    return array(
        new \Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension(
            new \Symfony\Component\Validator\Validator\RecursiveValidator(
                new \Symfony\Component\Validator\Context\ExecutionContextFactory(
                    new \Symfony\Component\Translation\Translator('en')
                ),

                new \Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory(
                    new \Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader
                ),

                new \Symfony\Component\Validator\ConstraintValidatorFactory
            )
        )
    );
}

Error with view rendering

Hi,
I got error while running following code. Please help me.

Controller -


use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
public function create(\Illuminate\Http\Request $request)
    {
        $user = \App\User::first();
        $form = app('form.factory')->create(FormType::class, $user)
            ->add('name', TextType::class)
            ->add('email', EmailType::class)
            ->add('save', SubmitType::class, array('label' => 'Save user'));
        $form->handleRequest($request);
        return view('sessions.create',['form' => $form->createView()]);
    }

View


 {{ form_start(form) }}
 {{ form_widget(form) }}
 {{ form_end(form) }}

Error-


 ErrorException in create.twig line 15:
The "Symfony\Bridge\Twig\Extension\FormExtension" extension is not enabled 

Laravel validation

To make it easy to validate, we can use the Laravel validator with Symfony forms:

 $form = $factory->create(FormType::class, $user)
        ->add('name', TextType::class, [
            'rules' => 'required',
        ])
        ->add('email', EmailType::class, [
            'rules' => 'email|unique'
        ]);

This would run the validator post-submit, so you can check $form->isValid() after submitting the form. When failed, the errors on the form are added.

This seems to work already with this commit: 811ca4c

Next step would be to:

  • Make the required option based on the rule, but this would change the default (Symfony default is required = true), to make front-end always in sync with back-end validation
  • Add HTML5 attributes for rules (eg; min:3 -> minlength attribute etc)

PHP 7.4 - not using Template

I'm just testing this package on PHP 7.4 before going live.

We have a theme that's defined to be used in the config/form.php file, however, it seems that is not being used at all.

On our PHP 7.2 installation, I see the HTML as:

<form name="my_form" method="post">
    <table id="my_form">
        <tbody>
            // the form content laid out in relevent tr/td's
        </tbody>
    </table>
</form>

However, on PHP 7.4, the exact same code renders out:

<form name="my_form" method="post">
    <tableid="my_form">
        // Form Elements with relevent tr/td etc
    </table>
</form>

Note, the <tableid> tag, rather than <table id="">.

Has anyone run into this issue at all? I'm wondering if it's beacuse the system is seeing the HTML tag as a "formid" tag, rather than a form tag with an ID attribute?

Whole form is rendered, even though just one row is called

In my form builder I have defined a name and email field (from the provided samples). In the view I only call the name field, yet the whole form is being rendered. This is the correct way of calling a row, right?

{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_end(form) }}

Error after some updates

I performed some updates to my code, and all works perfectly fine in my development environment, but when I pushed it out onto my production environment, I'm seeing the below error:

Unable to load the "Symfony\Component\Form\FormRenderer" runtime in "bootstrap_3_layout.html.twig". (View: /path/to/view.blade.php) (View: /path/to/view.blade.php)

Any ideas why this might be? PHP versions between live and dev are the same, all be it dev is run on centos, where live is on gentoo.

Looks like it's being thrown in vendor/twig/twig/lib/Twig/Environment.php:663.

Cache does not refresh after template changes?

Hi @barryvdh and community

What I've done:
I've overridden/extended the bootstrap 4 template.
config/form.php

[
    'theme' => 'bootstrap_4_customization.html.twig',   
    'template_directories' => [  
        resource_path('views/form')   
]

resources/views/form/bootstrap_4_customization.html.twig

{% extends 'bootstrap_4_layout.html.twig' %}

{% block form_row -%}
...

What's the question/issue:
Is it right that changes made at the template do not trigger a rebuild of the twig cached files?

Is there a way to configure such a cache rebuild or is this an issue?

Directives not work

Hi, Barry.

The directives indicated in the readme file are not the correct ones, I think it is lacking to update.

@formStart($form)
@formWidget($form)
@formEnd($form)

It worked for me this way:
@form_start($form)

Change form template on the fly

Hi @barryvdh !

Thank you so much for the package, it saved my life!

I wonder if there is a way of change the default form template on the fly. Maybe from a Blade view, form class or the controller...

Like symfony does from a Twig view:
{% form_theme form 'foundation_5_layout.html.twig' %}

Found that using the Config facade I can do it:
Config::set('form.theme', 'form_table_layout.html.twig');

But this it's a bit ugly because it changes the template for the following forms in the same request.

Regards,
Pablo.

Twig caching

Twig appears to be parsed on each request.
What would be the best way to go about implementing caching for twig templates?

Array Validations not showing in twig

Hi @barryvdh,
First of all, thank you very much for the package!, it's helping me a lot with a new project.

Like de title says, i need some guide to figure out why i cant get array validations errors in twig.
I can see those errors in blade ViewErrorBag, but not in twig.

Here a simple example:

web.php

Route::any('/test', 'FormController@demo');

FormController.php

public function demo(Request $request){
        $form = $this->createFormBuilder(null)
            ->add('name', TextType::class)
            ->add('file', FileType::class, [
                'multiple' => true
            ])
            ->add('save', SubmitType::class, ['label' => 'Save'])
            ->getForm();

        $form->handleRequest($request);

        if ($form->isSubmitted()) {
            $this->validateForm($form, $request, [
                'name' => 'required|min:10',
                "file" => "required|max:2",
                "file.*" => "file|max:1024|mimes:png"//Can't get this validations errors in twig
            ]);
            dd($form->getData());
        }

        return view('formularios.form_test', ['form' => $form->createView()]);
    }

form_test.blade.php

...
<div class="container">
        <div class="row">
            <div class="col-md-12">
                @formStart($form, [ 'attr' => ['autocomplete' => 'off']])
                @formWidget($form)
                @formEnd($form)
            </div>
        </div>
        <div class="row">
            <div class="col-md-12">
                @if ($errors->any()) //to check in ViewErrorBag
                    <div class="alert alert-danger">
                        <ul>
                            @foreach ($errors->all() as $error)
                                <li>{{ $error }}</li>
                            @endforeach
                        </ul>
                    </div>
                @endif
            </div>
        </div>
    </div>
...

And, for example if I add three jpg files (the validation only accept png), this is what i get:

image

As you can see, twig only shows the 'file' validation, but not the 'file.*' one.
Any help would be appreciated.

Regards,
Leo

Nested form types validation

Hey firstname,

Do you have any idea with nested form types validation with rules?

For instance:

class FooType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('custom', CustomType::class, $options);
        $builder->add('custom2', CustomType2::class, $options);
    ....

These types are always valid.
The rules are in the CustomType fields.

class CustomType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email', EmailType::class, [
                'rules' => [
                    'unique:user,email'
                ]
            ])

suggestion for syntax

Compare these two syntax :

$form = app('form.factory')->create('form', $user)
->add('name', 'text')
->add('email', 'email')

->add('save', 'submit', array('label' => 'Save user'));

$form = app('form.factory')->create('form', $user)
->text('user_name')
->email(''email_address')
->submit('save', array('label' => 'Save user'));

I think the second one read better, and IDE auto-completion is better for field types.
and it is simple to implement for you. it just requires some wrapper methods on current methods :

public function text($name) {
$this->add($name, 'text');
return $this;
}

I hope it makes help.
I have created such a form builder for a zend based application.

Use blade instead of twig

Hi @barryvdh,

I love the approach of the Symfony Forms library, so I'm happy with your bridge!

However, I don't feel that comfortable using a small piece of twig while Laravel uses blade. It feels overkill and I can imagine performance of the application won't get better since we have an extra render library.

I am wondering if we can make this library work with blade support, so we don't have to use twig anymore. I won't ask you to build this, but can you give me any directions? Should I lean on this library, or should I implement my own 'bridge' on Symfony Forms?

If you want to discuss this in more detail (in dutch :)), feel free to get in touch!

Regards, Bob

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.