I am in filament resource form and i have a repeater with relation but i have a form in the that repeater that creating a user data that is guranter type saving in database.
for images and documents i am using spatie media pacakge for adding that in media table. but now i have a form in the repeater (tenancy_tenant) the form is directly creating the user guranteer but when i submit that form spatie media files saving with the model type tenancy tenant but i want user model because i am creating user not tenancy tenany in the repeater here below the code
{
return [
Repeater::make('tenancy_tenants')
->relationship()
->hiddenLabel()
->columns(1)
->defaultItems(1)
->addActionLabel('Add Tenants')
->cloneable()
->schema([
Select::make('user_id')
->label('Choose Tenant')
->searchable()
->preload()
->native(false)
->options(
User::where('type', UserTypeEnum::Tenant)
->where('company_id', auth()->user()->company_id)
->whereNotNull('name')
->orderBy('id', 'desc')
->get()
->mapWithKeys(fn($user) => [
$user->id => $user->fullName,
])
)
->required()
->disableOptionsWhenSelectedInSiblingRepeaterItems()
->suffixAction(
Action::make('edit_tenant')
->icon('heroicon-o-pencil')
->modalHeading('Edit Tenant')
->modalButton('Update Tenant')
->form(function ($state) {
if (empty($state)) {
return null;
}
$tenant = User::find($state);
return [
Section::make('Details')
->schema([
TextInput::make('user_id')
->hidden()
->default($tenant->id ?? ''),
Select::make('meta.title')
->label('Title')
->options(fn() => Type::where('type', TypeEnum::USER_TITLE)->pluck('name', 'id'))
->searchable()
->preload()
->default($tenant->meta['title'] ?? null)
->createOptionForm([
TextInput::make('name')
->label('Title')
->unique(table: 'types', column: 'name'),
])
->createOptionAction(fn(Action $action) => $action
->modalHeading('Create Title')
->modalButton('Create Title'))
->createOptionUsing(function ($data, $set) {
$type = Type::create([
'name' => $data['name'],
'type' => TypeEnum::USER_TITLE->value,
]);
$set('meta.title', $type->id);
return $type->id;
}),
TextInput::make('name')
->label('First Name')
->required()
->default($tenant->name ?? ''),
TextInput::make('meta.last_name')
->label('Last Name')
->required()
->default($tenant->meta['last_name'] ?? ''),
DatePicker::make('meta.tenant.dob')
->label('Date of Birth')
->native(false)
->placeholder(config('app.date_format_placeholder'))
->icon('heroicon-m-calendar')
->displayFormat(config('app.date_format'))
->default($tenant->meta['tenant']['dob'] ?? null),
Group::make([
TextInput::make('email')
->label('Primary Email')
->rules([
fn(Get $get): Closure => function (string $attribute, $value, Closure $fail) use ($get) {
$tenantId = $get('user_id');
$existingUser = User::where('email', $value)
->when($tenantId, fn($query) => $query->where('id', '!=', $tenantId))
->exists();
if ($existingUser) {
$fail('The Email is already in use. Please choose another one.');
}
},
new ValidEmail,
])
->live(onBlur: true)
->afterStateUpdated(function ($livewire, $component) {
$livewire->validateOnly($component->getStatePath());
})
->email()
->default($tenant->email ?? ''),
Checkbox::make('meta.tenant.email_notifications')
->label('Enable Email Notifications')
->default($tenant->meta['tenant']['email_notifications'] ?? true),
]),
TextInput::make('password')
->password()
->maxLength(255)
->revealable()
->live(),
Group::make([
CustomPhoneInput::make('phone_work')
->label('Primary Phone')
->default($tenant->phone_work ?? ''),
Checkbox::make('meta.tenant.sms_notifications')
->label('Enable SMS Notifications')
->default($tenant->meta['tenant']['sms_notifications'] ?? false)
->live(),
]),
TextInput::make('meta.tenant.passport_or_driving_license_number')
->label('Passport/Driving License Number')
->nullable()
->default($tenant->meta['tenant']['passport_or_driving_license_number'] ?? ''),
Country::make('meta.tenant.country')
->label('Nationality')
->default($tenant->meta['tenant']['country'] ?? null),
CustomPhoneInput::make('meta.tenant.mobile_number')
->label('Mobile Number')
->visible(fn(Get $get) => $get('meta.tenant.sms_notifications'))
->default($tenant->meta['tenant']['mobile_number'] ?? ''),
])
->columns(2),
Section::make('Extra Contact Information')
->schema([
CustomPhoneInput::make('mobile')
->label('Secondary Phone'),
TextInput::make('address.line_one')
->label('Address Line 1')
->default($tenant->address['line_one'] ?? ''),
TextInput::make('address.line_two')
->label('Address Line 2')
->default($tenant->address['line_two'] ?? ''),
TextInput::make('address.city')
->label('City')
->default($tenant->address['city'] ?? ''),
TextInput::make('address.county')
->label('County')
->default($tenant->address['county'] ?? ''),
TextInput::make('address.postcode')
->label('Postcode')
->default($tenant->address['postcode'] ?? ''),
TextInput::make('address.country')
->label('Country')
->default($tenant->address['country'] ?? ''),
TextInput::make('meta.tenant.cc_email_addresses')
->label('CC Email Addresses')
->nullable()
->default($tenant->meta['tenant']['cc_email_addresses'] ?? ''),
TextInput::make('meta.tenant.vat_number')
->label('VAT Number')
->nullable()
->default($tenant->meta['tenant']['vat_number'] ?? ''),
])
->collapsible()
->collapsed()
->columns(2),
];
})
->action(function ($state, $data) {
if (empty($state)) {
return;
}
$tenant = User::find($state);
if ($tenant) {
$tenant->update([
'name' => $data['name'],
'email' => $data['email'],
'phone_work' => $data['phone_work'],
'mobile' => $data['mobile'],
'password' => $data['password'] ? Hash::make($data['password']) : $tenant->password, // Only update if provided
'address' => [
'line_one' => $data['address']['line_one'] ?? '',
'line_two' => $data['address']['line_two'] ?? '',
'city' => $data['address']['city'] ?? '',
'county' => $data['address']['county'] ?? '',
'postcode' => $data['address']['postcode'] ?? '',
'country' => $data['address']['country'] ?? '',
],
'meta' => [
'title' => $data['meta']['title'],
'last_name' => $data['meta']['last_name'],
'tenant' => [
'passport_or_driving_license_number' => $data['meta']['tenant']['passport_or_driving_license_number'] ?? '',
'vat_number' => $data['meta']['tenant']['vat_number'] ?? '',
'email_notifications' => $data['meta']['tenant']['email_notifications'] ?? true,
'cc_email_addresses' => $data['meta']['tenant']['cc_email_addresses'] ?? '',
'mobile_number' => $data['meta']['tenant']['mobile_number'] ?? '',
'sms_notifications' => $data['meta']['tenant']['sms_notifications'] ?? false,
'dob' => $data['meta']['tenant']['dob'] ?? null,
'country' => $data['meta']['tenant']['country'] ?? null,
],
],
]);
return $tenant->id;
}
})
)
->createOptionForm([
Section::make('Details')
->schema(array_merge([
Hidden::make('type')
->default(UserTypeEnum::Tenant),
], TenantsResource::userForm()))->columns(2),
])
->createOptionAction(function (Action $action) {
return $action
->modalHeading('Create Tenant')
->modalButton('Create Tenant');
})
->createOptionUsing(function ($data) {
$user = User::create([
'company_id' => auth()->user()->company_id ?? null,
'created_by' => auth()->user()->id,
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'phone_work' => $data['phone_work'],
'mobile' => $data['mobile'],
'type' => UserTypeEnum::Tenant,
'meta' => [
'last_name' => $data['meta']['last_name'] ?? '',
'company_name' => $data['meta']['company_name'] ?? null,
'title' => $data['meta']['title'],
'tenant' => [
'passport_or_driving_license_number' => $data['meta']['tenant']['passport_or_driving_license_number'] ?? '',
'vat_number' => $data['meta']['tenant']['vat_number'] ?? '',
'email_notifications' => $data['meta']['tenant']['email_notifications'] ?? true,
'cc_email_addresses' => $data['meta']['tenant']['cc_email_addresses'] ?? '',
'mobile_number' => $data['meta']['tenant']['mobile_number'] ?? '',
'sms_notifications' => $data['meta']['tenant']['sms_notifications'] ?? false,
'days_before_due_date' => $data['meta']['tenant']['days_before_due_date'] ?? 0,
'dob' => $data['meta']['tenant']['dob'] ?? null,
'country' => $data['meta']['tenant']['country'],
],
],
'address' => [
'line_one' => $data['address']['line_one'] ?? '',
'line_two' => $data['address']['line_two'] ?? '',
'city' => $data['address']['city'] ?? '',
'county' => $data['address']['county'] ?? '',
'postcode' => $data['address']['postcode'] ?? '',
'country' => $data['address']['country'] ?? '',
],
]);
$user->givePermissionTo([
'properties.view-any',
'units.view-any',
'invoices.view-any',
'invoices.view',
'media.view-any',
'media.view',
'notices.view-any',
'notices.view',
'work-orders.view-any',
'work-orders.view',
'work-orders.create',
'work-orders.edit',
'work-orders.delete',
'work-orders.action-email-to-property-manager',
'work-orders.action-provide-feedback',
'work-orders.action-archive',
]);
return $user->id;
}),
Checkbox::make('is_main')
->required()
->fixIndistinctState()
->label('Lead Tenant')
->rules([
function ($get) {
return function (string $attribute, $value, Closure $fail) use ($get) {
$repeaterItems = $get('../../tenancy_tenants');
$itemCount = count($repeaterItems);
if ($itemCount === 1 && !$value) {
$fail('The Lead Tenant checkbox is required.');
return;
}
if ($itemCount > 1) {
$checkedCount = collect($repeaterItems)->filter(function ($item) {
return $item['is_main'] ?? false;
})->count();
if ($checkedCount === 0) {
$fail('At least one tenant must be marked as the Lead Tenant.');
}
}
};
},
]),
// Add Guarantee Section
Checkbox::make('add_guarantee')
->label('Add Guarantee')
->live(),
Select::make('guarantor_id')
->label('Choose Guarantor')
->searchable()
->preload()
->live()
->reactive()
->native(false)
->options(
User::where('type', UserTypeEnum::GUARANTOR)
->where('company_id', auth()->user()->company_id)
->whereNotNull('name')
->orderBy('id', 'desc')
->get()
->mapWithKeys(fn($user) => [
$user->id => $user->fullName,
])
)
->visible(fn(Get $get) => $get('add_guarantee') === true)
->suffixAction(
Action::make('create_guarantor')
->icon('heroicon-o-plus')
->modalHeading('Create Guarantor')
->modalButton('Create Guarantor')
->form([
Section::make('Contact Details')
->schema([
Grid::make(2)
->schema([
Select::make('title')
->label('Title')
->options(
fn() => Type::where('type', TypeEnum::USER_TITLE)->pluck('name', 'id')
)
->searchable()
->preload()
->createOptionForm([
TextInput::make('name')
->label('Title')
->unique(table: 'types', column: 'name'),
])
->createOptionAction(fn(Action $action) => $action
->modalHeading('Create Title')
->modalButton('Create Title'))
->createOptionUsing(function ($data, $set) {
$type = Type::create([
'name' => $data['name'],
'type' => TypeEnum::USER_TITLE->value,
]);
$set('title', $type->id);
return $type->id;
}),
TextInput::make('name')
->label('First Name')
->required()
->maxLength(255),
]),
TextInput::make('meta.last_name')
->label('Last Name')
->required()
->maxLength(255),
Grid::make(2)
->schema([
TextInput::make('email')
->label('Email')
->email()
->maxLength(255)
->unique(table: 'users', column: 'email', ignoreRecord: true),
TextInput::make('phone')
->label('Phone')
->tel()
->maxLength(20),
]),
TextInput::make('address.address_line_1')
->label('Address Line 1')
->maxLength(255),
TextInput::make('address.address_line_2')
->label('Address Line 2')
->maxLength(255),
Grid::make(3)
->schema([
TextInput::make('address.city')
->label('City')
->maxLength(255),
TextInput::make('address.county')
->label('County')
->maxLength(255),
TextInput::make('address.postcode')
->label('Postcode')
->maxLength(10),
]),
]),
Section::make('Financial Verification')
->schema([
Select::make('meta.profession_type')
->label('Profession Type')
->options([
'employed' => 'Employed',
'self_employed' => 'Self-Employed',
'retired' => 'Retired',
'student' => 'Student',
'unemployed' => 'Unemployed',
'other' => 'Other',
])
->required(),
Radio::make('meta.home_owner')
->label('Home Owner')
->options([
'1' => 'Yes',
'0' => 'No',
])
->default('0')
->required()
->live(),
TextInput::make('meta.property_value')
->label('Property Value')
->numeric()
->prefix('£')
->minValue(0)
->step(0.01)
->visible(fn(Get $get): bool => $get('meta.home_owner') === '1')
->required(fn(Get $get): bool => $get('meta.home_owner') === '1'),
TextInput::make('meta.annual_income')
->label('Annual Income')
->numeric()
->prefix('£')
->minValue(0)
->step(0.01),
]),
Section::make('Additional Fields')
->schema([
Textarea::make('meta.notes')
->label('Notes')
->rows(4)
->maxLength(1000),
SpatieMediaLibraryFileUpload::make('id_document')
->label('ID Document')
->collection(MediaCollectionEnum::DOCUMENTS->value)
->acceptedFileTypes(['image/jpeg', 'image/png', 'application/pdf'])
->saveRelationshipsUsing(fn($component) => $component->saveUploadedFiles())
->columnSpanFull(),
SpatieMediaLibraryFileUpload::make('proof_of_address')
->label('Proof of Address')
->collection(MediaCollectionEnum::DOCUMENTS->value)
->acceptedFileTypes(['image/jpeg', 'image/png', 'application/pdf'])
->saveRelationshipsUsing(fn($component) => $component->saveUploadedFiles())
->columnSpanFull(),
SpatieMediaLibraryFileUpload::make('proof_of_ownership')
->label('Proof of Property Ownership')
->collection(MediaCollectionEnum::DOCUMENTS->value)
->acceptedFileTypes(['image/jpeg', 'image/png', 'application/pdf'])
->saveRelationshipsUsing(fn($component) => $component->saveUploadedFiles())
->columnSpanFull()
->visible(fn(Get $get): bool => $get('meta.home_owner') === '1'),
]),
])
->action(function ($data, $set) { // Added $set to update the Select
// 1. Create the Guarantor User
$guarantor = User::create([
'company_id' => auth()->user()->company_id ?? null,
'created_by' => auth()->user()->id,
'name' => $data['name'],
'email' => $data['email'],
'phone_work' => $data['phone'],
'type' => UserTypeEnum::GUARANTOR,
'meta' => [
'title' => $data['title'] ?? null,
'last_name' => $data['meta']['last_name'],
'profession_type' => $data['meta']['profession_type'] ?? null,
'home_owner' => $data['meta']['home_owner'] ?? null,
'property_value' => $data['meta']['property_value'] ?? null,
'annual_income' => $data['meta']['annual_income'] ?? null,
'notes' => $data['meta']['notes'] ?? null,
],
'address' => [
'line_one' => $data['address']['address_line_1'],
'line_two' => $data['address']['address_line_2'] ?? '',
'city' => $data['address']['city'],
'county' => $data['address']['county'],
'postcode' => $data['address']['postcode'],
],
]);
$set('guarantor_id', $guarantor->id);
return $guarantor->id;
})
),
]),
];
} ```