Laravel - Union + Paginate at the same time?

后端 未结 12 539
有刺的猬
有刺的猬 2020-12-09 04:20

Brief:

I am trying to union 2 tables recipes and posts then add ->paginate(5) to the queries.

But

相关标签:
12条回答
  • 2020-12-09 04:41

    Reiterating jdme's answer with a more elegant method from Illuminate\Database\Query\Builder.

    $recipes = DB::table("recipes") ..
    $items = DB::table("posts")->union($recipes) ..
    
    $query = DB::query()
        ->fromSub($items, "some_query_name");
    
    // Let's paginate!
    $query->paginate(5);
    

    I hope this helps!

    0 讨论(0)
  • 2020-12-09 04:42

    for paginate collection do this:

    add this to boot function in \app\Providers\AppServiceProvider

      /**
             * Paginate a standard Laravel Collection.
             *
             * @param int $perPage
             * @param int $total
             * @param int $page
             * @param string $pageName
             * @return array
             */
            Collection::macro('paginate', function($perPage, $total = null, $page = null, $pageName = 'page') {
                $page = $page ?: LengthAwarePaginator::resolveCurrentPage($pageName);
                return new LengthAwarePaginator(
                    $this->forPage($page, $perPage),
                    $total ?: $this->count(),
                    $perPage,
                    $page,
                    [
                        'path' => LengthAwarePaginator::resolveCurrentPath(),
                        'pageName' => $pageName,
                    ]
                );
            });
    

    From hereafter for all collection you can paginate like your code

    $items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at")
                    ->where("user_id", "=", $id)
                    ->union($recipes)
                    ->paginate(5)
    
    0 讨论(0)
  • 2020-12-09 04:45

    For those who may still look for the answer, I have tried union and paginate together and got right result under laravel 5.7.20. This will be better than merging collections then paginate which will not work on big amount of data.

    Some demo code (in my case, I will deal with multiple databases with same table name):

    $dbs=["db_name1","db_name2"]; 
    $query=DB::table("$dbs[0].table_name");
    for($i=1;$i<count($log_dbs);$i++){
        $query=DB::table("$dbs[$i].table_name")->union($query);
    }
    $query=$query->orderBy('id','desc')->paginate(50);
    

    I haven't tried on other higher version of laravel. But at least it could work now!

    More information

    My previous version of laravel is 5.7.9 which will report the Cardinality violation error. So the laravel team solved this issue in some version of 5.7.x.

    0 讨论(0)
  • 2020-12-09 04:46

    You're right, pagination cause problem. Right now, you can create a view and query the view instead of the actual tables, or create your Paginator manually:

    $page = Input::get('page', 1);
    $paginate = 5;
    
    $recipes = DB::table("recipes")->select("id", "title", "user_id", "description", "created_at")
                ->where("user_id", "=", $id);
    $items = DB::table("posts")->select("id", "title", "user_id", "content", "created_at")
                ->where("user_id", "=", $id)
                ->union($recipes)
                ->get();
    
    $slice = array_slice($items->toArray(), $paginate * ($page - 1), $paginate);
    $result = Paginator::make($slice, count($items), $paginate);
    
    return View::make('yourView',compact('result'));
    
    0 讨论(0)
  • 2020-12-09 04:46

    The accepted answer works great for Query Builder.

    But here's my approach for Laravel Eloquent Builder.

    Assume that we're referring to same Model

    $q1 = Model::createByMe();       // some condition
    $q2 = Model::createByMyFriend(); // another condition
    
    $q2->union($q1);
    $querySql = $q2->toSql();
    
    $query = Model::from(DB::raw("($querySql) as a"))->select('a.*')->addBinding($q2->getBindings());
    
    $paginated_data = $query->paginate();
    

    I'm using Laravel 5.6

    0 讨论(0)
  • 2020-12-09 04:47

    Using Eloquent

    I adapted jdme's answer in order to use it with Eloquent. I created a class extending the default Eloquent Builder and overiding the union method to fix the issue with paginate.

    Create app\Builder\BuilderWithFixes.php:

    <?php
    
    namespace App\Builder;
    
    use Illuminate\Database\Eloquent\Builder;
    
    class BuilderWithFixes extends Builder
    {
        /**
         * Add a union statement to the query.
         *
         * @param  \Illuminate\Database\Query\Builder|\Closure  $query
         * @param  bool  $all
         * @return \Illuminate\Database\Query\Builder|static
         */
        public function union($query, $all = false)
        {
            $query = parent::union($query, $all);
            $querySql = $query->toSql();
            return $this->model->from(\DB::raw("($querySql) as ".$this->model->table))->select($this->model->table.'.*')->addBinding($this->getBindings());
        }
    }
    

    In you Model (for example app\Post.php), include the method newEloquentBuilder below to replace the default Eloquent Builder with \App\Builder\BuilderWithFixes:

    <?php
    
    namespace App;
    
    use Eloquent as Model;
    
    class Post extends Model
    {
        // your model stuffs...
    
        public function newEloquentBuilder($query)
        {
            return new \App\Builder\BuilderWithFixes($query);
        }
    }
    

    Now you can use union + paginate at the same time within your model (in this case Post) normally, like:

    $recipes = Recipe::select("id", "title", "user_id", "description", "created_at")
                     ->where("user_id", "=", $id);
    
    $items = Post::select("id", "title", "user_id", "content", "created_at")
                 ->where("user_id", "=", $id)
                 ->union($recipes)
                 ->paginate(5);
    
    0 讨论(0)
提交回复
热议问题