3

I'd like to generate a url with a 'PUT' or 'POST' method requirement, via a <a href> link.

Since I use a framework server-side, I use a fake form to do this - example with PUT method :

<form action="someActionUri" method="post">
      <input type="hidden" name="_method" value="PUT" />
      <input type="submit" value="GO PUT" />
</form>

But, I'd like to use a simple <a> link :(

BENARD Patrick
  • 30,363
  • 16
  • 99
  • 105

3 Answers3

5

An anchor <a href="#"> will always use a GET request. There is no way to modify that. But, it is possible to fake a PUT or DELETE request using Symfony.

Symfony Forms will fake the verb by adding a hidden field to forms.

<input type="hidden" name="_method" value="PUT" />

Then internally it checks if the verb is POST, checks for the _method parameter and changes the verb from POST to PUT. This only works for HTML forms using the POST method.

The same can be done for GET verbs, but it requires using an Event Listener. Here is an example:

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;

class VerbListener
{
    public function onKernelRequest ( GetResponseEvent $event )
    {
        $request = $event->getRequest();

        if ( HttpKernel::MASTER_REQUEST === $event->getRequestType() 
                && $request->getMethod() === 'GET' )
        {
            if ( $request->query->get('_method') === 'PUT' ) {
                $request->setMethod( 'PUT' );
            }
        }
    }
}

And the service must be registered with a higher priority than the router_listener service, which matches the route to a controller.

services:
    acme.verb.listener:
        class: Acme\Bundle\Listener\VerbListener
        tags:
            - { name: kernel.event_listener,
                event: kernel.request,
                method: onKernelRequest,
                priority: 100 }  

The link can now be generated with the _method parameter

<a href="foo?_method=PUT"></a>
Twifty
  • 3,267
  • 1
  • 29
  • 54
1

In the routing file :

entity_edit:
    pattern: /entity/{id}/edit
    defaults: {_controller: MyBundle:Entity:put}

My controller (with fosRest):

/**
 * @Rest\View()
 */
public function putAction(Request $request, $id)
{
...code   ...
}

My TWIG:

<a href="{{ path('entity_edit', {'id': data.id} ) }}" >Edition</a>
BENARD Patrick
  • 30,363
  • 16
  • 99
  • 105
0

Even if I like @twifty response, I think that is a bit overkill to listen to every single request just to adapt some of them. It's much simpler to output a form instead of your link, so instead of <a href="{{ path('my_route_with_put_method') }}">link</a>, you can simply use:

<form action="{{ path('my_route_with_put_method') }}" method="POST">
    <button>link</button>
    <input type="hidden" name="_method" value="PUT">
</form>

You just need to pay attention, because you're using a block element (form) instead of an inline element (a), but you can easily go around that (for example including other inline elements inside the form or using CSS)

Massimiliano Arione
  • 2,422
  • 19
  • 40