Loading...

This is taking longer than expected.

Coding standards

Laravel Front

These are the standards the analyzer applies. Every rule states what it checks and why it exists.

All standards

CRUD Actions Belong to Policies

public $actions is not an authorization mechanism. Do not remove a CRUD action from public $actions to deny it for every user or record. Keep the action declared and deny it in the corresponding model policy, including with an unconditional return false when the action must never be allowed.
Use $actions only to describe the resource's structural operations. It must not encode user, permission, record-state, or business-authorization decisions.
This is a contextual rule: a Laravel Front resource is checked in isolation, so the analyzer cannot infer a violation from public $actions without the corresponding policy.

Filter Query Ownership

A Laravel Front filter is the single owner of the query condition driven by its request value. Do not inspect a filter request parameter in indexQuery() to repeat, infer, or override that filter’s condition. Use setDefault() for an initial filter value.
Configure generic filters through their supported API, including setOperator() and setScope(). When a requirement cannot be expressed by that configuration, create a dedicated filter and keep its query logic in apply().
Keep indexQuery() for query setup that is invariant to filter values, such as eager loading and ordering.

Conditional Fields and Filters Use show()

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.

Simple Entries Over Conditional Logic

fields(), filters() and actions() accept array entries (Laravel Front flattens them). An optional package's contribution is a single, null-safe entry inside the array — billingTeamOwnerResource()?->fields($this) ?? [], billingTeamOwnerResource()?->filters() ?? []. Do not rewrite it into intermediate variables, spreads, ternaries, or conditional early returns that decide membership; visibility belongs in show(), and null-safety belongs in ?->/??.

processData and processDataBeforeSaving Warn About Model Value Assignment

processData() and processDataBeforeSaving() are resource-layer hooks. Assigning a model attribute there is a warning, not an error: consider whether the value is a model invariant — something that must apply every time the model is saved (form, seeder, import, API). If it is, move it to the model mutator/setter or an observer so every flow applies it. If it is specific to this flow or resource (a default or context value), it belongs here and is fine.

// Model invariant — belongs in the model/observer
$data['name'] = Str::slug($data['name']);

// Flow-specific — fine to keep here
$data['guard_name'] = config('auth.defaults.guard');
$data['team_id'] = team()->id;

Never "fix" a warning by duplicating the assignment into create() and beforeUpdate(); either keep it in processData/processDataBeforeSaving or move it to the model/observer.

Front Resource Method Order

Laravel Front resources declare their methods in this order when present: indexQuery(), fields(), filters(), actions(), index_actions(), and only then the remaining methods (e.g. showOnMenu(), update(), processData*), whose relative order is free.

Display Names Are Declared as Properties

A Front class already translates its own display name in the base constructor: Resource::__construct() runs __() over $label, and IndexAction::__construct() does the same for $title. Overriding the constructor to assign those values repeats what the package does and hides a plain declaration inside a method.
Declare them as properties instead. The literal stays the translation key, so lang/ keeps resolving it exactly as before. A constructor left with nothing but those assignments and its parent::__construct() call goes away with them.

// Wrong — the base constructor already translates the label
public function __construct($source = null)
{
    $this->label = __('Kanban');
    $this->plural_label = __('Kanbans');

    parent::__construct($source);
}

// Correct
public $label = 'Kanban';

$plural_label is the exception, and the example above drops it on purpose. The base constructor builds it only when the class does not declare one, so a plural label declared as a property reaches the screen exactly as written, untranslated. Leave it out and let the constructor pluralize $label and translate the result — Kanban resolves the Kanbans key on its own. Declare it only when that pluralization is wrong, and then it holds the text the reader is meant to see.
A constructor that does real work keeps its place. The rule reports only an assignment whose value is a string literal, on its own or wrapped in __() or trans() — $label in a Front resource, $title in a Front action. A computed display name keeps its constructor.

Redundant Closure in make()

An Inputs\X::make(...) call takes a column name as its second argument, and Laravel Front resolves it through the same accessor path any closure would. A closure handed to make() that only reads a property off the record (return $item->some_label;) is therefore redundant: pass the column name and drop the closure.
A closure only earns its place when it transforms the value — a method call (->badge(), ->format()), an operator, a ternary/coalesce, concatenation, or a helper. The rule flags only the plain property read, never those transformations.

// Wrong — the column name does the same
Inputs\Text::make('Document type', function ($item) {
    return $item->fiscal_document_type_label;
});

// Correct
Inputs\Text::make('Document type', 'fiscal_document_type_label');

Action Authorization Lives Only in hasPermissions

A Front Action decides whether it may run in hasPermissions($object), and handle($object, $request) must not re-authorize. Move every permission check out of handle(): checkPermissionTo(), auth()->user()->can(...), Gate::authorize(), Gate::allows(), and abort_unless/abort_if with a permission or a 403 belong in hasPermissions(), which the framework evaluates before executing the action. Scope validation (for example isOutOfOwnerScope()) may also live in hasPermissions() when it participates in the authorization decision.
Do not declare hasPermissions() only to return true. The package base already answers true, so an action open to everyone says so by leaving it alone, and a declaration that repeats it reads as a decision nobody made. Declare it where the action decides something.

Weblabor Logo Developed by Weblabor