NeVeDlE's avatar

Pagination to 2 seperate variables

Hello, I'm trying to make a search in my website which works on posts/users, so i want if a user searched for something the code will search for it 2 models like here

//That's inside the Post Model
   public function scopeFilter($query, $search)
    {
        $query->when($search ?? false, function ($query, $search) {
            $query->where(function ($query) use ($search) {
                $query->where('title', 'like', '%' . $search . '%')
                    ->orWhere('body', 'like', '%' . $search . '%')
                    ->orWhere('excerpt', 'like', '%' . $search . '%');
            });
        });
    }
//And that's inside the User model
   public function scopeFilter($query, $search)
    {
        $query->where('username', 'like', '%' . $search . '%')->orWhere('name', 'like', '%' . $search . '%');
    }

//and in the controller im returning data as follow
  if(request('search')) {
                $users = User::Filter(request('search'))->paginate(6)->withQueryString();
                $posts = Post::Filter(request('search'))->paginate(6)->withQueryString();
                return view('posts.search-index', [
                    'posts' => $posts,
                    'users'=>$users,
                ]);
            }

now in the blade file i have to make 2 tables for each model's data(Post,User) to show search results, but what i really want is to show both results in 1 table using 1 paginate for all the data gathered

0 likes
19 replies
Sinnbeck's avatar

I am just trying to wrap my head around this. You want to show one table in total with a random mix of users and posts? Is that correctly understood?

NeVeDlE's avatar

@Sinnbeck i think i solved what i meant above but i got a new issue when im trying to use LengthAwarePaginator it doesnt correctly work it keeps displaying the same items from the first page

if(request('search')) {
                $users = User::Filter(request('search'))->get();
                $posts = Post::Filter(request('search'))->get();
                $result=$users->merge($posts);
                $results=new LengthAwarePaginator($result,$result->count(),10);
                $results=$results->withQueryString();
                return view('posts.search-index', [
                    'results' => $results,
                ]);
            }
Sinnbeck's avatar

You can give this a try

$usersQuery = User::filter(request('search'));
$fullQuery = Post::filter(request('search'))->union($usersQuery);
$results = \DB::query()
    ->fromSub($fullQuery, "all_items")
     ->paginate(6);
NeVeDlE's avatar

@Sinnbeck

SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: select count(*) as aggregate from ((select * from `posts` where (`title` like %search% or `body` like %search% or `excerpt` like %search%)) union (select * from `users` where (`username` like %search% or `name` like %search%))) as `all_items`)
Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

@NeVeDlE Yeah you of course need to make them select the same columns. That is the problem with mixing two different kinds of data. Here is an example.

$usersQuery = User::filter(request('search'))->select('id', 'name');
$fullQuery = Post::filter(request('search'))->select('id', 'name')->union($usersQuery);
$results = \DB::query()
    ->fromSub($fullQuery, "all_items")
     ->paginate(6);
1 like
NeVeDlE's avatar

@estudiante_uap i didint use the union i used array merge to make both collections as 1 like this

if (request('search')) {
                $users = User::Filter(request('search'))->get();
                $posts = Post::Filter(request('search'))->get();
                $result = $users->merge($posts);
                $results = $this->paginate($result);
                $results = $results->withQueryString();
                return view('posts.search-index', [
                    'results' => $results,
                ]);
            }
    public function paginate($items, $perPage = 10, $page = null, $options = [])
    {
        $page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
        $items = $items instanceof Collection ? $items : Collection::make($items);
        return new LengthAwarePaginator($items->forPage($page, $perPage), $items->count(), $perPage, $page, $options);
    }

and in my blade i used if conditions to display each model

  <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
                            <table class="min-w-full divide-y divide-gray-200">
                                <tbody class="bg-white divide-y divide-gray-200">
                                @foreach($results as $result)
                                    @if($result instanceof \App\Models\User)
                                        @if($result->id==auth()->id())
                                            @continue
                                        @endif
                                        <x-search-index-user :user="$result"/>
                                    @else
                                        <x-search-index-post :post="$result"/>
                                    @endif
                                @endforeach
                                <!-- More people... -->
                                </tbody>
                            </table>
                            {{$results->links()}}
                        </div>
1 like
Sinnbeck's avatar

@NeVeDlE Just be aware that this will extract and build every single user and post in your database. Might not be an issue now, but when you someday have 5000 posts, it will be quire slow :)

1 like
NeVeDlE's avatar

@Sinnbeck oh then i'll start working around ur previous reply thanks for advice!

NeVeDlE's avatar

@Sinnbeck Does it return every post and user even though im using scope filter which i search for items like this

public function scopeFilter($query, $search)
    {
        $query->where('username', 'like', '%' . $search . '%')->orWhere('name', 'like', '%' . $search . '%');
    }

and there's one for Post model i posted above. the problem that i want it to be sent as a User model bcz in my blade file i check if i follow the owner of the post or nah so ican display Follow or dont Follow

<x-a-connections :following="Auth::User()->following"
                             :currentPerson="$user"/>

which display a form if i follow him or aform if im not like this,

@props(['following','bool'=>false,'currentPerson'])
@foreach($following as $follow)
    @if($follow->id==$currentPerson['id'])
        <x-followed-button :follow="$follow"/>
        @php
            $bool=true;
        @endphp
        @break
    @endif
@endforeach
@if(!$bool)
    <x-not-followed-button :follow="$currentPerson"/>
@endif

but now u said that it'll take some time in the future, im lost now and have no idea what to do xD

Sinnbeck's avatar

@NeVeDlE and personally I would create two tables. If I know I am looking for a user, it will be annoying to have every other record be a post

NeVeDlE's avatar

@Sinnbeck I'm using clockwork ithink it does the same job and that's the queries

User	
SELECT *
FROM `users`
WHERE `id` = 32
LIMIT 1

1.82 ms
User	
SELECT `users`.*,
  `follows`.`followed_by` AS `pivot_followed_by`,
  `follows`.`followed` AS `pivot_followed`
FROM `users`
  INNER JOIN `follows` ON `users`.`id` = `follows`.`followed`
WHERE `follows`.`followed_by` = 32

0.47 ms
Post	
SELECT *
FROM `posts`
WHERE (
    `title` like '%s%'
    or `body` like '%s%'
    or `excerpt` like '%s%'
  )

0.34 ms
User	
SELECT *
FROM `users`
WHERE (
    `username` like '%s%'
    or `name` like '%s%'
  )

0.34 ms
User	
SELECT *
FROM `users`
WHERE `users`.`id` in (
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10,
    11,
    12,
    14,
    16,
    18,
    20,
    22,
    24,
    28,
    30,
    32
  )

0.31 ms

and about making 2 tables i just wanted the search to be more global like facebook or somthing like that, but if it's a pain i'll just revert back to 2 tables.

NeVeDlE's avatar

@Sinnbeck the union must have the same number of columns and the columns must also have similar data types but i need different result from each model so idon't know how to handle it

Sinnbeck's avatar

@NeVeDlE if they aren't the same then why have them in the same table? Seems like a mistake

You can have 2 paginations on the same page that work individually

NeVeDlE's avatar

@Sinnbeck oh so i should just make it 2 tables instead okay got it, what about my scopeFilters are they good or need to change something? thanks in advance

Sinnbeck's avatar

@NeVeDlE no that should be fine. Just add a name for each like this

Post::filter(request('search'))->paginate(6, ['*'], 'postPage')->withQueryString();
User::filter(request('search'))->paginate(6, ['*'], 'userPage')->withQueryString(); 
1 like

Please or to participate in this conversation.