Taking asynchronous back to the future
I first saw Laravel Livewire at Laracon Online back in February, and my co-workers and I were all pretty excited to try it out. During the convention, Livewire creator Caleb Porzio (@calebporzio) demonstrated his new (old) way of thinking about asynchronous, which really gets back to a more traditional method of creating a dynamic DOM. Just like back in the day, Livewire will return the actual HTML as a string to be displayed within the browser, rather than make a request to an API and have it return, say, an array of JSON objects. Leveraging Laravel's component feature to boot, Livewire is intriguing, and seemed worth giving a test drive.
I should say that Caleb's documentation is pretty great and would be a much better place to start if you're looking to begin learning Livewire. The purpose of this blog is really to get a working example and to share my thoughts on the framework/package.
Installation
Create our project
Just to keep things clean, I am going to create a fresh, brand-spankin' new Laravel project. We will name the project livewire-demo
.
$ composer create-project laravel/laravel livewire-demo
Install Livewire
$ cd livewire-demo && composer require livewire/livewire
Include Livewire assets
If you started a fresh application, you can use the Laravel 7 syntax. Older versions will need to use the bottom syntax. We are just going to use the welcome.blade.php
template that comes out of the box with a new Laravel project just to get started quickly.
Laravel 7+
resources/views/welcome.blade.php
<head>
... // All the other stuff in <head>
<livewire:styles>
</head>
<body>
... // All the other stuff in <body>
<livewire:scripts>
</body>
Older versions
resources/views/welcome.blade.php
<head>
... // All the other stuff in <head>
@livewirestyles
</head>
<body>
... // All the other stuff in <body>
@livewirescripts
</body>
Tidying up before getting started
I removed most of the styles in place, leaving just the font and some basic styles on the body to make things more aesthetically tolerable while we are working on this. This is my final Blade template.
resources/views/welcome.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 200;
height: 100vh;
margin: 0;
text-align: center;
}
</style>
<livewire:styles>
</head>
<body>
<div>
<h1>Livewire Demo</h1>
</div>
<livewire:scripts>
</body>
</html>
Start the server
Just for simplicity, we are going to use php artisan serve
.
From terminal
$ php artisan serve
Laravel development server started: http://127.0.0.1:8000
Setting up
Having been working in Spotify recently, let's follow suit and say we are going to create a basic application for my vinyl collection. Here is what I am envisioning:
- Create an
Album
model with basic info like artist and release date and its affiliated migration file - Create
AlbumFactory
andAlbumSeeder
classes to populate the database with some dummy test albums just for our demo - Create an
AlbumSearch
component powered by Livewire that will dynamically pull down these albums as we type in the search bar.
Getting set up
Create our model, factory, and seeder classes
To start we can create our Album
model and migration using the make:model
command with the -m
flag to optionally create the migration at the same time. Let's go ahead and chain on our command to create our AlbumFactory
and AlbumSeeder
, as well.
From terminal
$ php artisan make:model Album -m && php artisan make:factory AlbumFactory && php artisan make:seeder AlbumSeeder
database/migrations/2020_04_16_180906_create_albums_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAlbumsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('albums', function (Blueprint $table) {
$table->id();
$table->string('artist');
$table->string('name');
$table->string('cover_image')->default('https://placehold.it/200x200');
$table->date('release_date');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('albums');
}
}
database/factories/AlbumFactory.php
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Album;
use Faker\Generator as Faker;
$factory->define(Album::class, function (Faker $faker) {
return [
'artist' => $faker->name,
'name' => $faker->sentence,
'cover_image' => $faker->imageUrl(200,200),
'release_date' => $faker->dateTimeBetween(), // Defaults to between now and 30 years ago
];
});
database/seeders/AlbumSeeder.php
<?php
use Illuminate\Database\Seeder;
class AlbumSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(\App\Album::class, 20)->create();
}
}
Migrate & Seed
Run our migrations and then seed the database with our seeder.
From terminal
$ php artisan migrate && php artisan db:seed
Creating the Livewire Component
Use artisan to create a Livewire component
Livewire gives us a handy Artisan command that we can use to create a new component. Passing album-search
to the make:livewire
command will generate two files: a Laravel Component, located in app/Http/Livewire/AlbumSearch.php
, and a Blade template, located in resources/views/livewire/album-search.php
. Properties you set on the component, such as search
below, are accessible within the Blade template using wire:model
.
From terminal
$ php artisan make:livewire album-search
App/Http/Livewire/AlbumSearch.php
<?php
namespace App\Http\Livewire;
use App\Album;
use Livewire\Component;
class AlbumSearch extends Component
{
public $search = '';
public function render()
{
return view('livewire.album-search', [
'albums' => Album::where('artist', 'LIKE', "%$this->search%")->get(),
]);
}
}
resources/views/livewire/album-search.php
<div>
<input wire:model="search" type="text" placeholder="Search albums..."/>
<ul>
@foreach($albums as $album)
<li>{{ $album->artist }}</li>
@endforeach
</ul>
</div>
Wrapping up
I think my biggest takeaway from test driving Livewire is how much it actually feels like I am using a Javascript framework like React or Vue. Setting the properties on the PHP class feels very much like accessing state
in React or a data
value in Vue. I love the intuitive file structure for components and their Blade templates.
While I will probably continue to use Javascipt, the advantages to using Livewire instead would be that I can keep everything within the Laravel ecosystem. I could leverage things like the Laravel Debug Bar that I don't get to easily take advantage of when using a decoupled Javascript application for my front end. Caleb's documentation is fantastic, which goes a long way these days. It is clear he is passionate about the project, and generally I feel like it's a safe bet that Livewire will continue to grow and improve over time because of that. Overall, I enjoyed working in it and might use it again for some smaller pieces in an application where I don't want to pull in a whole new Javascript framework.