salahaldain-abduljalil's avatar

I want to process data from database without collection

Excuse me guys, I have products table and promote_products table and belongsTo products I want to get all products from database I need to arrange them as two products and the third as a promoter, if it exists, and so on without using collection, only through the database queries. Can I do that or not?

0 likes
12 replies
jlrdw's avatar

Yes let the database do the work. You should not need to query all at one time however. Query by category. Perhaps show some code you tried so far and then someone can guide you.

But for the products that have a promotion, you could flag them and show as a promotion if the flag is set to true.

salahaldain-abduljalil's avatar

@jlrdw But I need to shift the projects as promotion on the top not on its origin place for Example when projects are displayed, two are displayed as a regular and the third is a promoter important I want to handle that with the query and this is my code to getting the point

        $queryProjects = Project::
            ->selectRaw($selectRaw)
            ->where([['verified', true], ['active', true]])
               ->orderBy('id', 'DESC');

        $allProjects                = $queryProjects->get();
        $promotedProjectsCollection = collect($queryProjects->promoted()->get());

        $promotedProjectsCollection = $promotedProjectsCollection->keyBy('id');

        $isAllPromoted = $allProjects->every(function ($project) use ($promotedProjectsCollection) {
            return $promotedProjectsCollection->contains('id', $project->id);
        });

        if ($isAllPromoted && ! $allProjects->isEmpty()) {

            $allProjectsWithPromoted = $allProjects;
        } else {

            $filteredProjects = $allProjects->reject(function ($project) use ($promotedProjectsCollection) {
                return $promotedProjectsCollection->contains('id', $project->id);
            });

            $i                       = 0;
            $allProjectsWithPromoted = [];
            foreach ($filteredProjects as $project) {
                $allProjectsWithPromoted[] = $project;
                $i++;
                if ($i % 2 == 0 && ! $promotedProjectsCollection->isEmpty()) {
                    $allProjectsWithPromoted[] = $promotedProjectsCollection->shift();
                }
            }
        }

        $collection     = collect($allProjectsWithPromoted);
        $perPage        = 20;
        $currentPage    = request()->input('page', 1);
        $paginatedItems = $collection->slice(($currentPage - 1) * $perPage, $perPage)->values();

        $paginator = new \Illuminate\Pagination\LengthAwarePaginator(
            $paginatedItems,
            $collection->count(),
            $perPage,
            $currentPage,
            [
                'path'  => request()->url(),
                'query' => request()->query(),
            ]
        );
Tray2's avatar

@salahaldain-abduljalil If I understand you correctly, you want to set override the order by and prioritize promoted projects.

You can easily handle that with a regular subquery.

Something like.

SELECT *, (SELECT COUNT(*) FROM promoted_projects WHERE project_id = p.id) prio
FROM projects p
ORDER (SELECT COUNT(*) FROM promoted_projects WHERE project_id = p.id) DESC, p.title;
salahaldain-abduljalil's avatar

@Tray2 What I want is to arrange the projects in the form of two new, unpromoted projects and the third promoted, meaning all the newer projects are next to the newer ones, but after every two, the third is promoted, regardless of whether its ID is new or old means like this unpromoted unpromoted promoted unpromoted unpromoted promoted and so on.

salahaldain-abduljalil's avatar

@Tray2 Everything goes well with my code By collections and shift all promoted projects for Entire project But I need someway to change it via query to make it more Efficient , For Example in another databases like oracle etc.. You can do this via the row_number function but it is not supported inside mysql.

jlrdw's avatar

@salahaldain-abduljalil

Lookup the loop in the blade chapter

Something like:

@if($loop->iteration % 3 == 0)

Something like that, not tested.

See https://laravel.com/docs/12.x/blade#the-loop-variable

Or setup your own counter something like:

Just quick pseudocode:

$count = 1; 

foreach ($whatever as $key) {
    if ($count % 3 === 0) {
        do whatever;
    }else{
        do other thing
    $count++;
}

And paginate. Work out keeping track of the next page of data with count. You may need to pass current back to controller in the query string and even initialize it in the controller to maintain your every third item.

I normally just use a little trial and error til I figure out this type of thing.

1 like
jaseofspades88's avatar

Why wouldn't you use collections? The methods only largely defer to native array methods anyway and it tends to be cleaner and easier to understand the pipelines.

jlrdw's avatar

@jaseofspades88 I agree a collection is fine for a smaller amount of data.

However I wouldn't put tens of thousands of records in a collection.

But working with a smaller query result, then yes collections can be very helpful.

I only setup things to query what's needed.

I have seen replies where someone starts using a collection with say a couple hundred records. Later as the data grows they see first hand that regular queries paginated is much better.

But @salahaldain-abduljalil mentioned all data. That's why I suggested only get what's needed. Then a collection would or could work well.

krisi_gjika's avatar
Level 14

since you want to show "unpromoted unpromoted promoted unpromoted unpromoted promoted" why not use 2 paginated queries? Something like:

$promoted = Product::query()->promoted()->paginate(5);

// assuming 1 page should show 15 products, you can calculate 1/3 and 2/3 from any page size
// both queries use the same 'page' query param by design so that their pages move together
$unpromoted = Product::query()->unpromoted()->paginate($promoted->isNotEmpty() ? 10 : 15);


// products/index.blade.php
@foreach($unpromoted as $product)
		@if($loop->iteration % 3 == 0 && isset($promoted[($loop->iteration / 3) - 1]))
				{{ $promoted[($loop->iteration / 3) - 1]?->name }}
		@endif
		
		{{ $product->name }}
@endforeach

{{ $unpromoted->links() }}

Please or to participate in this conversation.