Whether an entry is displayed is decided by show(). Inputs and filters both carry it, and Laravel Front honours it the same way for both: a filter with show(false) is not rendered, is not applied to the query, and is not counted among the active filters.
Do not decide membership in the array instead. A resource that adds or omits an entry to hide it has moved a visibility decision into the shape of fields() or filters(), where the reader can no longer see it next to the field it affects.
// Wrong
return [
Inputs\Text::make('Name'),
...(billingAvailable() ? app('billing.user-resource')->fields($this) : []),
];
// Wrong
public function filters()
{
return billingAvailable()
? app('billing.user-resource')->filters()
: [];
}
// Correct
return [
Inputs\Text::make('Name'),
Inputs\Text::make('Current Plan', 'current_plan_name')->show(billingUser() !== null),
];
The host resource owns its own list. An optional package contributes an entry through a call the resource makes — billingUser()?->addonsPanel($this) reads as one entry in the array, keeps the package's logic in the package, and keeps the entry in the position the resource chose. A package may ship resources for its own models; it must not supply the fields, filters, or ordering of a resource the host application owns.
Constructing an entry is not the same as showing it. show() decides display, and it is read after the entry is built. An entry whose construction is unsafe — it queries an optional package's tables, or names a class that may not be installed — has already run by then.
Make the construction inert instead, so show() can own the decision. A filter's options are the usual case: pass a closure and resolve it when the field is built, which happens only for filters that survived show().
// Correct — nothing runs unless the filter is shown
(new Filters\SelectFilter)
->setTitle(__('Plan'), 'plan', 'plan')
->options(function () {
return collect(['' => __('All')])->merge(billingCoreModel('plan')::pluck('name', 'id'));
})
->setScope('byPlan')
->show(billingUser() && billingCoreModel('plan')::isActive()),
When construction cannot be deferred, guard it with an early return at the top of the method. That guard says the entry cannot be built, which is a different statement from the entry should be hidden, and it keeps the conditional out of the array.