Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vjacob's avatar
Level 15

How to Load Form Macros in Laravel 5

In Laravel 5, If you are using form macros, they you may have noticed that the csrf token will not be generated when using Form::open() depending on how you are loading the macros. This is because macros have to be loaded after the HTMLServiceProvider is called. To fix this, load your macros like this

<?php namespace App\Providers;

use Illuminate\Html\HtmlServiceProvider;

class MacroServiceProvider extends HtmlServiceProvider
{
 /**
  * Register the application services.
  *
  * @return void
  */
 public function register()
 {
        // Macros must be loaded after the HTMLServiceProvider's
        // register method is called. Otherwise, csrf tokens
        // will not be generated
    parent::register();

        // Load macros
        require base_path() . '/path/to/your/macros.php';
 }
}
~~~

This way, macros are loaded after the HTMLServiceProvider and CSRF tokens will be generated automatically using Form::open(). Just make sure to include it in your config/app.php

0 likes
15 replies
pmall's avatar

Ow cool. I didn't understood why this didnt work. But I think it should and will be fixed out of the box somehow.

bestmomo's avatar

Why extend HtmlServiceProvider ? Just create a new service provider for macros and all is fine. But I think the best solution, rather than macro is just extend FormBuilder with new methods.

stonebitsrl's avatar

Hello bestmomo "But I think the best solution, rather than macro is just extend FormBuilder with new methods."

Explain me please how can I extend FormBuilder with new methods. It is registered with HtmlServiceProvider, so shold I rewrite HtmlServiceProvider too?

Thanks.

bobbybouwmann's avatar

@stonebitsrl It's really easy! Here is an example

First you need to create a new ServiceProvider. Let's call it HtmlServiceProvider.php So in the app\Services\Html directory we create this file

<?php namespace App\Services\Html;

class HtmlServiceProvider extends \Collective\Html\HtmlServiceProvider {

    /**
     * Register the form builder instance.
     */
    protected function registerFormBuilder()
    {
        $this->app->bindShared('form', function($app)
        {
            $form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken());

            return $form->setSessionStore($app['session.store']);
        });
    }

}

Note: I use the laravelcollective/html package for the formbuilder. This package is not maintained by Taylor anymore, but it is the same as illuminate/html

Now we need to create a FormBuilder class. I did this in the same directory, so App\Services\Html\FormBuilder.php

<?php namespace App\Services\Html;

class FormBuilder extends \Collective\Html\FormBuilder {

    public function textfield($name, $label, $errors, $labelOptions = array(), $inputOptions = array())
    {
        $labelOptions['class'] = 'form-label';
        $inputOptions['class'] = 'form-control';
        $inputOptions['placeholder'] = $label;

        return sprintf(
            '<div class="form-group">%s<div%s>%s%s</div></div><!-- end form-group -->',
            parent::label($name, $label, $labelOptions),
            $errors->has($name) ? ' class="error-control"' : '',
            parent::text($name, null, $inputOptions),
            $errors->has($name) ? '<span class="error"><label class="error" for="' . $name . '">' . $errors->first($name) . '</label></span>' : ''
        );
    }

    public function submit($value = null, $options = [])
    {
        $options['class'] = 'btn btn-cons btn-success' . (isset($options['class']) ? ' ' . $options['class'] : '');

        return parent::submit($value, $options);
    }

}

As you can see I extend the FormBuilder class from the laravelcollective\html package. If you use the Illuminate\html package then you need to extend that class of course.

Now you can override any function you want. I give a little example with the submit button and the textfield I use for my current project.

I will give you an example on how you can call them in your view, but first we need to register the FormBuilder. To do that we need to update the config/app.php file

// in your providers array remove the service provider from the package and replace it with the new service provider
'providers' => [
    
    // .... Other service providers
    
    'App\Providers\DatabaseServiceProvider',
    'App\Providers\RouteServiceProvider',
    'App\Services\Html\HtmlServiceProvider' // THe service provider we just created

];

You don't have to update the aliases array but it should look like this

'aliases' => [
     
    // ... Other aliases
    
    'Form'          => 'Collective\Html\FormFacade',
    'Html'          => 'Collective\Html\HtmlFacade'

];

And I call both examples like this in my views

{!! Form::textfield('client_name', 'Client Name', $errors) !!}

{!! Form::submit('Submit my aweomse Form') !!}

You can override each function like you wish! If you don't override the function it will use the default function from the package ;)

Happy coding :D

5 likes
pmall's avatar

Why cant we just add macros in a service provider boot method ?

bestmomo's avatar

@pmall as usual there are many ways to get the same result. I think that extend a class is the more natural way. But macros are also fine :)

pmall's avatar

I mean, the way to add method to the form builder is to use form macros. What I see above is very complex.

bestmomo's avatar

@pmall I dont see it complex, just with more code and as I said above, more natural. The fact that this class is macroable doesn't mean that the way to add functionalities is to use macros, but that it's possible to do that.

pmall's avatar

The fact that this class is macroable doesn't mean that the way to add functionalities is to use macros

Of course it does mean this. I disagree on this.

2 likes
Giolf's avatar

@bobbybouwmann Great explanation, but i have a question:

If i understand at this point you can disable the "original" HtmlServiceProvider in your config/app.php file. Because what are you doing is to register a new provider that extends the Class HtmlServiceProvider, so this new provider can does exactly the same things of the other one with more feauters provided by the methods of his extended class.

so why have 2 Service Provider when with the Child class you can access on the method of the both Classes ?

edit Ok it's late and i didn't see your comment:

// in your providers array remove the service provider from the package and replace it with the new service provider

So your anwser is perfectly perfect ;)

gildniy's avatar

I think this is better:


    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        foreach (glob(base_path('resources/macros/*.macro.php')) as $filename) {
            require_once($filename);
        }
    }
netm's avatar

If you are trying to use this with Laravel 5.3 you need to add app['view'] as the 5.3 Collective version interface has changed. So:

$form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken()

Becomes

$form = new FormBuilder($app['html'], $app['url'], $app['view'], $app['session.store']->getToken()

Please or to participate in this conversation.