4

I've created a restful API using laravel controllers. I have a PhotosController which has a destroy($id) method for resource deletion. also I have a piece of javascript code that sends a DELETE request to my app. the result should be the deletion of the photo with $id id. but laravel doesn't route my request to destroy method. instead it sends an 401 Unauthorized error.

the thing is that I want to send DELETE request to my app via Ajax, but laravel doesn't let my request to be routed!

routes.php file :

Route::resource('photos', 'PhotosController');

destroy method :

public function destroy($id)
{
    try{
        unlink($_SERVER["DOCUMENT_ROOT"].'/uploads/doctors/' . $id);
        Session::forget('photo');
        $msg = Notification::where('flag', 's')->where('code', 'user-update-delete-photo-gallery')->first()->msg;
        return Response::json(array('success' => $msg));
    }catch (Exception $e){
        App::abort(500, $e->getMessage());
    }
}

my Ajax request :

$.ajax(
    {
        url: "/photos/" + name,
        method : "DELETE", // Or POST : result is the same
        data :{
            _token : $("input[name=_token]").val(),
            _method : 'DELETE'
        },
        success: function(data, textStatus, jqXHR ){
            parent.replaceWith("");
            toastr.success(data['success']);
            $("#overlay").hide();
        },
        beforeSend : function(jqXHR, settings ){
            $("#overlay").show();
        },
        error : function(jqXHR, textStatus, errorThrown ){
            toastr.error(jqXHR.responseText);
            $("#overlay").hide();
        }
    }
);

Thanks for your help.

  • Maybe it is because the request is using `method : "DELETE"`. I think the app is probably expecting POST. Isn't the `_method : 'DELETE'` in the data array supposed to take care of that? – Don't Panic Apr 27 '15 at 15:36
  • @Don'tPanic thank you for your participation. I've used `POST` but the result is the same. –  Apr 27 '15 at 15:37
  • One more thing, don't resource controller method names need to be prefixed with the HTTP method? – Don't Panic Apr 27 '15 at 15:39
  • @Don'tPanic no, in Laravel 4.2 there is no need to do that. take a look at [Laravel Documentation](http://laravel.com/docs/4.2/controllers) –  Apr 27 '15 at 15:41
  • Are you sure? I was just looking [here](http://laravel.com/docs/4.2/controllers#implicit-controllers), specifically. Where it says "Next, just add methods to your controller, prefixed with the HTTP verb they respond to:" – Don't Panic Apr 27 '15 at 15:43
  • What's in the log? And do you definitely have a form on the page with the CSRF token in the `_token` input field? – Matt Gibson Apr 27 '15 at 15:44
  • Sorry, I just noticed that is for `Route::controller`, not for `Route::resource`. – Don't Panic Apr 27 '15 at 15:45
  • @Don'tPanic That's true for additional implicit methods, but the standard methods in [the table under "RESTful resource controllers"](http://laravel.com/docs/4.2/controllers#restful-resource-controllers) definitely work in 4.2. – Matt Gibson Apr 27 '15 at 15:46
  • What version of jQuery do you use? Are you sure the token string is sent correctly? – Yasen Zhelev Apr 27 '15 at 16:23

2 Answers2

7

I do this sort of thing all the time in my Laravel Apps with no issues. This code allows the user to delete a resource through AJAX while presenting a bootstrap confirmation dialog first. The code is laid out in the order the events would occur.

VIEW WITH RESOURCE TO DELETE

<a class="delete-plan" href="{{ route('admin.plans.destroy', $plan['id']) }}" data-redirect="{{ route('admin.plans.index') }}" data-plan-name="{{ $plan['name'] }}" data-lang="billing.plans">
    <i class="fa fa-trash fa-lg"></i>
</a>

JQUERY TO PROMPT CONFIRMATION MODAL

$('.delete-plan').on('click', function(e) {
    e.preventDefault();

    var data = {
        'route':        $(this).attr('href'),
        'redirect':     $(this).data('redirect'),
        'modal_title':  'Delete Plan',
        'content_view': 'Are you sure you want to delete plan: <strong>' + $(this).data('plan-name') + '</strong>?',
        'lang':         $(this).data('lang')
    };

    loadDestroyModal(data);
});

function loadDestroyModal(data) {
    $.get('/ajax/destroy-modal', { data: data }, function(modal) {
        $('body').append(modal);
        $('#destroy-modal').modal('show');
    });
}

AJAX CONTROLLER

// routed by /ajax/destroy-modal
public function destroyModal() {
    $data = Input::get('data');

    $params = [
        'route'    => $data['route'],
        'redirect' => $data['redirect'],
        'title'    => $data['modal_title'],
        'content'  => $data['content_view'],
        'lang'     => $data['lang']
    ];

    return View::make('_helpers.modal-destroy', $params);
}

DESTROY CONFIRMATION MODAL (_helpers.modal-destroy)

<div id="destroy-modal" class="modal fade">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal">
                    <span aria-hidden="true"><i class="fa fa-times"></i></span>
                    <span class="sr-only">Close</span>
                </button>
                <h4 class="modal-title">{{ $title }}</h4>
            </div>
            <div class="modal-body">
                {{ $content }}
            </div>
            <div class="modal-footer">
                <button id="modal-confirm" type="button" class="btn btn-primary" data-route="{{ $route }}"
                data-redirect="{{ $redirect }}" data-lang="{{ $lang }}">Confirm</button>
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>

JQUERY TO PROCESS DESTROY METHOD AND REDIRECT FLASH MESSAGE

$('body').on('click', '#destroy-modal #modal-confirm', function(e) {
    var redirect = $(this).data('redirect');
    var lang     = $(this).data('lang');

    $(this).html('<i class="fa fa-spinner fa-spin"></i> Please Wait');

    $.ajax({
        'url':     $(this).data('route'),
        'type':    'DELETE',
        'success': function(response) {
            if (response) {
                redirectWithFlashMessage(redirect, 'destroy', 'success', lang);
            } else {
                redirectWithFlashMessage(redirect, 'destroy', 'errors', lang);
            }
        }
    });
});

PLANS CONTROLLER

public function destroy($id)
{
    try
    {
        Stripe::plans()->destroy(['id' => $id]);

        return Response::json(TRUE);
    }
    catch (Exception $e)
    {
        return Response::json(FALSE);
    }
}

JQUERY FOR REDIRECTION

function redirectWithFlashMessage(redirect, type, status, lang) {
    var params = {
        type:   type,
        status: status,
        lang:   lang
    };

    $.get('/ajax/flash', params, function(response) {
        window.location.href = redirect;
    });
}

AJAX CONTROLLER (Redirect with Flash)

public function flashData() {
    $message_type = 'success' == Input::get('status') ? 'success' : 'failure';

    $message = Lang::get(Input::get('lang'))[Input::get('type') . '_' . $message_type];

    Session::flash($message_type, $message);

    return ['status' => $message_type, 'message' => $message];
}

It's a lot of code but once setup it's extremely easy to replicate.

Jared Eitnier
  • 7,012
  • 12
  • 68
  • 123
0

I think your system's requiring the authentication for controller action "destroy" method. So you need to login before calling that method.

If you're using the middleware "auth" (app\Http\Middleware\Authenticate.php), you can easy find the "handle" function that returning "Unauthorized" error.

Hope this will help.

Brilliant-DucN
  • 706
  • 8
  • 12