19 View per visualizzare eliminare i dati

Ora che abbiamo i metodi per visualizzare ed eliminare i records, creiamo una views che ci permetterà di formattarli correttamente.

Creaiamo albums.blade.php in resources/views che sarà il file incaricato di restituite la view per gli albums. Nel metodo index() invece che restituire la select, restituiremo un array di dati da passare alla view albums

return view('albums', ['albums' => DB::select($sql, $where)]);

quindi la view caricata sarà albums.blade.php che riceverà i dati della DB::select(), facciamo un test in albums.blade.php con dd() verificando che escano i dati

dd($albums);

creiamo la view

iniziamo col dire che questo layout estende quello di default (resources/views/templates/layout.blade.php) e dichiariamo la @section content che conterrà gli albums

@extends('templates.layout')
@section('content')

@endsection

adesso con un @foreach andiamo a ciclare gli albums

<ul>
 @foreach($albums as $album)
 @endforeach
</ul>

inseriamo nel @foreach gli <li> che conterranno il nome degli album

@foreach($albums as $album)
 <li>
  {{$album->album_name}}
 </li>
@endforeach

proviamo ad aprire gli albums e verifichiamo che escano i nomi. Approfittiamo per mettere un link sulla navbar agli albums

<li class="nav-item">
 <a class="nav-link" href="/albums">Albums</a>
</li>

aggiungiamo a fianco al nome un <a> per creare un bottone per cancellare l’album

<li>
 <p> {{$album->album_name}} </p>
 <a class="btn btn-danger">DELETE</a>
</li>

aggiungiamo l’ href che conterrà il segnaposto per l’id dell’album e la rotta per la cancellazione

<a href="/albums/{{$album->id}}/del" class="btn btn-danger">DELETE</a>

per tornare indietro dalla pagina/abums/del, mettiamo nella funzione delete() del controller un redirect()->back()

public function delete(int $album)
{
 $sql = 'DELETE FROM albums WHERE id=:id';
 DB::delete($sql, ['id' => $album]);
 return redirect()->back();
}

aggiungiamo l’id per vedere il record eliminato ed eliminiamo il tag <p> e aggiungiamo un titolo <h1> e un po’ di classi per migliorare l’estetica.

Ecco la nostra view

@extends('templates.layout')
 @section('content')
 <h1>Albums</h1>
 <ul class="list-group">
  @foreach($albums as $album)
   <li class="list-group-item d-flex justify-content-between">
    {{$album->id}} {{$album->album_name}}
     <a href="/albums/{{$album->id}}/del" class="btn btn-danger">DELETE</a>
   </li>
  @endforeach
 </ul>
@endsection

Cancelliamo con jQuery e AJAX

Vediamo come cancellare i dati utilizzando AJAX, per prima cosa nella view aggiungiamo un @section footer per iniettare codice javascript tramite @parent

@section('footer')
 @parent
  <script>
   alert('ciao');
  </script>
@endsection

con jQuery aspettiamo il caricamento della pagina con la funzione ready() applicata al document. Quando il DOM sarà pronto eseguiamo la funzione anonima

<script>
 $('document').ready(function () {

 });
</script>

la funzione ascolterà tramite un listener se è stato premuto il bottone delete, il listener lo applicheremo solo sull’ <ul> e non su ogni <li>, altrimenti si appesantirebbe il codice.

$('document').ready(function () {
 $('ul').on('click', 'a', function () {

 });
});

seleziono <ul>, con il metodo on() inserisco l’evento click, applicato al tag <a> e quando avviene lancio la funzione

$('ul').on('click', 'a', function (ele) {
 ele.preventDefault();
 alert(ele.target.href);
});

la funzione riceve l’elemento cliccato, annulla il comportamento grazie al metodo preventDefault(), per prova stampiamo l’ href dell’elemento con target.href.

Per selezionare l’href con jQuery si può anche usare attr()

$(this).attr('href');

entrambi i modi vanno bene, mettiamo l’href in una variabile e

facciamo la chiamata AJAX

var urlAlbum = $(this).attr('href');
$.ajax(urlAlbum,
 {
  complete : function (resp) {
  alert(resp.responseText);
 }
})

il metodo $.ajax() di jQuery riceve come primo parametro l’url, poi ha diverse opzioni per specificare il metodo, noi per ora specifichiamo solo il metodo complete che verrà eseguito quando la chiamata è finita e lancerà la funzione che riceverà la response e la verifichiamo stampando la sua proprietà responseText.

Nel metodo delete() del controller noi restituivamo la redirection()->back(), con la chiamata ajax non abbiamo più bisogno del redirezionamento, percui lo commentiamo e restituiamo direttamente il delete()

public function delete(int $album)
{
 $sql = 'DELETE FROM albums WHERE id=:id';
 return DB::delete($sql, ['id' => $album]);
 // return redirect()->back();
}

Se viene restituito il valore 1 il record è stato eliminato, quindi

complete : function (resp) {
 if (resp.responseText == 1) {

 }
 else {
  alert('Problemi con il server');
 }

se il resp.responseText è uguale a 1 cancelliamo il tag <li> corrispondente, altrimenti errore. Per cancellare memorizziamo il tag <li> selezionato in una variabile

var li = ele.target.parentNode;

utilizziamo removeChild() per rimuovere l’elemento

if (resp.responseText == 1) {
 li.parentNode.removeChild(li);
}

oppure jQuery basta fare

$(li).remove();

ecco il codice completo per lo script

@section('footer')

@parent

<script>
$('document').ready(function () {
$('ul').on('click', 'a',function (ele) {
 ele.preventDefault();
 var urlAlbum = $(this).attr('href');
 var li = ele.target.parentNode;
 $.ajax(
  urlAlbum,
  {
   complete : function (resp) {
   console.log(resp);

   if(resp.responseText == 1){
    alert(resp.responseText)
    li.parentNode.removeChild(li);
    // $(li).remove();
    } else {
    alert('Problem contacting server');
    }
   }
 })
});

});

</script>

@endsection

Eliminiamo con DELETE e CSFR

Ritorniamo a passare la chiamata tramite metodo HTTP DELETE come è corretto fare, quindi correggiamo il ruote in web.php da get() a delete()

Route::delete('albums/{albumo}', [AlbumsController::class, 'delete']);

Leviamo il commento da app/Http/kernel.php alla riga VerifyCsrfToken::class

Per testare possiamo anche usare il tool presente phpStorm: Tools/HTTPClient/TestRestRestful Web Service e provando col metodo HTTP DELETE riceviamo un TokenMismatchException nell’html della risposta.

Laravel crea un Token di sessione che dobbiamo restituire nella richiesta per verificare la corretta chiamata e prevenire il Cross-site request forgery. Il middleware di Laravel verifica la corrispondenza del token che però nel nostro caso non esiste.

Reperiamolo dal nostro template con csrf_token()

@section('content')
 <h1>Albums</h1>
 {{csrf_token()}}

Comunque non possiamo inserirlo nella chiamata anche copiandolo dal browser, perchè quel token che ci compare è relativo solo per quella sessione del browser. Cancelliamo il percorso /del dal template

<a href="/albums/{{$album->id}}" class="btn btn-danger">DELETE</a>

ora diciamo ad AJAX che utilizziamo il metodo DELETE

$.ajax(
 urlAlbum,
 {
  method: 'delete',
  complete : function (resp) {

Ora creiamo un form che effettui la chiamata tramite delete e passi il token, il nome che deve essere passato è _token

<h1>Albums</h1>
<form>
 <input type="hidden" name="_token" id="-token" value="{{csrf_token()}}">
 <ul class="list-group">

adesso passiamo questo token via AJAX mediante il parametro data: che passerà un nome _token che avrà come valore quello recuperato dall’ <input id=”_token”> tramite il metodo val()

method: 'DELETE',
data:{
 _token:$('#_token').val()
},