How to pass parameters from ListRecords to CreateRecord in Resources?

I have the following relations Menus > Categories > Products. I have ListProducts where I do allow user to choose a Menu from a dropdown and picked menu_id is saved via a $queryString. So now when I click on a Create action I would like to carry this menu_id to the Create page and display related Categories to the user when creating + keeping him in the same Menu. Any best practices on of how to achieve this in Filament?
class ListProducts extends ListRecords
{
protected static string $resource = ProductResource::class;

public Collection $menus;

public ?int $menu = null;

protected $queryString = [
'menu'
];
public function mount(): void
{
parent::mount();

$this->menus = Menu::with('products')->get();
}

protected function getActions(): array
{
return [
Actions\SelectAction::make('menu')
->options($this->menus->pluck('title', 'id')->toArray()),

Actions\CreateAction::make(),
];
}

.....
class ListProducts extends ListRecords
{
protected static string $resource = ProductResource::class;

public Collection $menus;

public ?int $menu = null;

protected $queryString = [
'menu'
];
public function mount(): void
{
parent::mount();

$this->menus = Menu::with('products')->get();
}

protected function getActions(): array
{
return [
Actions\SelectAction::make('menu')
->options($this->menus->pluck('title', 'id')->toArray()),

Actions\CreateAction::make(),
];
}

.....
15 Replies
Dan Harrin
Dan Harrin2y ago
this sort of structure isnt really natively supported yet, but @dufjix might have a solution for you. he's working on a package
Crylar
Crylar2y ago
Thank you for getting back to me. I was wondering if we could expose methods for the actions on url generation that would allow to override and pass custom parameters for the action urls. I could PR if you think this could be a good idea. 🙂
Dan Harrin
Dan Harrin2y ago
you can just pass ->url() to override the default with your new params
Crylar
Crylar2y ago
The problem is that getUrl is a static method. The only override possible now is to completely override the configureCreateAction.
Crylar
Crylar2y ago
So I guess passing ->url() to a custom action works but not as flexible when working within Resource default CRUD actions, unless I am not seeing a point. 🙂
Crylar
Crylar2y ago
but if we could expose something like as a protected methods. This would allow to someone easily extend the default CRUD actions with custom parameters.
Crylar
Crylar2y ago
or just $this->getCreateActionUrl method and move the whole closure into it. So if someone needs custom URL he would not need to completely repeat the configureCreateAction code.
Dan Harrin
Dan Harrin2y ago
it doesnt matter if its static CreateAction::make()->url(Resource::getUrl('create', ['params']))
Crylar
Crylar2y ago
Oh, I guess I got confused with configureCreateAction and thought I would lose some sort of the functionality. It looks like to work for what I need I guess. One more question - if I wanted to override table edit action urls I would also need to include $record as a parameter to not break the Resources functionality, right?
Dan Harrin
Dan Harrin2y ago
yes EditAction::make()->url(fn ($record) => Resource::getUrl('edit', ['record' => $record]))
Crylar
Crylar2y ago
Nice! This is the way to even be able to build up nested routes too, just need a little bit of customisation but very possible. I have seen some questions in help that were looking for this. Thank you for the great package you are building. I can't wait for v3. 👍 🤜
Crylar
Crylar2y ago
wow this is very nice. I have actually started to build my own implementation some days ago but you got something going here! I do hope this ends up in the core eventually.
Hro
Hro2y ago
It does not need to be in there fully, just need some additonal helpers so we can reduce the package implementation footprint.
Crylar
Crylar2y ago
class Resource extends BaseResource
{
protected static ?string $parentResource = null;

public static function getParentResource(): ?string
{
return static::$parentResource;
}

public static function hasParentResource(): bool
{
return static::getParentResource() !== null;
}

protected static function shouldRegisterNavigation(): bool
{
// should only be registered if has no parent resource
return static::$shouldRegisterNavigation && static::$parentResource === null;
}

/**
* Shallow nested CRUD route
*/

public static function getRoutes(): Closure
{
return function () {
$parentSlug = static::hasParentResource()
? static::getParentResource()::getSlug()
: null;

$slug = static::getSlug();

Route::name("{$slug}.")
->middleware(static::getMiddlewares())
->group(function () use ($slug, $parentSlug) {
foreach (static::getPages() as $name => $page) {

$baseUri = [$slug, $page['route']];
if ($parentSlug !== null && in_array($name, ['index', 'create'])) {
$baseUri = [$parentSlug, '{parentRecord}', ...$baseUri];
}

$uri = join('/', array_map(fn ($item) => trim($item, '/'), $baseUri));

Route::get($uri, $page['class'])->name($name);
}
});
};
}
}
class Resource extends BaseResource
{
protected static ?string $parentResource = null;

public static function getParentResource(): ?string
{
return static::$parentResource;
}

public static function hasParentResource(): bool
{
return static::getParentResource() !== null;
}

protected static function shouldRegisterNavigation(): bool
{
// should only be registered if has no parent resource
return static::$shouldRegisterNavigation && static::$parentResource === null;
}

/**
* Shallow nested CRUD route
*/

public static function getRoutes(): Closure
{
return function () {
$parentSlug = static::hasParentResource()
? static::getParentResource()::getSlug()
: null;

$slug = static::getSlug();

Route::name("{$slug}.")
->middleware(static::getMiddlewares())
->group(function () use ($slug, $parentSlug) {
foreach (static::getPages() as $name => $page) {

$baseUri = [$slug, $page['route']];
if ($parentSlug !== null && in_array($name, ['index', 'create'])) {
$baseUri = [$parentSlug, '{parentRecord}', ...$baseUri];
}

$uri = join('/', array_map(fn ($item) => trim($item, '/'), $baseUri));

Route::get($uri, $page['class'])->name($name);
}
});
};
}
}
This was my process of thinking but I do see you are something heading in a similar direction. I will check your package. 🙂
Want results from more Discord servers?
Add your server