F
Filament5mo ago
Pan

Custom Action

What I am trying to do: I am trying to create a custom action like the AssociateAction however I want to customize it on my own form. What I did: I tried following this https://filamentphp.com/docs/3.x/actions/modals#modal-forms, specifically this part
->action(function (array $data, Post $record): void {
$record->author()->associate($data['authorId']);
$record->save();
})
->action(function (array $data, Post $record): void {
$record->author()->associate($data['authorId']);
$record->save();
})
I've changed the $record into $post and the author() into just ->user_id and right now I am testing it ->associate(1) by just passing an int to see if it works. My issue/the error: if I kept the $post-user_id()->associate(1) i get the error BadMethodCall Call to undefined method App\Models\Equipment::user_id() changing it into post-user_id->associate(1) i get the error Call to a member function associate() on null Code:
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('name')
->columns([
Tables\Columns\TextColumn::make('name'),
])
->headerActions([
Tables\Actions\Action::make('Borrow Test')
->form([
Forms\Components\Select::make('name')
->searchable()
->options(Equipment::query()->pluck('name','barcode'))
//->getSearchResultsUsing(fn (string $search): array => Equipment::where('barcode', 'like', "%{$search}%")->limit(50)->pluck('name')->toArray())
])
->action(function (array $data, Equipment $post): void{
$post->user_id->associate(1);
$post->save();
})
])
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('name')
->columns([
Tables\Columns\TextColumn::make('name'),
])
->headerActions([
Tables\Actions\Action::make('Borrow Test')
->form([
Forms\Components\Select::make('name')
->searchable()
->options(Equipment::query()->pluck('name','barcode'))
//->getSearchResultsUsing(fn (string $search): array => Equipment::where('barcode', 'like', "%{$search}%")->limit(50)->pluck('name')->toArray())
])
->action(function (array $data, Equipment $post): void{
$post->user_id->associate(1);
$post->save();
})
])
Ignore the other headerActions like AssociateAction and 'Borrow' Thank you !!!!
11 Replies
Matthew
Matthew5mo ago
Why dont you directly assign the value to it and then save the model?
->action(function (array $data, Equipment $equipment): void {
$equipment->user_id = 1; // Directly assigning the value
$equipment->save();
})
->action(function (array $data, Equipment $equipment): void {
$equipment->user_id = 1; // Directly assigning the value
$equipment->save();
})
make sure the Equipment model has user_id in the fillable array
Pan
Pan5mo ago
Thanks it seem to should work fine now since I get a new error after trying the code you gave.
->action(function (array $data, Equipment $equipment): void{
$equipment->user_id = 1;
$equipment->save();
})
->action(function (array $data, Equipment $equipment): void{
$equipment->user_id = 1;
$equipment->save();
})
I get an error SQLSTATE[HY000]: General error: 1364 Field 'name' doesn't have a default value
insert into `equipment` (`user_id`, `updated_at`, `created_at`) values (1, 2024-01-24 23:40:49, 2024-01-24 23:40:49)
insert into `equipment` (`user_id`, `updated_at`, `created_at`) values (1, 2024-01-24 23:40:49, 2024-01-24 23:40:49)
I also have in my AppServiceProvider.php
public function boot(): void
{
//
Model::unguard();
}
public function boot(): void
{
//
Model::unguard();
}
Matthew
Matthew5mo ago
yeah, it seems you dont have "name" in the fillable array of your model
Pan
Pan5mo ago
I still get the same error SQLSTATE[HY000]: General error: 1364 Field 'name' doesn't have a default value In my model I have this
class Equipment extends Model
{
use HasFactory;
protected $fillable = [
'name','user_id'
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
class Equipment extends Model
{
use HasFactory;
protected $fillable = [
'name','user_id'
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
Matthew
Matthew5mo ago
ok, then this tells me that the 'name' column on the equipment table is not nullable. In that case, the model is expecting a value but isnt receiving one
Pan
Pan5mo ago
it works but it's not actually associating like I wanted to but instead create a new record with user_id that was selected
No description
awcodes
awcodes5mo ago
$equipment->user()->associate(1) You’re not using the relationship. You might need to look up the user model first though.
$user = User::find(1);

$equipment->user()->associate($user)
$user = User::find(1);

$equipment->user()->associate($user)
Pan
Pan5mo ago
I tried but it still does the same, I also added the use App\Models\User;
Tables\Actions\Action::make('Borrow Test')
->form([
Forms\Components\Select::make('name')
->searchable()
->options([
'1' => 'VOM',
])
//Equipment::query()->pluck('name','barcode')
//->getSearchResultsUsing(fn (string $search): array => Equipment::where('barcode', 'like', "%{$search}%")->limit(50)->pluck('name')->toArray())
])
->action(function (array $data, Equipment $equipment): void{
$user = User::find(1);
$equipment->user()->associate($user);
$equipment->save();
})
Tables\Actions\Action::make('Borrow Test')
->form([
Forms\Components\Select::make('name')
->searchable()
->options([
'1' => 'VOM',
])
//Equipment::query()->pluck('name','barcode')
//->getSearchResultsUsing(fn (string $search): array => Equipment::where('barcode', 'like', "%{$search}%")->limit(50)->pluck('name')->toArray())
])
->action(function (array $data, Equipment $equipment): void{
$user = User::find(1);
$equipment->user()->associate($user);
$equipment->save();
})
Also for some reason the ->options(Equipment::query()->pluck('name','barcode')) stopped working so I tried temporarily the options with hard coded options and I get this error Filament\Forms\Components\Select::isOptionDisabled(): Argument #2 ($label) must be of type string, null given But focusing on the main issue is that it does the same where it also creates a new record instead of associating the user_id to the VOM
awcodes
awcodes5mo ago
Something is off in your relationships then. Hard to say without seeing all the code. Sorry.
Pan
Pan5mo ago
Here are the codes for the User model
namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable implements FilamentUser
{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'role_id',
];

/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
public function role(): BelongsTo
{
return $this->belongsTo(Role::class);
}

public function equipments(): HasMany
{
return $this->hasMany(Equipment::class);
}

public function isAdmin(): bool
{
return $this->role->name === 'Admin';
}

public function canAccessPanel(Panel $panel): bool
{
// Uncomment this to require roles to access panel
if ($panel->getId() === 'admin'){
return str_contains($this->role_id, 5);
}
if ($panel->getId() === 'moderator'){
if (str_contains($this->role_id, 4) || str_contains($this->role_id, 5)){
return true;
}return false;
}
return true;

}
}
namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable implements FilamentUser
{
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'role_id',
];

/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
public function role(): BelongsTo
{
return $this->belongsTo(Role::class);
}

public function equipments(): HasMany
{
return $this->hasMany(Equipment::class);
}

public function isAdmin(): bool
{
return $this->role->name === 'Admin';
}

public function canAccessPanel(Panel $panel): bool
{
// Uncomment this to require roles to access panel
if ($panel->getId() === 'admin'){
return str_contains($this->role_id, 5);
}
if ($panel->getId() === 'moderator'){
if (str_contains($this->role_id, 4) || str_contains($this->role_id, 5)){
return true;
}return false;
}
return true;

}
}
I'll send the next Equipmentmodel Equipment model code
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Equipment extends Model
{
use HasFactory;
protected $fillable = [
'name','user_id'
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Equipment extends Model
{
use HasFactory;
protected $fillable = [
'name','user_id'
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
bump, is the problem the type of relationship I used for associate not to work? hi, i've posted all the codes, I really need help. Thank you!
awcodes
awcodes5mo ago
I think the problem is that equipment is null in the callback of the action.