I am currently work student management project using FilamentPHP v3. Before I make this post I already research on filament documentation and youtube but still not found how to solve it. For now I already succeed to create new student(student table) with parent detail(student_parent table). The issue i have here is if I click the view button or edit button it does not show the parent detail. I do not want to use relation manager for this purpose because i want to stick with same design as form. Please help me how to make it show on view and edit page.
here the StudentResource code
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\StudentResource\Pages;
use App\Filament\Resources\StudentResource\RelationManagers;
use App\Models\Student;
use App\Models\StudentParent;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
class StudentResource extends Resource
{
protected static ?string $model = Student::class;
protected static ?string $navigationIcon = 'heroicon-o-users';
protected static ?string $navigationLabel = 'Student';
protected static ?string $modelLabel = 'Student';
protected static ?string $navigationGroup = 'Student Management';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Fieldset::make('Student Information')
->schema([
Forms\Components\Select::make('student_code_id')
->label('Kod Pelajar')
->relationship('studentCode', 'code_name')
->required()
->reactive() // Ensure the component reacts immediately
->afterStateUpdated(function ($state, callable $set) {
if ($state) {
// Update code_no when student_code_id changes
$set('code_no', Student::getNextCodeNo($state));
}
}),
Forms\Components\TextInput::make('code_no')
->label('Nombor Kod Pelajar')
->disabled(), // This field is shown but disabled
// Add a hidden field to store the actual code_no value
Forms\Components\Hidden::make('code_no')
->default(fn (callable $get) => Student::getNextCodeNo($get('student_code_id'))),
Forms\Components\Select::make('student_subcode_id')
->label('Subkod Pelajar')
->relationship('studentSubcode', 'subcode_name')
->required()
->reactive() // Ensure the component reacts immediately
->afterStateUpdated(function ($state, callable $set) {
if ($state) {
// Update subcode_no when student_subcode_id changes
$set('subcode_no', Student::getNextSubcodeNo($state));
}
}),
Forms\Components\TextInput::make('subcode_no')
->label('Nombor Subkod Pelajar')
->disabled(), // This field is shown but disabled
// Add a hidden field to store the actual subcode_no value
Forms\Components\Hidden::make('subcode_no')
->default(fn (callable $get) => Student::getNextSubcodeNo($get('student_subcode_id'))),
Forms\Components\Select::make('student_type_id')
->label('Status Belajar')
->relationship('studentType', 'type_name')
->required(),
Forms\Components\Select::make('student_session_id')
->label('Sesi Pelajar')
->relationship('studentSession', 'session_name')
->required(),
Forms\Components\Select::make('student_category_id')
->label('Kategori Pelajar')
->relationship('studentCategory', 'category_name')
->required(),
Forms\Components\TextInput::make('batch')
->label('Batch')
->numeric()
->required(),
Forms\Components\TextInput::make('fullname')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Nama Penuh')
->required(),
Forms\Components\Textarea::make('address')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Alamat')
->required(),
Forms\Components\TextInput::make('postcode')
->label('Poskod')
->required(),
Forms\Components\TextInput::make('city')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Bandar')
->required(),
Forms\Components\TextInput::make('state')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Negeri')
->required(),
Forms\Components\TextInput::make('nric')
->label('Nombor Kad Pengenalan')
->required()
->maxLength(12)
->unique(),
Forms\Components\DatePicker::make('date_of_birth')
->label('Tarikh Lahir')
->required(),
Forms\Components\DatePicker::make('date_of_admit')
->label('Daftar Pada'),
// ->required(),
])
->columns(2),
Forms\Components\Fieldset::make('Father Information')
->schema([
Forms\Components\TextInput::make('father_name')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Nama Ayah'),
Forms\Components\TextInput::make('father_nric')
->label('Nombor Kad Pengenalan')
->maxLength(12),
// ->unique(),
Forms\Components\TextInput::make('father_age')
->label('Umur Ayah')
->numeric(),
Forms\Components\TextInput::make('father_phone')
->label('Nombor Telefon Ayah')
->tel(),
Forms\Components\TextInput::make('father_email')
->label('Email Ayah')
->email(),
Forms\Components\TextInput::make('father_occupation')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Pekerjaan Ayah'),
Forms\Components\TextInput::make('father_gross_salary')
->label('Gaji Kasar Ayah')
->numeric(),
Forms\Components\TextInput::make('father_net_salary')
->label('Gaji Bersih Ayah')
->numeric(),
])
->columns(3),
Forms\Components\Fieldset::make('Mother Information')
->schema([
Forms\Components\TextInput::make('mother_name')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Nama Ibu'),
Forms\Components\TextInput::make('mother_nric')
->label('Nombor Kad Pengenalan')
->maxLength(12),
// ->unique(),
Forms\Components\TextInput::make('mother_age')
->label('Umur Ibu')
->numeric(),
Forms\Components\TextInput::make('mother_phone')
->label('Nombor Telefon Ibu')
->tel(),
Forms\Components\TextInput::make('mother_email')
->label('Emel Ibu')
->email(),
Forms\Components\TextInput::make('mother_occupation')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Pekerjaan Ibu'),
Forms\Components\TextInput::make('mother_gross_salary')
->label('Gaji Kasar Ibu')
->numeric(),
Forms\Components\TextInput::make('mother_net_salary')
->label('Gaji Bersih Ibu')
->numeric(),
])
->columns(3),
Forms\Components\Fieldset::make('Emergency Information')
->schema([
Forms\Components\TextInput::make('emergency_contact_name')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Nama Kecemasan')
->required(),
Forms\Components\TextInput::make('emergency_contact_nric')
->label('Nombor Kad Pengenalan')
->maxLength(12),
// ->unique(),
Forms\Components\TextInput::make('emergency_contact_phone')
->label('Nombor Telefon Kecemasan')
->tel()
->required(),
Forms\Components\TextInput::make('emergency_contact_relation')
->extraInputAttributes(['onChange' => 'this.value = this.value.toUpperCase()'])
->label('Hubungan')
->required(),
Forms\Components\TextInput::make('emergency_contact_gross_salary')
->label('Gaji Kasar')
->numeric(),
// ->required(),
Forms\Components\TextInput::make('emergency_contact_net_salary')
->label('Gaji Bersih')
->numeric(),
// ->required(),
])
->columns(3),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('studentCode.code_name')
->label('Kod')
->searchable(),
Tables\Columns\TextColumn::make('studentSubcode.subcode_name')
->label('Subkod')
->searchable(),
Tables\Columns\TextColumn::make('studentType.type_name')
->label('Status Belajar')
->searchable(),
Tables\Columns\TextColumn::make('studentSession.session_name')
->label('Sesi')
->searchable(),
Tables\Columns\TextColumn::make('studentCategory.category_name')
->label('Kategori')
->searchable(),
Tables\Columns\TextColumn::make('batch')
->label('Batch')
->searchable(),
Tables\Columns\TextColumn::make('fullname')
->label('Nama Penuh')
->searchable(),
Tables\Columns\TextColumn::make('address')
->label('Alamat')
->searchable(),
Tables\Columns\TextColumn::make('nric')
->label('Kad Pengenalan')
->searchable(),
])
->filters([
Tables\Filters\SelectFilter::make('student_type_id')
->label('Status Belajar')
->relationship('studentType', 'type_name'),
Tables\Filters\SelectFilter::make('student_session_id')
->label('Sesi Pelajar')
->relationship('studentSession', 'session_name'),
Tables\Filters\SelectFilter::make('student_category_id')
->label('Kategori Pelajar')
->relationship('studentCategory', 'category_name'),
Tables\Filters\Filter::make('batch')
->label('Batch')
->form([
Forms\Components\TextInput::make('batch')
->label('Batch')
->numeric(),
]),
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
// RelationManagers\ParentRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListStudents::route('/'),
'create' => Pages\CreateStudent::route('/create'),
'view' => Pages\ViewStudent::route('/{record}'),
'edit' => Pages\EditStudent::route('/{record}/edit'),
];
}
public static function handleStudentCreation(array $data): Model
{
// Insert the student
$student = static::getModel()::create($data);
// Create a new StudentParent model instance
$guardian = new StudentParent();
$guardian->student_id = $student->id; // Set to the ID of the newly created student
$guardian->father_name = $data['father_name'] ?? null;
$guardian->father_nric = $data['father_nric'] ?? null;
$guardian->father_age = $data['father_age'] ?? null;
$guardian->father_phone = $data['father_phone'] ?? null;
$guardian->father_email = $data['father_email'] ?? null;
$guardian->father_occupation = $data['father_occupation'] ?? null;
$guardian->father_gross_salary = $data['father_gross_salary'] ?? null;
$guardian->father_net_salary = $data['father_net_salary'] ?? null;
$guardian->mother_name = $data['mother_name'] ?? null;
$guardian->mother_nric = $data['mother_nric'] ?? null;
$guardian->mother_age = $data['mother_age'] ?? null;
$guardian->mother_phone = $data['mother_phone'] ?? null;
$guardian->mother_email = $data['mother_email'] ?? null;
$guardian->mother_occupation = $data['mother_occupation'] ?? null;
$guardian->mother_gross_salary = $data['mother_gross_salary'] ?? null;
$guardian->mother_net_salary = $data['mother_net_salary'] ?? null;
$guardian->emergency_contact_name = $data['emergency_contact_name'] ?? null;
$guardian->emergency_contact_nric = $data['emergency_contact_nric'] ?? null;
$guardian->emergency_contact_phone = $data['emergency_contact_phone'] ?? null;
$guardian->emergency_contact_relation = $data['emergency_contact_relation'] ?? null;
$guardian->emergency_contact_gross_salary = $data['emergency_contact_gross_salary'] ?? null;
$guardian->emergency_contact_net_salary = $data['emergency_contact_net_salary'] ?? null;
// Save the StudentParent model
$guardian->save();
return $student;
}
}
here relationship on Student Model.
public function parent()
{
return $this->hasOne(StudentParent::class, 'student_id', 'id');
}
here relationship on StudentParent Model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class StudentParent extends Model
{
use HasFactory;
protected $fillable = [
'student_id',
'father_name',
'father_nric',
'father_age',
'father_phone',
'father_email',
'father_occupation',
'father_gross_salary',
'father_net_salary',
'mother_name',
'mother_nric',
'mother_age',
'mother_phone',
'mother_email',
'mother_occupation',
'mother_gross_salary',
'mother_net_salary',
'emergency_contact_name',
'emergency_contact_nric',
'emergency_contact_phone',
'emergency_contact_relation',
'emergency_contact_gross_salary',
'emergency_contact_net_salary',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function student()
{
return $this->belongsTo(Student::class);
}
}
this is my screenshot of the issue:
I think it will be simple if you use relationship on the form like this
Forms\Components\Group::make()
->relationship('parent')
->schema([
TextInput::make('father_name')
->required(),
// etc
]);
But if you still want to use the form like before, you can customize the data before filling the form on the view page by customize mutateFormDataBeforeFill function:
protected function mutateFormDataBeforeFill(array $data): array
{
$guardian = $this->getRecord()->parent;
foreach($guardian->attributesToArray() as $key => $value) {
$data[$key] = $value;
}
return $data;
}
Ref: