laravel / ideas

Issues board used for Laravel internals discussions.
938 stars 28 forks source link

Add filter method to Eloquent\Builder to let filter accessors #2508

Open rinodrummer opened 3 years ago

rinodrummer commented 3 years ago

Hi!

Sometimes would be helpful to have the possibility to filter by accessors before calling the get() method.

I thought on a solution which will let this customization:

<?php
    namespace Illuminate\Database\Eloquent;

    // Uses...

    class Builder {
        // Properties...

        /**
         * Stores a queue of filters.
         *
         * @var array<callable>
         */
        protected array $filters = [];

        // Methods...

        /**
         * Adds a filter to the filters queue.
         *
         * @param callable $f
         * @return $this
         */
        public function filter(callable $f) {
            $this->filters[] = $f;

            return $this;
        }

        /**
         * Execute the query as a "select" statement.
         * Filters the query
         *
         * @param array|string $columns
         * @param bool $ignoreFilters
         * @return \Illuminate\Database\Eloquent\Collection|static[]
         */
        public function get($columns = ['*'], bool $ignoreFilters = false)
        {
            $builder = $this->applyScopes();

            // If we actually found models we will also eager load any relationships that
            // have been specified as needing to be eager loaded, which will solve the
            // n+1 query issue for the developers to avoid running a lot of queries.
            if (count($models = $builder->getModels($columns)) > 0) {
                $models = $builder->eagerLoadRelations($models);
            }

            $result = $builder->getModel()->newCollection($models);

            // Added code
            if (!$ignoreFilters && sizeof($this->filters) > 0) {
                foreach ($this->filters as $filterCb) {
                    if (empty($result)) {
                        break;
                    }

                    $result->filter($filterCb);
                }
            }

            return $result;
        }
    }

Doing like this, it's possible to pass a filtered query as an argument and filter query results by accessors, opening the doors to a lot of other useful combinations.

Obviously it needs to be observed and discussed.

Let me know what you think!