carstenjaksch's avatar

Get page with matching related site id or page without any related sites

Hi! šŸ‘‹

I have a little query or brain issue.

This query should only return one page, but it returns both the page with matching related site id AND the page without any related sites.

What am I doing wrong?

$site = app(Site::class); // set during middleware
$errorPage = Page::where('type', '404')->where(function (Builder $query) use ($site) {
            $query->whereHas('sites', function (Builder $query) use ($site) {
                $query->where('id', $site->id);
            })->orDoesntHave('sites');
        })->get();

Page and Site models are part of a "Many to Many (Polymorphic)" relationship.

0 likes
5 replies
LaryAI's avatar
Level 58

To address your issue, you need to ensure that your query logic correctly prioritizes pages with a matching related site ID over pages without any related sites. The current query returns both types of pages because it uses an or condition that doesn't enforce exclusivity.

To achieve the desired behavior, you can use a more refined approach by first attempting to find a page with a matching related site ID. If no such page exists, then you can fall back to finding a page without any related sites.

Here's a solution that should work for your case:

$site = app(Site::class); // set during middleware

// First, try to find a page with a matching related site ID
$errorPage = Page::where('type', '404')
    ->whereHas('sites', function (Builder $query) use ($site) {
        $query->where('id', $site->id);
    })
    ->first();

// If no such page exists, find a page without any related sites
if (!$errorPage) {
    $errorPage = Page::where('type', '404')
        ->doesntHave('sites')
        ->first();
}

This code first attempts to find a 404 page that has a related site with the given site ID. If such a page is found, it is returned. If not, the code then looks for a 404 page that does not have any related sites.

This approach ensures that you only get one page, either the one with the matching related site ID or the one without any related sites, depending on availability.

MohamedTammam's avatar

whereHas and orDoesntHave are cancelling each other.

$query->whereHas('sites', ...)
	 ->orDoesntHave('sites')

I guess you meant to only use whereHas and first

$errorPage = Page::where('type', '404')->where(function (Builder $query) use ($site) {
            $query->whereHas('sites', function (Builder $query) use ($site) {
                $query->where('id', $site->id);
            });
        })->first();
carstenjaksch's avatar

@MohamedTammam The whereHas on its own gives me the page that has a matching related site, but doesn't fall back to a page without any related site, if no site was found. The more I research, the more I think that this is not possible with one query.

MohamedTammam's avatar

@carstenjaksch

The whereHas on its own gives me the page that has a matching related site, but doesn't fall back to a page without any related site

You need first to check it there's a result, you use. Otherwise you run the other query. But not both at the same time.

Unless you want to write subquery.

Please or to participate in this conversation.