LoginSignup
9
11

More than 3 years have passed since last update.

Laravel Livewire を使って動的テーブル表示を簡単に作る JavaScriptを1行も書かずに

Last updated at Posted at 2019-11-28

livewire_demo.gif

Laravel Livewireとは

CALEB PORZIOが作った Laravel用パッケージ
https://github.com/calebporzio/laracasts-livewire-datatable
https://laravel-livewire.com/
動的なテーブル表示を簡単につくれる

2019/11/26 に CALEB PORZIOがLaracastsにチュートリアル動画をアップした。
https://laracasts.com/series/guest-spotlight/episodes/3

 CALEB PORZIO の チュートリアル動画を参考にして Books動的テーブル表示を作ってみました。view blade, Controller, Modelはほぼ彼のチュートリアル動画の内容と同じです。

CALEB PORZIOのgithubのサンプルはMySQLで動かない

CALEB PORZIOはsqliteで 動作確認をしているが .envのDB_CONNECTIONをmysqlに変更すると動かない。原因はapp/Http/Livewire/ContactsTable.phpがorder by ``のようなSQLを発行してしまうからで13行目にある public \$sortField; を public \$sortField = 'id';などに変更するとMySQLでも動作するようになる。

Laravel project

Table

tablesとauthorsテーブルを用意してseederでbooksを500レコード authorsを3レコード作成。適当なfactoryも作る。 php artisan migrate; php artisan db:seed でデータを用意。

class CreateBooksTable extends Migration {
    public function up() {
        Schema::create('books', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');  // ←追加
            $table->unsignedInteger('author_id');  // ←追加
            $table->timestamps();
        });
    }
}
class CreateAuthorsTable extends Migration {
    public function up() {
        Schema::create('authors', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');  // ←追加
            $table->timestamps();
        });
    }
}
class BooksTableSeeder extends Seeder {
    public function run()
    {
        factory(\App\Book::class, 500)->create();  // ←追加
    }
}
class AuthorsTableSeeder extends Seeder {
    public function run()
    {
        factory(\App\Author::class, 3)->create();  // ←追加
    }
}

LivewireでBooks一覧画面を作る

Livewireのインストール

コンポーザーでインストール

composer require livewire/livewire

make:livewireでbladeとClassを作る

php artisan make:livewire books-table

以下のファイルが作られる
CLASS: app/Http/Livewire/BooksTable.php
VIEW: resources/views/livewire/books-table.blade.php

routes/web.php

一行だけ追加

Route::get('books', 'BooksController');

app/Author.php

booksテーブルとのリレーションだけ追加。

class Author extends Model
{
    protected $guarded = [];

    public function books()
    {
        return $this->hasMany(Book::class);
    }
}

app/Book.php

Authorテーブルへのリレーションを追加。
検索メソッドを追加。(CALEB PORZIOのContacts.phpを参考にしました。ほぼ同じです。)

class Book extends Model
{
    protected $guarded = [];

    public static function search(string $query)
    {
        $res = empty($query) ? static::query()
            : static::where('title', 'like', '%' . $query . '%');
        return $res;
    }

    public function author()
    {
        return $this->belongsTo(Author::class);
    }
}

app/Http/Controllers/BooksController.php

    public function __invoke(Request $request)
    {
        $books = \App\Book::paginate(); // ←この処理使われていない. $booksはBooksTable.phpで取得するから
        return view('books', ['books' => $books]);
    }

app/Http/Livewire/BooksTable.php

CALEB PORZIOのapp/Http/Livewire/ContactsTable.phpとほぼ同じです。booksテーブル用に少しだけ変更しました。

class BooksTable extends Component
{
    use withPagination;

    public $perPage = 10;
    public $search = '';
    public $sortField = 'id';
    public $sortAsc = true;

    public function clear() {
        $this->search = '';
    }

    public function sortBy($field) {
        if ($this->sortField === $field) {
            $this->sortAsc = !$this->sortAsc;
        } else {
            $this->sortAsc = true;
        }
        $this->sortField = $field;
    }

    public function render()
    {
        $books = \App\Book::search($this->search)
            ->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')
            ->paginate($this->perPage);
        return view('livewire.books-table', ['books' => $books]);
    }
}

resources/views/books.blade.php

@livewireAssetsを追加して
@livewire('books-table) でbooks-table.blade.phpを読み込みます。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Books</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css">
    @livewireAssets
</head>
<body style="padding:8px;">
<h1>Books</h1>
<div class="container pt-4">
    @livewire('books-table')
</div>
</body>
</html>

/resources/views/livewire/books-table.blade.php

テーブル表示のviewを書きます

wire:model、wire:click.preventの設定があるところが app/Http/Livewire/BooksTable.php と連携するところです。

の値はBooksTable.phpの$searchと同期します。

  • Books用に変更してありますが、CALEB PORZIOの resources/views/livewire/contacts-table.blade.php とほぼ同じです。 CALEB PORZIOのチュートリアル動画を参照してください。
<div>
    <div class="row mb-4">
        <div class="col form-inline">
            Per Page: &nbsp;
            <select wire:model="perPage" class="form-control">
                <option>10</option>
                <option>15</option>
                <option>25</option>
            </select>
        </div>

        <div class="col">
            <input wire:model="search" class="form-control" type="text" placeholder="Search books...">
        </div>
    </div>

    <div class="row">
        <table class="table">
            <thead>
            <tr>
                <th>ID</th>
                <th>
                    <a wire:click.prevent="sortBy('title')" role="button" href="#">
                        Title
                    </a>
                </th>
                <th>
                    <a wire:click.prevent="sortBy('author_id')" role="button" href="#">
                    Author
                    </a>
                </th>
            </tr>
            </thead>
            <tbody>
            @foreach ($books as $book)
                <tr>
                    <td>{{ $book->id }}</td>
                    <td>{{ $book->title }}</td>
                    <td>{{ $book->author['name'] }}</td>
                </tr>
            @endforeach
            </tbody>
        </table>
    </div>

    <div class="row">
        <div class="col">
            {{ $books->links() }}
        </div>

        <div class="col text-right text-muted">
            Showing {{ $books->firstItem() }} to {{ $books->lastItem() }} out of {{ $books->total() }} results
        </div>
    </div>
</div>

動作確認

php artisan serve

ブラウザで実際に動かしてみる。
すごい!動く!JavaScriptを1行も書かずに!

CALEB PORZIOすごい!

CALEB PORZIOのチュートリアル動画
https://laracasts.com/series/guest-spotlight/episodes/3

9
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11