Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.2k views
in Technique[技术] by (71.8m points)

php - Failing validation doesn't stop code execution in livewire component

I'm trying to show a list of models. Having a text filter posed no problem but when trying to add a year filter, problems started occurring.

If a non-numeric value is sent back from the from or to input, I expected validation to catch it and just flash some errors to the session but instead I still get SQL errors because either from or to were not numeric (for example: invalid input syntax for integer: "2020a" (SQL: select * f rom "my_models" where "year" between 2020a and 2021))

Livewire Component code

class ModelList extends Component
{
    use WithPagination;
    
    public $from;
    public $to;
    public $search;

    protected $models;

    protected $rules = [
        'from'   => ['required', 'integer', 'between:1970,2021'],
        'to'     => ['required', 'integer', 'between:1970,2021'],
        'search' => ['nullable', 'string'],
    ];

    public function mount()
    {
        $this->from   = (int) date('Y') - 10;
        $this->to     = (int) date('Y');
        $this->search = null;

        $this->doQuery();
    }

    public function updating($name, $value)
    {
        $this->validate();
        $this->doQuery();
        $this->resetPage();
    }

    public function render()
    {
        return view('livewire.list', ['models' => $this->models]);
    }

    protected function doQuery()
    {
        $this->models = MyModel::query()
            ->whereBetween('year', [$this->from, $this->to])
            ->search($this->search)
            ->orderByDesc('year')
            ->paginate(10);
    }
}

View

<div>
    <div id="filters" class="flex flex-col text-gray-800">
        <div>
            <input type="number" wire:model.lazy="from" min="1970" max="{{ date('Y') }}">
            @error('from')
                <span class="text-red-800">{{ $message }}</span>
            @enderror
        </div>
        <div>
            <input type="number" wire:model.lazy="to" min="1970" max="{{ date('Y') }}">
            @error('to')
                <span class="text-red-800">{{ $message }}</span>
            @enderror
        </div>
        <div>
            <input type="text" wire:model.lazy="search">
            @error('search')
                <span class="text-red-800">{{ $message }}</span>
            @enderror
        </div>
    </div>
    <div id="list">
        @foreach ($models as $model)
            {{ $model->id }}<br>
        @endforeach
    </div>
    <div id="pagination">
        {{ $models->links() }}
    </div>
</div>

I've tried different things like

  • just doing the query inside the render() method, inlining the doQuery() method.
  • just doing the query inside updating() method.
  • just doing the query inside mount() method.
  • doing the query inside both updating(), mount(), and render() methods.
  • calling $this->validate() inside render()

but none of the above seem to work with bad input. Sometimes $models gets unset and I get an error for calling the links() method on it. If that doesn't happen, then I get an SQL error because the bad input went right through the validation.

I know I could use a select input or add some javascript to prevent non-numeric values but that doesn't fix the underlying issue of not being able to trust Livewire to validate my input. (And it only takes opening the console and writing a couple of lines to completely invalidate such flimsy validation.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you really want to validate your input you have to wrap your input fields into a form tag. like:

<form>
    <div id="filters" class="flex flex-col text-gray-800">
        <div>
            <input type="number" wire:model.lazy="from" min="1970" max="{{ date('Y') }}">
            @error('from')
                <span class="text-red-800">{{ $message }}</span>
            @enderror
        </div>
        <div>
            <input type="number" wire:model.lazy="to" min="1970" max="{{ date('Y') }}">
            @error('to')
                <span class="text-red-800">{{ $message }}</span>
            @enderror
        </div>
        <div>
            <input type="text" wire:model.lazy="search">
            @error('search')
                <span class="text-red-800">{{ $message }}</span>
            @enderror
        </div>
    </div>
</form>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...