Cargando…

Esto está tardando más de lo esperado.

Estándares de código

Laravel Front

Estos son los estándares que aplica el analizador. Cada regla dice qué revisa y por qué existe.

Todos los estándares

Las acciones CRUD pertenecen a las Policies

public $actions no es un mecanismo de autorización. No elimines una acción CRUD de public $actions para denegarla a todos los usuarios o registros. Mantén la acción declarada y deniégala en la Policy del modelo correspondiente, incluso con un return false incondicional cuando la acción nunca deba permitirse.
Usa $actions solo para describir las operaciones estructurales del recurso. No debe codificar decisiones de usuario, permisos, estado del registro ni autorización de negocio.
Esta es una regla contextual: un recurso de Laravel Front se comprueba de forma aislada, por lo que el analizador no puede inferir una infracción a partir de public $actions sin la Policy correspondiente.

Propiedad de la consulta de filtros

Un filtro de Laravel Front es el único propietario de la condición de consulta impulsada por su valor de solicitud. No inspecciones un parámetro de solicitud de filtro en indexQuery() para repetir, inferir o sobrescribir la condición de ese filtro. Usa setDefault() para un valor inicial del filtro.
Configura los filtros genéricos mediante su API compatible, incluyendo setOperator() y setScope(). Cuando un requisito no pueda expresarse con esa configuración, crea un filtro específico y mantén su lógica de consulta en apply().
Mantén indexQuery() para la configuración de la consulta que sea invariante respecto a los valores de los filtros, como la carga anticipada y el ordenamiento.

Los campos y filtros condicionales usan show()

Que una entrada se muestre lo decide show(). Tanto los inputs como los filtros lo incluyen, y Laravel Front lo respeta igual en ambos casos: un filtro con show(false) no se renderiza, no se aplica a la consulta y no se cuenta entre los filtros activos.
No decidas su pertenencia en el array en su lugar. Un resource que añade u omite una entrada para ocultarla ha movido una decisión de visibilidad a la forma de fields() o filters(), donde quien lee el código ya no puede verla junto al campo al que afecta.

// 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),
];

El resource anfitrión es dueño de su propia lista. Un paquete opcional aporta una entrada mediante una llamada que hace el resource — billingUser()?->addonsPanel($this) se lee como una sola entrada en el array, mantiene la lógica del paquete dentro del paquete y conserva la entrada en la posición que eligió el resource. Un paquete puede incluir resources para sus propios modelos; no debe proporcionar los fields, filters ni el orden de un resource que pertenece a la aplicación anfitriona.
Construir una entrada no es lo mismo que mostrarla. show() decide la visualización, y se evalúa después de que la entrada se construye. Una entrada cuya construcción no es segura — consulta las tablas de un paquete opcional o nombra una clase que quizá no esté instalada — ya se habrá ejecutado para entonces.
En su lugar, haz que la construcción sea inerte, para que show() pueda hacerse cargo de la decisión. Las opciones de un filtro son el caso habitual: pasa un closure y resuélvelo cuando se construya el field, lo que solo ocurre con filtros que hayan pasado 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()),

Cuando la construcción no puede diferirse, protégela con un retorno temprano al principio del método. Esa protección indica que la entrada no puede construirse, que es una afirmación distinta de decir que la entrada debería ocultarse, y mantiene la condición fuera del array.

Entradas Simples en Lugar de Lógica Condicional

fields(), filters() y actions() aceptan entradas de array (Laravel Front las aplana). La contribución de un paquete opcional es una única entrada segura ante null dentro del array — billingTeamOwnerResource()?->fields($this) ?? [], billingTeamOwnerResource()?->filters() ?? []. No la reescribas en variables intermedias, spreads, ternarios ni retornos anticipados condicionales que decidan la pertenencia; la visibilidad corresponde a show(), y la seguridad ante null corresponde a ?->/??.

processData y processDataBeforeSaving advierten sobre la asignación de valores del modelo

processData() y processDataBeforeSaving() son hooks de la capa de recursos. Asignar un atributo del modelo ahí es una advertencia, no un error: considera si el valor es un invariante del modelo — algo que debe aplicarse cada vez que se guarda el modelo (formulario, seeder, importación, API). Si lo es, muévelo al mutator/setter del modelo o a un observer para que se aplique en todos los flujos. Si es específico de este flujo o recurso (un valor predeterminado o de contexto), debe ir aquí y está bien.

// 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;

Nunca "corrijas" una advertencia duplicando la asignación en create() y beforeUpdate(); o bien mantenla en processData/processDataBeforeSaving o muévela al modelo/observer.

Orden de métodos en recursos Laravel Front

Los recursos de Laravel Front declaran sus métodos en este orden cuando están presentes: indexQuery(), fields(), filters(), actions(), index_actions(), y solo después los métodos restantes (p. ej. showOnMenu(), update(), processData*), cuyo orden relativo es libre.

Los nombres para mostrar se declaran como propiedades

Una clase Front ya traduce su propio nombre para mostrar en el constructor base: Resource::__construct() ejecuta __() sobre $label, y IndexAction::__construct() hace lo mismo para $title. Sobrescribir el constructor para asignar esos valores repite lo que ya hace el paquete y oculta una declaración simple dentro de un método.
Decláralos como propiedades en su lugar. El valor literal sigue siendo la clave de traducción, así que lang/ continúa resolviéndola exactamente igual que antes. Un constructor que no contiene nada más que esas asignaciones y su llamada a parent::__construct() desaparece junto con ellas.

// 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 es la excepción, y el ejemplo anterior lo omite a propósito. El constructor base solo lo construye cuando la clase no declara uno, así que una etiqueta plural declarada como propiedad llega a la pantalla exactamente como está escrita, sin traducirse. Omítela y deja que el constructor pluralice $label y traduzca el resultado: Kanban resuelve por sí solo la clave Kanbans. Declárala solo cuando esa pluralización sea incorrecta, y entonces contendrá el texto que el lector debe ver.
Un constructor que hace trabajo real mantiene su lugar. La regla solo reporta una asignación cuyo valor es un literal de cadena, por sí solo o envuelto en __() o trans() — $label en un recurso Front, $title en una acción Front. Un nombre para mostrar calculado conserva su constructor.

Closure redundante en make()

Una llamada Inputs\X::make(...) toma un nombre de columna como segundo argumento, y Laravel Front lo resuelve mediante la misma ruta de acceso que usaría cualquier closure. Por lo tanto, una closure pasada a make() que solo lee una propiedad del registro (return $item->some_label;) es redundante: pasa el nombre de la columna y elimina la closure.
Una closure solo se justifica cuando transforma el valor: una llamada a método (->badge(), ->format()), un operador, un ternario/coalescencia, concatenación o un helper. La regla solo marca la lectura simple de la propiedad, nunca esas transformaciones.

// 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');

La autorización de acciones vive solo en hasPermissions

Una Front Action decide si puede ejecutarse en hasPermissions($object), y handle($object, $request) no debe volver a autorizar. Mueve toda comprobación de permisos fuera de handle(): checkPermissionTo(), auth()->user()->can(...), Gate::authorize(), Gate::allows(), y abort_unless/abort_if con un permiso o un 403 pertenecen a hasPermissions(), que el framework evalúa antes de ejecutar la acción. La validación de alcance (por ejemplo isOutOfOwnerScope()) también puede vivir en hasPermissions() cuando participa en la decisión de autorización.
No declares hasPermissions() solo para devolver true. La base del paquete ya responde true, así que una acción abierta a todo el mundo lo expresa dejándola tal cual, y una declaración que lo repite se lee como una decisión que nadie tomó. Declárala donde la acción decida algo.

Weblabor Logo Desarrollado por Weblabor