Ok, now your application is set.
We can dive in something more concrete.
- an user can signin, login, logout and manage his account
- a link has a name, a status (published or not) and obviously a link
What we need to manage our links.
Remember what I said in the first part :
- an user can signin, login, logout and manage his account
- a user can see a list of their links
- an user can add, edit or delete a link
- an user can have a shareable link to share his links with the world
- a link has a name, a status (published or not) and obviously a link. May be we will add some options later.
an user can signin, login, logout and manage his account
This is done with Filament. In fact, no completely, we can login, logout but not register or even manage our own account.
But, you will see it's very easy to add register and profile page for users.
In app/Providers/Filament/AdminPanelProvider.php
you have to add only 2 lines under login() function call to implement both functionnalities :
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
// Lesson 2 : here we add registration and profile page
->registration() // registration
->profile() // profile management
->colors([
'primary' => Color::Amber,
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
Widgets\AccountWidget::class,
Widgets\FilamentInfoWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
}
}
Go back to your login page : http://linkeeper.com/admin/login
and you will see
that the link for signup has been added.
If you login, you will see under your menu the new sub menu 'Profile'
It allows you to edit your nickname, your e-mail address and change your password.
So, after this very cool step we can check the first functionnality :
✅ an user can signin, login, logout and manage his account
- an user can add, edit or delete a link
- an user can have a shareable link to share his links with the world
- a link has a name, a status (published or not) and obviously a link. May be we will add some options later.
a link has a name, a status (published or not) and obviously a link
I know it is not in the order of the list, but first it's me writing, second I need to define table links
and its fields before an user can add link, right ?
So let me do this ! 🤣
Let's think about table schema
Ok, what will be the fields in our table links
?
Of course an id
, an url
, a short description
, a published
status (published or not), a user_id
foreign key and finally the classicals created_at
and updated_at
.
The user_id
is to define the relationship between a user and a link.
In fact, we can resume this relation by :
- a user can have 0, 1 or many links
- a link (unique by its
id
) belongs to one and only one user
This is why we use a one-to-many relationship.
What do you thing, is something missing ? Tell me in the comment if you think I forgot something.
Ok, now we have to prepare the migration for links
table.
php artisan make:migration create_links_table
Now we have to edit this migration to add fields to the table. So, let's edit the migration file
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('links', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->string('description', 255)->nullable();
$table->boolean('published')->default(false);
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('links');
}
};
Let's migrate
Ok, it's time to migrate. For that, we can use (again) Artisan.
php artisan migrate
Remember if you make something wrong or you want to modify immediately the schema, you can rollback your migration with :
php artisan migrate:rollback
And if you want to see the status of your migrations, you can use :
php artisan migrate:status
Look at this screenshot captured before running the last migration.
We can see two things :
- first the migration for the
links
table was not run already - second the three first migrations was launched in the same sequence : the sequence number [1]
Define model and relationship
The next step is to define the Link
model and adapt User
model.
We will use again Artisan :
php artisan make:model Link
Now we have to tell the model that a link belongs to an user.
Add this function to the model class Link
:
public function user()
{
return $this->belongsTo(User::class);
}
To finish with the model, we have to update the model User
by adding it a function links
public function links()
{
return $this->hasMany(Link::class);
}
We need fake datas to plays with
After defining our database schema, let's do something very important.
Create fake datas to play with. Please do not use production datas from your database during development.
For many reasons such as ethics, confidentiality and security. We are professionals, so let's act accordingly.
The factories
By default Laravel comes with User
factory, so we just need to create a Link
factory.
php artisan make:factory Link
In the generated definition
function we put this code :
public function definition(): array
{
return [
'url' => fake()->url(),
'description' => fake()->sentence(5),
'published' => fake()->boolean(80),
];
}
Factories use Faker to generate fake datas but very close to real datas.
In this code, we generate :
- an url
- a description based on a sentence of 5 words. In fact, as I let the second parameter of
sentence
method by default (true), sentence will generate sentences with approximately 5 words. It could be four, five, six or even seven words. - true or false for the
published
field with a minimum true percentage of 80%
The seeder
Laravel comes with database/seeders/DatabaseSeeder.php
We have to edit it to use our factory.
Here it is the code :
/**
* Seed the application's database.
*/
public function run(): void
{
User::factory()->create([
'name' => 'Happytodev',
'email' => 'happytodev@gmail.com',
'email_verified_at' => now(),
'password' => Hash::make('Password*'),
'remember_token' => Str::random(10),
]);
User::factory()
->count(10)
->create()
->each(function ($user) {
Link::factory()
->count(rand(2, 10))
->create(['user_id' => $user->id]);
});
}
Some explanations :
- First I create my own user to connect easily each time with my own email
- Second part does this
- create 10 fake users
- for every user create between 2 and 10 links, during the creation we told that the
user_id
fiels must be filled with the current createduser->id
.
And now, the magic could begin.
Let's go back in your terminal, and launch simply this command :
php artisan db:seed
Go into your DB viewer, and check the tables users
and links
.
You should see something like that :
If you want to check everything works fine, we can go a walk in Tinker.
php artisan tinker
Tinker will let us to play with our datas and check the relationship is currently working.
User::find(6)->links()->get(['id', 'url', 'published', 'user_id']);
Here, we ask to find every link for the user with id = 6. We ask too to get only interesting fields.
Seeing these results, we can improve immediately something.
What if, we only want the published links ?
So you can do it like this :
> User::find(6)->links()->where('published', 1)->get(['id', 'url', 'published', 'user_id']);
But there is a better way to do this and don't repeat yourself later. We can use Scopes.
A scope can add a bunch of query to your query. Scopes can be global or local. I let you consult the documentation to see more.
Here we will use a local scope.
Go to your link
model and add this function :
/**
* Scope a query to only include published links.
*/
public function scopePublished(Builder $query): void
{
$query->where('published', 1);
}
Now, if in Tinker we use the following query :
User::find(6)->links()->published()->get(['id', 'url', 'published', 'user_id']);
we will obtain only the published links from user with id = 6.
And voilà , we can cross this point in our list :
✅ an user can signin, login, logout and manage his account
- a user can see a list of their links
- an user can add, edit or delete a link
- an user can have a shareable link to share his links with the world
✅ a link has a name, a status (published or not) and obviously a link. May be we will add some options later.
Well, we'll stop here for today.
In this longer chapter, we saw introduction to :
- how to add functionnality to login page in Filament
- models
- factory
- seeder
- Tinker
- scopes
As usual, I wait for your comments below.
See you.
Top comments (0)