laravel / nova-issues

557 stars 34 forks source link

BelongsTo field in custom actions not validated properly when parent resource is being searched #6529

Closed shaffe-fr closed 2 months ago

shaffe-fr commented 2 months ago

Description:

If a BelongsTo field is used in a custom Action and if the parent resource is being searched, the Relatable validation rule fails because the parent search is applied to the validation query.

This occurs because BelongsTo creates the associatable query using the request search.

     public function getRules(NovaRequest $request)
    {
        $query = $this->buildAssociatableQuery( // <==
            $request, $request->{$this->attribute.'_trashed'} === 'true'
        )->toBase();

        return array_merge_recursive(parent::getRules($request), [
            $this->attribute => array_filter([
                $this->nullable ? 'nullable' : 'required',
                new Relatable($request, $query),
            ]),
        ]);
    }
    public function buildAssociatableQuery(NovaRequest $request, $withTrashed = false)
    {
        // ...
        $request->first === 'true'
                        ? $query->whereKey($model->newQueryWithoutScopes(), $request->current)
                        : $query->search(
                            $request, $model->newQuery(), $request->search, // <== here
                            [], [], TrashedStatus::fromBoolean($withTrashed)
                        );
    }

Detailed steps to reproduce the issue on a fresh Nova installation:

  1. Create a Product resource
    class ProductResource extends Resource
    {
    public $search = ['name'];
    public function fields(NovaRequest $request): array
    {
        return [
            ID::make(),
            Text::make(__('fields.name'), 'name'),
            BelongsTo::make('Feature', 'feature', FeatureResource::class),
        ];
    }
    public function actions(NovaRequest $request): array
    {
        return [
            AssociateFeatureToProductAction::make()->onlyOnIndex(),
       ];
    }
    }
  2. Create a Feature resource
    class FeatureResourceextends Resource
    {
    public function fields(NovaRequest $request): array
    {
        return [
            ID::make(),
            Text::make(__('fields.name'), 'name'),
        ];
    }
    }
  3. Create a custom action to associate a feature to several products.

    
    class AssociateFeatureToProductAction extends Action
    {
    public function handle(ActionFields $fields, Collection $models)
    {
       /// associate $fields->get('feature') to products
    }
    
    public function fields(NovaRequest $request): array
    {
        return [
            BelongsTo::make('Feature', 'feature', FeatureResource::class),
        ];
    }
    }
5. Open a browser and type a search to filter the products list
6. Select some products, open the action and pick a feature that doesn't match the searched keyword
7. Execute the action.
8.  The `nova::validation.relatable` error message will pop.

### Workaround:

My quick workaround was to add the following method to FeatureResource:
```php
    public static function buildIndexQuery(NovaRequest $request, $query, $search = null,
        array $filters = [], array $orderings = [],
        $withTrashed = TrashedStatus::DEFAULT): Builder
    {
        // Fixes issue when filtering parent resource while validating a belongsTo field
        if ($request instanceof ActionRequest) {
            $search = null;
        }

        return parent::buildIndexQuery($request, $query, $search, $filters, $orderings, $withTrashed);
    }
jeremynikolic commented 2 months ago

Thanks for reaching out to us 🙏

We have a fix incoming for this issue 👍

jeremynikolic commented 2 months ago

Duplicates laravel/nova-issues#6518