Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Controller e interfacce per le operazioni CRUD

Continuiamo a sviluppare la nostra applicazione d'esempio imparando a definire i controller per le operazioni CRUD (Create, Read, Update, Delete) con il framework PHP Laravel.
Continuiamo a sviluppare la nostra applicazione d'esempio imparando a definire i controller per le operazioni CRUD (Create, Read, Update, Delete) con il framework PHP Laravel.
Link copiato negli appunti

I primi controller CRUD

Dopo aver definito la struttura dei dati e dopo aver introdotto una reale persistenza dei dati, possiamo finalmente concentrarci sui controller più o meno ufficiali per il nostro progetto. La prima funzionalità che aggiungeremo metterà a disposizione del cliente alcune interfacce per le operazioni CRUD (Create, Read, Update, Delete) sulle varie entità disponibili. L'acronimo CRUD incapsula tutte le funzionalità base che vengono spesso implementate nei backoffice. Per il momento ci concentreremo sulle funzionalità core e tralasceremo altri aspetti come le performance e la sicurezza, questi argomenti, non secondari, li affronteremo in futuro.

Il core di Laravel non presenta un modulo per la gestione dell'HTML e dei form, ma esiste una dipendenza esterna, chiamata Laravel Collective Form, che permette di creare form in maniera dichiarativa e object-oriented. Laravel Collective è una suite che può includere diverse funzionalità, per il momento utilizzeremo solamente il modulo HTML. Una volta installato tramite Composer, dobbiamo configurare il provider e i due alias. Per il momento ignoriamo lo scopo di queste attività in quanto le approfondiremo nelle prossime lezioni.

Passiamo ora alla creazione dei controller tramite i comandi Laravel. Lanciamo quindi php artisan make:controller AuthorController e php artisan make:controller BookController e configuriamo le rotte in questo modo per il mapping automatizzato tra rotte e metodi:

Route::resource('author', 'AuthorController');
Route::resource('book', 'BookController');

L'implementazione dei controller è molto simile, all'interno dell'articolo approfondiremo in dettaglio solamente AuthorController e per quanto riguarda BookController le sole caratteristiche peculiari.

AuthorController

Partiamo quindi con la definizione del controller AuthorController analizzando metodo per metodo, ricordando che ogni metodo corrisponde ad un URL mappato automaticamente tramite Route::resource.

Il metodo index si occupa di mostrare l'elenco degli autori disponibili:

public function index()
{
    $authorList = Author::orderBy('firstname', 'asc')->orderBy('lastname', 'asc')->get();
    return View::make('author/index')->with('authorList', $authorList);
}

Il metodo è abbastanza banale: tramite Eloquent recuperiamo l'elenco degli autori ordinati per firstname e lastname e passiamo la lista ottenuta alla view author/index. I metodi create e store si occupano della creazione di un autore. Il primo permette di inizializzare un form vuoto, mentre il secondo della validazione e della persistenza:

public function create()
{
    $author = new Author();
    return View::make('author/form')->with('author', $author);
}
public function store(Request $request)
{
    $this->validate($request, Author::$rules);
    Author::create($request->all());
    return Redirect::to(route('author.index'));
}

Il primo metodo non fa altro che istanziare un nuovo autore vuoto e di passarlo a author/form, store invece invoca la validazione e in caso positivo salva l'autore all'interno del database e redirige l'utente alla pagina precedente.

Riguardo alla validazione, l'implementazione di store utilizza il metodo validate, ereditato dal controller grazie al trait ValidatesRequests che richiede l'oggetto request e un elenco di regole di validazione, nella fattispecie la proprietà statica $rules presente in Author. Avere le regole di validazione definite dentro il modello è una prassi consolidata in modo da poterle condividere senza doverle ripetere. Ecco come sono implementate:

public static $rules = [
    'firstname' => 'required|min:3|max:40',
    'lastname' => 'required|min:3|max:40'
];

I metodi edit ed update rappresentano gli speculari di create e store ma si occupano della modifica di un record esistente. Il primo recupera l'autore corrente dal database mentre il secondo valida e persiste:

public function edit($id)
{
    $author = Author::findOrFail($id);
    return View::make('author/form')->with('author', $author);
}
public function update(Request $request, $id)
{
    $this->validate($request, Author::$rules);
    $author = Author::findOrFail($id);
    $author->update($request->all());
    return Redirect::to(route('author.index'));
}

Rispetto ai due metodi precedenti l'unica differenza risiede nel fatto che non partiamo da un autore vuoto, ma lo recuperiamo, grazie a findOrFail dal database. L'ultimo metodo si occupa dell'eliminazione di un record:

public function destroy($id)
{
    Author::destroy($id);
    return Redirect::to(route('author.index'));
}

Una volta definiti i metodi passiamo alle view: index e form (riutilizzato sia in fase di create che di edit).

@section('content')
    <p>
        <a href="{{ route('author.create') }}">Create new author</a>
    </p>
    <table class="table">
        @foreach($authorList as $author)
            <tr>
                <td>{{ $author->firstname }} {{ $author->lastname }}</td>
                <td>{{ $author->bookCount }}</td>
                <td>
                    <a class="btn btn-default" href="{{ route('author.edit', ['author' => $author->id]) }}">Modifica</a>
                </td>
                <td>
                    {!! Form::open(['route' => ['author.destroy', $author->id], 'method' => 'delete' ]) !!}
                        {!! Form::submit('Elimina', ['class' => 'btn btn-danger']) !!}
                    {!! Form::close() !!}
                </td>
            </tr>
        @endforeach
    </table>
@endsection

index.blade.php si occupa di mostrare all'interno di una table i record ottenuti dal controller e di inserire i link per la modifica e l'eliminazione. Da notare che il metodo delete deve essere invocato utilizzando POST, quindi è necessario mascherare il form con un pulsante.

@section('content')
    {!! Form::model($author, [
        'route' => isset($author->id) ? ['author.update', $author->id] : 'author.store',
        'method' => isset($author->id) ? 'put' : 'post'
        ]) !!}
        <div class="form-group {{ $errors->has('firstname') ? 'has-error' : '' }}">
            {!! Form::label('firstname', 'Firstname') !!}
            {!! Form::text('firstname', null, ['class' => 'form-control']) !!}
            @foreach($errors->get('firstname') as $error)
                <span class="help-block">{{ $error }}</span>
            @endforeach
        </div>
        <div class="form-group {{ $errors->has('lastname') ? 'has-error' : '' }}">
            {!! Form::label('lastname', 'Lastname') !!}
            {!! Form::text('lastname', null, ['class' => 'form-control']) !!}
            @foreach($errors->get('lastname') as $error)
                <span class="help-block">{{ $error }}</span>
            @endforeach
        </div>
        <div class="form-group">
            {!! Form::submit('Save', ['class' => 'btn btn-primary']) !!}
        </div>
    {!! Form::close() !!}
@endsection

La seconda view, form.blade.php, è senza dubbio più interessante. Innanzitutto si nota come il form viene instanziato grazie all'helper Form::model che permette di associare automaticamente i campi HTML con le proprietà del modello passato come parametro. Inoltre differenziamo la rotta in base alla presenza o meno dell'id (se non ha id sarà una create altrimenti una edit). Per ogni campo viene mostrata label, campo di testo (gestito automaticamente da Form::model) e l'eventuale messaggio di errore.

BookController

Il secondo controller è sviluppato con gli stessi pattern visti in precedenza tranne per alcuni aspetti. Per esempio nel metodo index utilizziamo with per usare l'eager loading e ottimizzare il numero di query eseguite:

$bookList = Book::orderBy('title', 'asc')->with('author')->get();

Ciò è perché l'informazione dell'autore sarà necessaria all'interno della view:

<td>{{ $book->author->lastname }}</td>

Nei metodi create e edit, che devono preparare la form per la creazione e la modifica, sarà necessario recuperare la lista degli autori disponibili che serviranno per popolare una select dando all'utente la possibilità di scegliere l'autore del libro corrente. Cosi facendo costruiamo un array associativo chiave/valore (in particolare id/lastname) da passare alla view:

$authorList = Author::orderBy('lastname', 'asc')->orderBy('firstname', 'asc')->lists('lastname', 'id');

per poi utilizzarlo nella view grazie a Form::select:

{!! Form::select('author_id', $authorList, null, ['class' => 'form-control'])!!}

Conclusioni

Biblios ha finalmente ha raggiunto un livello di utilizzabilità accettabile. Dal prossimo articolo introdurremo aspetti avanzati che ci permetteranno di arricchire l'applicazione.


Ti consigliamo anche