Now, so far our CRM doesn't accomplish too much as we can only manage a list of our client information.
We want to be able to track projects for our clients, categorize and tag those projects, and eventually track billable time in the form of tasks for those projects.
We will break this part up into a few sections, but first we need a place to create projects for our clients.
Lets create a projects table:
1php artisan make:migration create_projects_table
1Schema::create('projects', function (Blueprint $table) {2 $table->id();3 $table->foreignIdFor(Customer::class);4 $table->string('name');5 $table->text('description')->nullable();6 $table->boolean('billable')->default(false);7 $table->timestamps();8 $table->softDeletes();9});
We don't need too much info so far, later our categories and tags will be many to many polymorphic relationships and will be stored elsewhere.
After we migrate, we can create out Project model:
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Factories\HasFactory; 6use Illuminate\Database\Eloquent\Model; 7use Illuminate\Database\Eloquent\Relations\BelongsTo; 8use Illuminate\Database\Eloquent\Relations\BelongsToMany; 9use Illuminate\Database\Eloquent\SoftDeletes;10 11class Project extends Model12{13 use HasFactory,14 SoftDeletes;15 16 protected $fillable = [17 'customer_id',18 'name',19 'description',20 'billable',21 ];22 23 protected $casts = [24 'billable' => 'boolean',25 ];26 27 public function customer(): BelongsTo28 {29 return $this->belongsTo(Customer::class);30 }31 32 public function users(): BelongsToMany33 {34 return $this->belongsToMany(User::class);35 }36}
Once we have our Project model, we can use Filament to generate a resource for us:
1php artisan make:filament-resource Project --generate
We should now have a Filament/ProjectResource
folder as well as a Filament/ProjectResource.php
file.
We will leave the folder alone for now, but the file should have been pre-generated by Filament and look something like this (I added a few extras such as users and customer):
1<?php 2 3namespace App\Filament\Resources; 4 5use App\Filament\Resources\ProjectResource\Pages; 6use App\Models\Project; 7use Filament\Forms\Components\Select; 8use Filament\Forms\Components\Textarea; 9use Filament\Forms\Components\TextInput;10use Filament\Forms\Components\Toggle;11use Filament\Forms\Form;12use Filament\Resources\Resource;13use Filament\Tables;14use Filament\Tables\Table;15use Illuminate\Database\Eloquent\Builder;16use Illuminate\Database\Eloquent\SoftDeletingScope;17 18class ProjectResource extends Resource19{20 protected static ?string $model = Project::class;21 22 protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';23 24 public static function form(Form $form): Form25 {26 return $form27 ->schema([28 TextInput::make('name')29 ->columnSpanFull()30 ->required(),31 Textarea::make('comments')32 ->columnSpanFull(),33 Select::make('customer_id')34 ->searchable()35 ->preload()36 ->required()37 ->relationship(name: 'customer', titleAttribute: 'name'),38 Select::make('user_id')39 ->searchable()40 ->preload()41 ->placeholder('Search for people...')42 ->required()43 ->multiple()44 ->relationship(name: 'users', titleAttribute: 'name'),45 Toggle::make('billable'),46 ]);47 }48 49 public static function table(Table $table): Table50 {51 return $table52 ->columns([53 Tables\Columns\TextColumn::make('customer.name'),54 Tables\Columns\TextColumn::make('name'),55 Tables\Columns\IconColumn::make('billable')56 ->boolean(),57 ])58 ->filters([59 Tables\Filters\TrashedFilter::make(),60 ])61 ->actions([62 Tables\Actions\EditAction::make(),63 ]);64 }65 66 public static function getPages(): array67 {68 return [69 'index' => Pages\ListProjects::route('/'),70 'create' => Pages\CreateProject::route('/create'),71 'edit' => Pages\EditProject::route('/{record}/edit'),72 ];73 }74 75 public static function getEloquentQuery(): Builder76 {77 return parent::getEloquentQuery()78 ->withoutGlobalScopes([79 SoftDeletingScope::class,80 ]);81 }82}
We should now have a working CRUD interface that looks like this:
And a table that looks like this:
In the next section we will be turning the CRUD into a wizard that looks like this:
As well as a more informative table with filters:
Stay tuned for part four where we will add polymorphic tags and categories to our projects as well as turn the form into a wizard.