Home > Back-end >  Laravel query builder doesn't work with COALESCE
Laravel query builder doesn't work with COALESCE

Time:12-23

This MySQL query works perfectly and return values in both subject and body_html columns.

SELECT
    translations.id,
    COALESCE(locale.subject, fallback.subject) as subject,
    COALESCE(locale.body_html, fallback.body_html) as body_html 
FROM
    translations
LEFT JOIN
    translations AS locale
    ON locale.translatable_id = translations.translatable_id
    AND locale.translatable_type = translations.translatable_type
    AND locale.locale = "fr" 
LEFT JOIN
    translations AS fallback
    ON fallback.translatable_id = translations.translatable_id
    AND fallback.translatable_type = translations.translatable_type
    AND fallback.locale = "en" 
WHERE
    translations.translatable_id = 1
    AND translations.translatable_type = "App\\Models\\Email" 
LIMIT 1;

Now the same with the Laravel query builder, (note: the variables used here have a correct value):

return Translation::selectRaw(
        'translations.id,'.
        'COALESCE(locale.subject, fallback.subject) AS subject,'.
        'COALESCE(locale.body_html, fallback.body_html) AS body_html'
    )
    ->where('translations.translatable_id', $this->id)
    ->where('translations.translatable_type', get_class($this))
    ->leftJoin('translations AS locale', function ($join) use($locale) { 
        $join->on('locale.translatable_id', 'translations.translatable_id')
            ->where('locale.translatable_type', 'translations.translatable_type')
            ->where('locale.locale', $locale);
    })
    ->leftJoin('translations AS fallback', function ($join) {
        $join->on('fallback.translatable_id', 'translations.translatable_id')
            ->where('fallback.translatable_type', 'translations.translatable_type')
            ->where('fallback.locale', config('app.fallback_locale'));
    })
    ->first();

The id column value is correct but subjectand body_html column values are empty.
Why ?

CodePudding user response:

Your query is mostly correct, but there's a mistake in both of your joins:

->leftJoin('translations AS locale', function ($join) use($locale) { 
    $join->on('locale.translatable_id', 'translations.translatable_id')
        ->where('locale.translatable_type', 'translations.translatable_type') // <- HERE
        ->where('locale.locale', $locale);
})
->leftJoin('translations AS fallback', function ($join) {
    $join->on('fallback.translatable_id', 'translations.translatable_id')
        ->where('fallback.translatable_type', 'translations.translatable_type') // <- HERE
        ->where('fallback.locale', config('app.fallback_locale'));
})

When you do where('fallback.translatable_type', 'translations.translatable_type'), it's not comparing the 2 columns. It's trying to match fallback.translatable_type to the string literal 'translations.translatable_type'.

Simply replace those where methods by either whereColumn or on and it should work.

DB::table('translations')->selectRaw(
        'translations.id,'.
        'COALESCE(locale.subject, fallback.subject) AS subject,'.
        'COALESCE(locale.body_html, fallback.body_html) AS body_html'
    )
    ->where('translations.translatable_id', 1)
    ->where('translations.translatable_type', 'App\\Models\\Translation')
    ->leftJoin('translations AS locale', function ($join) { 
        $join->on('locale.translatable_id', 'translations.translatable_id')
            ->whereColumn('locale.translatable_type', 'translations.translatable_type')
            ->where('locale.locale', 'fr');
    })
    ->leftJoin('translations AS fallback', function ($join) {
        $join->on('fallback.translatable_id', 'translations.translatable_id')
            ->whereColumn('fallback.translatable_type', 'translations.translatable_type')
            ->where('fallback.locale', config('app.fallback_locale'));
    })
    ->first();
DB::table('translations')->selectRaw(
        'translations.id,'.
        'COALESCE(locale.subject, fallback.subject) AS subject,'.
        'COALESCE(locale.body_html, fallback.body_html) AS body_html'
    )
    ->where('translations.translatable_id', 1)
    ->where('translations.translatable_type', 'App\\Models\\Translation')
    ->leftJoin('translations AS locale', function ($join) { 
        $join->on('locale.translatable_id', 'translations.translatable_id')
            ->on('locale.translatable_type', 'translations.translatable_type')
            ->where('locale.locale', 'fr');
    })
    ->leftJoin('translations AS fallback', function ($join) {
        $join->on('fallback.translatable_id', 'translations.translatable_id')
            ->on('fallback.translatable_type', 'translations.translatable_type')
            ->where('fallback.locale', config('app.fallback_locale'));
    })
    ->first();
  • Related