Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

RayC's avatar
Level 10

Livewire pagination adding form fields to query string

Hello,

I have a livewire page with some filters at the top of the page long with an 'Apply Filters' button. These all work as expected with no issues. I am also including the query string so users can bookmark filtered results for later use, this work sa expected.

Issue is with the pagination, If I for instance change the date, select something, add any text to the search filed and then click ANY link in the pagination such as 'Next' or a page number, the information in the form is appended to the query string and applied to the search query, even though I have NOT clicked 'Apply Filters'.

How can I stop the query from being generated and the filters being applied when clicking any link in the pagination?

Here is the form:

<form wire:submit.prevent="processSearch">
    <div class="row">
        <div class="form-group">
            <div for="date_sent" class="col-form-label mt-1">{{ __('Date Range') }}&nbsp;</div>
            <div class="d-flex mt-n1">
                <div class="mr-2 mt-1 align-middle">
                    <div class="font-weight-normal col-form-label">
                        From
                    </div>
                </div>
                <div>
                    <x-date-picker name="logStartDate" id="logStartDate" wire:model.defer="logStartDate" />
                </div>
                <div class="mt-1 align-middle">
                    <div class="font-weight-normal col-form-label custom_to">
                        To
                    </div>
                </div>
                <div>
                    <x-date-picker name="logEndDate" id="logEndDate" wire:model.defer="logEndDate" />
                </div>
            </div>
        </div>
        <div class="form-group col">
            <div for="date_sent" class="col-form-label mt-1">{{ __('Mapped Connection') }}&nbsp;</div>
            <div class="d-flex">
                <div>
                    <label>
                        <select name="mappedConnection" id="mappedConnection" class="form-control" wire:model.defer="mappedConnection">
                            <option value="">Select all</option>
                            @foreach($mappedConnections as $conn)
                                <option value="{{ $conn->name }}">{{ $conn->name }}</option>
                            @endforeach
                        </select>
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group mr-3">
            <div for="date_sent" class="col-form-label mt-1">{{ __('Friendly Name') }}&nbsp;</div>
            <div class="d-flex">
                <div>
                    <label>
                        <input  name="search" id="search" type="text" class="form-control" wire:model.defer="search" />
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group mr-3">
            <div for="date_sent" class="col-form-label mt-1">{{ __('Import Status') }}&nbsp;</div>
            <div class="d-flex">
                <div>
                    <label>
                        <select name="importStatus" id="importStatus" class="form-control" wire:model.defer="importStatus">
                            <option value="">Select all</option>
                            @foreach(StatusEnum::$statuses as $key => $value)
                                <option value="{{ $key }}">{{ $value }}</option>
                            @endforeach
                        </select>
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group mr-3">
            <div for="date_sent" class="col-form-label mt-1">{{ __('Has Notes') }}&nbsp;</div>
            <div class="d-flex">
                <div class="pt-1">
                    <label>
                        <input name="hasNotes" id="hasNotes" type="checkbox" class="form-control checkbox" wire:model.defer="hasNotes" />
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group">
            <div for="date_sent" class="col-form-label mt-1">&nbsp;</div>
            <div class="d-flex">
                <div class="pt-1 mr-2">
                    <button type="submit" class="btn btn-primary custom_reset">Apply Filters</button>
                </div>
                <div class="pt-1">
                    <button class="btn btn-secondary custom_reset" wire:click="resetFilters">Reset Filters</button>
                </div>
            </div>
        </div>
    </div>
</form>

Here is the pagination:

<x-pagination.per-page :results="$results" />
@if ($results->hasPages())
    <div class="float-right">
        {{ $results->links() }}
    </div>
@endif

Here is the component:

<?php

namespace App\Http\Livewire\Logs;

use App\SbFileLog;
use App\WildcardSiloFiles;
use Carbon\Carbon;
use App\Destination;
use App\ImportQueue;
use Livewire\Component;
use App\Enums\StatusEnum;
use App\SbMappedConnection;
use Livewire\WithPagination;
use App\Services\PassThroughService;
use Illuminate\Support\Facades\Request;

class SbImportLog extends Component
{
    use WithPagination;

    /**
     * @var string
     */
    protected $paginationTheme = 'bootstrap';

    /**
     * @var string[]
     */
    protected $queryString = [
        'logStartDate',
        'logEndDate',
        'search',
        'mappedConnection',
        'importStatus',
        'hasNotes'
    ];

    /**
     * @var
     */
    public $flashMessage;

    /**
     * @var int
     */
    public $perPage = 25;

    /**
     * @var
     */
    public $importStatusName;

    /**
     * @var null
     */
    public $logStartDate = null;

    /**
     * @var null
     */
    public $logEndDate = null;

    /**
     * @var null
     */
    public $search = null;

    /**
     * @var null
     */
    public $mappedConnection = null;

    /**
     * @var null
     */
    public $importStatus = null;

    /**
     * @var null
     */
    public $hasNotes = null;

    /**
     * @return void
     */
    public function mount()
    {
        $importStatusId = Request::query('importStatus', null);
        $this->logStartDate = (Request::query('from_date')) ?? Request::query('logStartDate', null);
        $this->logEndDate =(Request::query('end_date')) ?? Request::query('logEndDate', null);
        $this->importStatus = Request::query('importStatus', null);
        $this->hasNotes = Request::query('hasNotes', null);
        $this->mappedConnection = Request::query('mappedConnection', null);
        if ($importStatusId) {
            $this->importStatusName = StatusEnum::getStatus($importStatusId);
        }
    }

    /**
     * Create the search query
     *
     * @return object
     */
    public function getLogsQueryProperty(): object
    {
        if ($this->logStartDate && $this->logEndDate == null) {
            $this->logEndDate = now()->format('Y-m-d');
        } elseif ($this->logStartDate == null) {
            $this->logStartDate = Carbon::today()->subDays(2)->format('Y-m-d');
        }
        if(!$this->hasNotes)
        {
            $this->hasNotes = null;
        }
        return SbFileLog::query()
            ->leftJoin('sb_files', 'sb_files.id', '=', 'sb_file_logs.sb_file_id')
            ->leftJoin('crossover', 'crossover.sb_file_id', '=', 'sb_file_logs.sb_file_id')
            ->join('sb_mapped_connections', 'sb_mapped_connections.id', '=', 'sb_file_logs.mapped_connection_id')
            ->select(
                'sb_mapped_connections.name',
                'sb_files.friendly_name',
                'sb_file_logs.*',
                'crossover.silo_id',
                'crossover.id as crossover_id'
            )
            ->when($this->logStartDate ?? null, function ($query, $startDate) {
                return $query->whereDate('sb_file_logs.created_at', '>=', Carbon::parse($startDate));
            })
            ->when($this->logEndDate ?? null, function ($query, $endDate) {
                return $query->whereDate('sb_file_logs.created_at', '<=', Carbon::parse($endDate));
            })
            ->when($this->importStatus ?? null, function ($query, $importStatus) {
                return $query->where('import_status', $importStatus);
            })
            ->when($this->mappedConnection ?? null, function ($query, $connMaps) {
                return $query->where('name', $connMaps);
            })
            ->when($this->hasNotes ?? null, function ($query) {
                return $query->where('notes', '!=', '');
            })
            ->when($this->search ?? null, function ($query) {
                $query->searchFilters(['friendly_name'], $this->escapeLike($this->search));
            })
            ->groupBy('sb_file_logs.id')
            ->whereNotNull('sb_file_logs.import_status')
            ->with('sb_file.sb_file_x_sb_group');
    }

    /**
     * Get the initial set of reports when page is loaded
     *
     * @return object
     */
    public function getLogsProperty(): object
    {
        return $this->logsQuery->paginate($this->perPage);
    }

    /**
     * Escape search string to allow _ in search.
     *
     * @param $string
     * @return string
     */
    private function escapeLike($string): string
    {
        $search = array('%', '_');
        $replace = array('_', '\_');
        return str_replace($search, $replace, $string);
    }

    /**
     * Process search with filters
     *
     * @return object
     */
    public function processSearch(): object
    {
        $this->emit('initializeRowExpander');
        return $this->logsQuery->paginate($this->perPage);
    }

    public function render()
    {
        $mappedConnectionsArray = SbMappedConnection::orderBy('name')->get();
        $destinations = Destination::orderBy('name')->get();

        $this->dispatchBrowserEvent('contentChanged');

        //dd($this->logs);
        return view('livewire.logs.sb-import-log', [
            'results' => $this->logs,
            'mappedConnections' => $mappedConnectionsArray,
            'destinations' => $destinations
        ]);
    }

    /**
     * Reset form fields for search
     *
     * @return void
     */
    public function resetFilters()
    {
        $this->logStartDate = null;
        $this->logEndDate = null;
        $this->search = null;
        $this->mappedConnection = null;
        $this->importStatus = null;
        $this->hasNotes = null;
        $this->resetPage();
    }

    /**
     * Update page when filters are applied. Rest pagination to
     * page 1 to avoid no results when on another page and applying
     * new filters or search.
     *
     * @return void
     */
    public function updatingFilters()
    {
        $this->resetPage();
    }
}

Thank you in advance for your help.

0 likes
1 reply
LaryAI's avatar
Level 58

To prevent the query from being generated and the filters being applied when clicking any link in the pagination, you can modify the Livewire component as follows:

  1. Add a new property called $applyFilters and set its initial value to false in the component:
/**
 * @var bool
 */
public $applyFilters = false;
  1. Modify the processSearch method to set the $applyFilters property to true:
/**
 * Process search with filters
 *
 * @return object
 */
public function processSearch(): object
{
    $this->applyFilters = true;
    $this->emit('initializeRowExpander');
    return $this->logsQuery->paginate($this->perPage);
}
  1. Modify the getLogsQueryProperty method to apply the filters only when $applyFilters is true:
/**
 * Create the search query
 *
 * @return object
 */
public function getLogsQueryProperty(): object
{
    if ($this->logStartDate && $this->logEndDate == null) {
        $this->logEndDate = now()->format('Y-m-d');
    } elseif ($this->logStartDate == null) {
        $this->logStartDate = Carbon::today()->subDays(2)->format('Y-m-d');
    }
    if (!$this->hasNotes) {
        $this->hasNotes = null;
    }
    $query = SbFileLog::query()
        ->leftJoin('sb_files', 'sb_files.id', '=', 'sb_file_logs.sb_file_id')
        ->leftJoin('crossover', 'crossover.sb_file_id', '=', 'sb_file_logs.sb_file_id')
        ->join('sb_mapped_connections', 'sb_mapped_connections.id', '=', 'sb_file_logs.mapped_connection_id')
        ->select(
            'sb_mapped_connections.name',
            'sb_files.friendly_name',
            'sb_file_logs.*',
            'crossover.silo_id',
            'crossover.id as crossover_id'
        )
        ->when($this->applyFilters, function ($query) {
            return $query
                ->when($this->logStartDate ?? null, function ($query, $startDate) {
                    return $query->whereDate('sb_file_logs.created_at', '>=', Carbon::parse($startDate));
                })
                ->when($this->logEndDate ?? null, function ($query, $endDate) {
                    return $query->whereDate('sb_file_logs.created_at', '<=', Carbon::parse($endDate));
                })
                ->when($this->importStatus ?? null, function ($query, $importStatus) {
                    return $query->where('import_status', $importStatus);
                })
                ->when($this->mappedConnection ?? null, function ($query, $connMaps) {
                    return $query->where('name', $connMaps);
                })
                ->when($this->hasNotes ?? null, function ($query) {
                    return $query->where('notes', '!=', '');
                })
                ->when($this->search ?? null, function ($query) {
                    $query->searchFilters(['friendly_name'], $this->escapeLike($this->search));
                });
        })
        ->groupBy('sb_file_logs.id')
        ->whereNotNull('sb_file_logs.import_status')
        ->with('sb_file.sb_file_x_sb_group');

    return $query;
}
  1. Modify the render method to reset the $applyFilters property after rendering the view:
public function render()
{
    $mappedConnectionsArray = SbMappedConnection::orderBy('name')->get();
    $destinations = Destination::orderBy('name')->get();

    $this->dispatchBrowserEvent('contentChanged');

    $this->applyFilters = false;

    return view('livewire.logs.sb-import-log', [
        'results' => $this->logs,
        'mappedConnections' => $mappedConnectionsArray,
        'destinations' => $destinations
    ]);
}

With these changes, the filters will only be applied when the "Apply Filters" button is clicked, and not when clicking any link in the pagination.

Please or to participate in this conversation.