Sebastian Faltoni Software Architect. Passionate about Clean Code, Tech, Web, AI, .NET, Python, IOT, Swift, SwiftUI and also Blazor

Getting started with your first Laravel 8 and Jetstream web site

7 min read

Requirements

  • NodeJS last version
  • PHP
  • Composer

Introduction

I’ve been programming for more than 20 years, mostly with .NET. Recently I had to work on a project with PHP. It was a simple project, and because I didn’t touch PHP for ten years apart from a few quick fixes on WordPress. I decided to go and look at how the PHP community evolved, and while looking around for the best web framework, Laravel kept coming up.

They call it The PHP Framework For Web Artisans, and because it has been built with developer happiness in mind and with a solid foundation, you shouldn’t be sweating on the small thing. This a great way to introduce a framework to a new developer. All framework I have tried over the years they all look shiny and fabulous on the surface, but in the end to get a full app you need to stitch a few libraries together with different design and goals. By the end of the day, you wasted a lot of time-fighting to make everything work together, while it would have been better if you could spend that time building your application. Of course, any framework or technology is going to have its issues. When most of my time is spent on framework issues instead of my code issues, I start to question why in 2021 we are still having all these issues. So I decided to give it a try.

My own requirements before i even start a simple project

But before creating a new Laravel project, I had to check what they usually use for building their web user interfaces. I was looking to start simple. I prefer to avoid complex front-end framework like React and Vue because I’m looking to develop apps with the path of least resistance. Those frameworks tend to make things more convoluted and complex than they need to be, especially for simple control panels where all you need to do is data entry.

But I also wanted to use Tailwind CSS because I fall in love with it a few weeks before even thinking of using PHP and Laravel. And by simply looking a little deeper at the Laravel web site, I was happily surprised that they work a lot with Tailwind CSS. They have a project called Jetstream witch provides two UI Stacks, one with Livewire + Blade and the other is Inertia + Vue. As you can imagine, I have chosen Livewire + Blade witch is the more straightforward approach for me, and the best part is that both use Tailwind CSS, so they are telling me to download it now!!

How to create your first Laravel jetstream project

Before you can create a Laravel jetstream project, you need to create an empty Laravel project. To do so, I usually use the Laravel installer, and you need to have composer installed. If you currently don’t have the Laravel command-line installer, you get it on your machine by running the following command.

composer global require laravel/installer

When the above command finish, you can proceed further by creating an empty Laravel project with the following command.

laravel new myjetstreamapp

You can specify any name you like in place of myjetstreamapp

Now we are ready to install the jetstream starter kit with composer. In this example, I will proceed with the livewire version. For more info, check jetstream’s official website. First run composer require.

composer require laravel/jetstream

Then we need to install Jetstream with livewire support.

php artisan jetstream:install livewire

Then as per jetstream doc we must finish our installation by running npm and migrations.

npm install
npm run dev
php artisan migrate

You have probably noticed that by running the last command php artisan migrate it output an error, this usually happen because you don’t have a connection configure for your database.

Usually, I would configure a mysql test database, but I will guide you with sqlite, a robust database on a file for this tutorial.

To enable sqlite you need to edit the .env file. If you don’t see it on your os is because it is a hidden file. On MacOS, you can press CMD + . to show hidden files; if you press it again, it will hide them again. Open the .env file on your editor of choice and first set the DB_CONNECTION setting as:

DB_CONNECTION=sqlite

Then you need to remove DB_DATABASE setting line. If you don’t remove it, it will prevent database migration. Now create an empty sqlite database with the following command.

touch database/database.sqlite

Now you can rerun the migration command. The migration command will create the default tables included with jetstream for user management.

php artisan migrate

Now you should see something like the following output.

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (1.32ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.83ms)
Migrating: 2014_10_12_200000_add_two_factor_columns_to_users_table
Migrated:  2014_10_12_200000_add_two_factor_columns_to_users_table (0.77ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.81ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (1.08ms)
Migrating: 2021_05_17_193135_create_sessions_table
Migrated:  2021_05_17_193135_create_sessions_table (1.04ms)

Ok it’s time to see how you project looks like, run php artisan serve and navigate on your browser at http://127.0.0.1:8000/.

Your empty project is now ready!

Seeding the database with a default user

Right now, you can’t log in to your new website because there are no users. But Laravel, for this specific purpose, provides a feature named Database Seeder, which is very useful for providing default data on your database or populating your database with test data. Later, I will show you how to seed some faked users to build a user list, using seeders and fakers to make this trivial. For now, let’s create a default test user. Execute the following command, which will generate a UserSeeder class for you.

php artisan make:seeder UserSeeder

The UserSeeder.php is located inside the database/seeders folder.

Replace the file content with the following snippet

<?php

namespace Database\\Seeders;

use Illuminate\\Database\\Seeder;
use Illuminate\\Support\\Facades\\App;
use Illuminate\\Support\\Facades\\Hash;
use Illuminate\\Support\\Str;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        if (App::environment('local')) {
            \\App\\Models\\User::insert([
                [
                    'name' => 'Admin',
                    'email' => 'admin@localhost',
                    'email_verified_at' => now(),
                    'password' => Hash::make("admin@pwd23"), // password
                    'remember_token' => Str::random(10)
                ]
            ]);
        }
    }
}

This will create a new user with admin@localhost as login email and admin@pwd23.

But before you try to login on your app you need to run the seeder with the following artisan command.

php artisan db:seed --class=UserSeeder

Try to login to check if everything is running ok.

Populating the users table with fake users

There are a few steps to make a seeder with randomly generated users. But Laravel makes this very easy trivial. We need to make a few fake users so we can later build a user data table using Livewire, and without a few users, we wouldn’t be able to see a paginated table. In an empty Laravel project, you would have to run the php artisan make:factory UserFactory to create a custom factory for your user model, but you get one for free on the jetstream template. This is what you get on the box.

<?php

namespace Database\\Factories;

use App\\Models\\Team;
use App\\Models\\User;
use Illuminate\\Database\\Eloquent\\Factories\\Factory;
use Illuminate\\Support\\Str;
use Laravel\\Jetstream\\Features;

class UserFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = User::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'name' => $this->faker->name,
            'email' => $this->faker->unique()->safeEmail,
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     *
     * @return \\Illuminate\\Database\\Eloquent\\Factories\\Factory
     */
    public function unverified()
    {
        return $this->state(function (array $attributes) {
            return [
                'email_verified_at' => null,
            ];
        });
    }

    /**
     * Indicate that the user should have a personal team.
     *
     * @return $this
     */
    public function withPersonalTeam()
    {
        if (! Features::hasTeamFeatures()) {
            return $this->state([]);
        }

        return $this->has(
            Team::factory()
                ->state(function (array $attributes, User $user) {
                    return ['name' => $user->name.'\\'s Team', 'user_id' => $user->id, 'personal_team' => true];
                }),
            'ownedTeams'
        );
    }
}

Look at the definition function. You can see that it is using $this->faker to generate a fake name and email; for the email, you can see nicety $this->faker->unique()->safeEmail this will guarantee that you generated email is unique. Now modify the UserSeeder.php run method as per example, notice that now we check if the admin user already exists.

public function run()
{
    if (App::environment('local')) {
        if(User::where('name', '=', 'Admin')->count() === 0) {
            \\App\\Models\\User::insert([
                [
                    'name' => 'Admin',
                    'email' => 'admin@localhost',
                    'email_verified_at' => now(),
                    'password' => Hash::make("admin@pwd23"), // password
                    'remember_token' => Str::random(10)
                ]
            ]);
        }
        User::factory()->times(200)->create();
    }
}

Now run the seed command again to add 200 users to you database.

php artisan db:seed --class=UserSeeder

If you open the sqlite database in a db viewer like TablePlus, you should see 201 rows.

Now we are ready to proceed with the creation of a Livewire users table

Creating the user table component with Livewire and an open source component

You could easily create your data table with Livewire and Tailwind CSS, but we will use an excellent open-source library. Install the library with the following command.

composer require mediconesystems/livewire-datatables

As suggested by the package author you should add the following css snippet in your app.css

which is located inside resources/css , this is necessary to avoid flickers as the package use alpinejs.

[x-cloak] {
    display: none;
}

Now we need to create our UsersListComponent with the following.

php artisan livewire:make UsersListComponent

This will create a new livewire component where we are going to add our data table.

Open the users-list-component.blade.php file and replace the content with the following code.

<div>
    <x-slot name="header">
        <div class="md:flex md:items-center md:justify-between md:space-x-5">
            <div class="flex items-start space-x-5">
                <div class="flex-shrink-0">
                    <div class="relative">
                        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
                            {{ __('Users') }}
                        </h2>
                    </div>
                </div>

            </div>
        </div>

    </x-slot>
    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
                <livewire:datatable
                    searchable="name,email"
                    model="App\\Models\\User"
                    include="id, name, email, created_at"
                    dates="created_at"
                />
            </div>
        </div>
    </div>
</div>

Most of the code is layout but if you look at the data table part is very trivial to define a simple data table that provides pagination and search! Here only the datable snippet.

<livewire:datatable
              searchable="name,email"
              model="App\\Models\\User"
              include="id, name, email, created_at"
              dates="created_at"
          />

But we are not finished! We need to enable a new route in the web.php file to show it! Open the web.php file and remove the dashboard route and replace it with the following code, which embeds the dashboard route and users route inside a group, so both get authenticated, and there is no need to duplicate the middleware part.

Route::middleware(['auth:sanctum', 'verified'])->group(function (){
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');

    Route::get('/users', \\App\\Http\\Livewire\\UsersListComponent::class)->name('users');
});

Now serve it with php artisan serve and look at your newly create users page!

Conclusion

Building great websites in Laravel and livewire is very trivial. This is a simple example but is a good starting point to see how productive this ecosystem is. Hopefully, you enjoyed this article, and if so, please leave a comment or a clap!

Resources

Sebastian Faltoni Software Architect. Passionate about Clean Code, Tech, Web, AI, .NET, Python, IOT, Swift, SwiftUI and also Blazor

Laravel Livewire Property is not loaded when fill is…

If you are using Laravel Livewire, and on your component your are trying to load properties from your eloquent model using fill, for example...
Sebastian Faltoni
33 sec read

Laravel Unique Validation rule on update has already been…

You got the validation rule with unique working on create, but when you are trying to update the same record your get The name...
Sebastian Faltoni
36 sec read

Leave a Reply

Your email address will not be published. Required fields are marked *