Courses

Creating CRM with Filament 3: Step-By-Step

Creating Lead Sources Resource

Next up, we need to know where our Customer came from. So for that, we will create a Lead Source Resource in Filament:

In this lesson, we will:

  • Create lead_sources DB structure: Model/Migration and a belongsTo relationship with customers
  • Create Seeds with semi-real data without factories
  • Create a Filament Resource for Lead sources
  • Add a DeleteAction to the table with validation if that record is used
  • Add lead source information to the Customer Resource table/form
  • Divide the menu into two levels: introduce Settings parent menu item

Creating Lead Source Database

The Lead Source Resource will have the following fields:

  • id
  • name

And that's it. We want to keep it simple for now.

We will start by creating a new database table:

Migration

Schema::create('lead_sources', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});

Then, we will create a new Model for the Lead Source:

Model

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
 
class LeadSource extends Model
{
protected $fillable = [
'name',
];
 
public function customers(): HasMany
{
return $this->hasMany(Customer::class);
}
}

As you can see, we have already added a relationship to our Customer Model. This will be useful later on.

Then, we can create a seed for our Lead Source:

database/seeders/DatabaseSeeder.php

public function run(): void
{
// ...
 
$leadSources = [
'Website',
'Online AD',
'Twitter',
'LinkedIn',
'Webinar',
'Trade Show',
'Referral',
];
 
foreach ($leadSources as $leadSource) {
LeadSource::create(['name' => $leadSource]);
}
}

This will seed some basic Lead Sources into our database. Of course, you can add more if you want.

Next, we have the last step - connecting our Lead Source to our Customer via new migration:

Migration

use App\Models\LeadSource;
 
// ...
 
Schema::table('customers', function (Blueprint $table) {
$table->foreignIdFor(LeadSource::class)->nullable()->constrained();
});

And our last step is modifying our Customer Model:

app/Models/Customer.php

 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
 
class Customer extends Model
{
use SoftDeletes;
use HasFactory;
 
protected $fillable = [
'first_name',
'last_name',
'email',
'phone_number',
'description',
'lead_source_id'
];
 
public function leadSource(): BelongsTo
{
return $this->belongsTo(LeadSource::class);
}
 
// ...
}

That's it. You should now have a new table lead_sources created with some data inside of it if you run:

php artisan migrate:fresh --seed


Creating Lead Source Resource

To create this Resource, we will use the same command as before:

php artisan make:filament-resource LeadSource --generate

This will once again create the necessary files for us, so all we have to do is load the page and see what we got:

It loads nicely and already has the data from our database. Even the create form is working. But there's one thing we need to change:

  • There's no delete! - We need to have a delete button for our Lead Source, and it has to check if there are no Customers connected to it.

So let's do that by modifying our LeadSourceResource:

app/Filament/Resources/LeadSourceResource.php

use Filament\Notifications\Notification;
 
// ...
 
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')
->searchable(),
Tables\Columns\TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make()
->action(function ($data, $record) {
if ($record->customers()->count() > 0) {
Notification::make()
->danger()
->title('Lead Source is in use')
->body('Lead Source is in use by customers.')
->send();
 
return;
}
 
Notification::make()
->success()
->title('Lead Source deleted')
->body('Lead Source has been deleted.')
->send();
 
$record->delete();
})
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
 
// ...

Let's go over the changes we made:

  • We added a DeleteAction to our table
  • When the DeleteAction is clicked, we check if there are any Customers connected to it
  • If there are, we show a notification and don't delete the Lead Source
  • If there aren't, we show a notification and delete the Lead Source

That's it! This is how it looks in our browser:

And if we try to delete a Lead Source that has Customers connected to it (for this example, we did this directly in the database):

It works! We can't delete a Lead Source with Customers connected to it.


Modifying Customer Resource and Database Table - Adding Lead Source

Now that we have our Lead Sources and can manage them, we should add a select field to our Customer Resource so we can select a Lead Source for our Customer:

app/Filament/Resources/CustomerResource.php

// ...
 
public static function form(Form $form): Form
{
return $form
->schema([
// ...
Forms\Components\Textarea::make('description')
->maxLength(65535)
->columnSpanFull(),
Forms\Components\Select::make('lead_source_id')
->relationship('leadSource', 'name'),
]);
}
 
// ...

This should add a select field to our Customer Resource:

As a last step, we must add this field to the table. Otherwise, we won't be able to see it:

app/Filament/Resources/CustomerResource.php

// ...
 
public static function table(Table $table): Table
{
return $table
->columns([
// ...
Tables\Columns\TextColumn::make('phone_number')
->searchable(),
Tables\Columns\TextColumn::make('leadSource.name'),
// ...
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
 
// ...

Now, loading the Customer table should show us the Lead Source column:

That's it! It is this simple to add a new field to our Resource.


Cleaning Up the Navigation - Adding Settings Dropdown

Since introducing our Lead Sources, we should think about how we want to organize our navigation. What we have now is this:

And it looks alright, but imagine if we have 10 or 20 different Resources. It would be a mess. So let's clean it up a bit by adding a dropdown for our Settings:

app/Providers/Filament/AdminPanelProvider.php

 
use Filament\Navigation\NavigationGroup;
// ....
 
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
// ...
->navigationGroups([
NavigationGroup::make()
->label('Settings')
->icon('heroicon-o-cog-6-tooth')
->collapsed(),
])
->authMiddleware([
Authenticate::class,
]);
}
}

This registers that such a group can be used in our navigation, but we haven't added anything. So let's do that:

app/Filament/Resources/LeadSourceResource.php

class LeadSourceResource extends Resource
{
protected static ?string $model = LeadSource::class;
 
protected static ?string $navigationGroup = 'Settings';
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
 
// ...
}

Once this is done, we should see a new dropdown in our navigation:

This becomes much cleaner and will give us a place to put all our settings.

Bonus: The Icon Needs to be Removed

As a bonus tip, you might have noticed that we have removed the icon from our LeadSourceResource. Filament does not support an icon on the settings dropdown and our resource. Here's what would happen if we did not delete the icon:

avatar

i created a new migration like this php artisan make:migration AddForeignKeyToCustomersTable

runing php artisan migrate:fresh --seed gived me the following error

SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (Connection: mysql, SQL: alter table customers add constraint customers_app\models\leadsource_foreign foreign key (App\Models\LeadSource) references App\Models\LeadSources (id))

avatar

Could you add your migration here?

avatar

in the app/Filament/Resources/LeadSourceResource.php you shuld add use Filament\Notifications\Notification;

👍 1
avatar

Thank you for spotting this, updated!

avatar

we getting error on bulk delete lead source

avatar

Can you provide more details? Unsure what the error is.