2016-09-10 4 views
1

私は私のウェブサイトのテキストのさまざまなブロックを格納するテーブルがあります。私は現在、ページのスラッグと好みの言語を使って、各ページの正しいブロックを選択しています。お気に入りの言語が利用できない場合、フォールバック言語で同じテキストブロック(同じタイトル)を選択したいと思います。別の値が存在しない場合、ある値に基づいてテーブルからアイテムを選択する方法はありますか? (雄弁/ sql)

ブロックテーブル

columns: 
| id | slug | title | language | content | 

entries: 
| 1 | home | first | en | the first block | 
| 2 | home | first | nl | het eerste blok | 
| 3 | home | second | en | the second block | 
    --> block "second" not available for the 'nl' language 

必要な出力現在、私はこれにアプローチする方法がわからないので、私は唯一、選ばれる好ましい言語でブロックを選択しています

fallback language = en 

selected prefered language = en, output: 
| 1 | home | first | en | the first block | 
| 3 | home | second | en | the second block | 

selected prefered language = nl, output: 
| 2 | home | first | nl | het eerste blok | 
| 3 | home | second | en | the second block | 
    --> select this one, because the 'nl' version is not available 

「優先言語」ブロックが利用できない場合は、「フォールバック言語」ブロックを選択します。私は両方の言語で2つのクエリを実行しようとする可能性がありますし、何らかの形でそれらをマージし、このブロックのタイトルカウントが1よりも低い場合にのみ 'フォールバック言語'ブロックを挿入しますが、これは非常に精巧で、私のアプリのために

私は雄弁使用しています:

$blocks = Block::->where('slug', '=', 'home') 
       ->whereIn('language', $selectedLanguage) 
       ->get(); 

私は雄弁でこれを行うことができますどのように? (または生のSQLの場合は?)

答えて

2

フォールバック言語にLEFT JOINを使用して、優先言語のエントリが存在するかどうかを確認できます。たとえば、あなたのpreferd言語が「NL」ですとフォールバック言語は、クエリは次のようになります(EN)の場合:言葉で参加

select blocks.* 
from blocks 
left join blocks b1 
    on b1.slug = blocks.slug 
    and b1.title = blocks.title 
    and b1.language = 'nl' 
    and blocks.language <> 'nl' 
where blocks.slug = 'home' 
    and blocks.language in ('nl', 'en') 
    and b1.id is null 

sqlfiddle

が気にいらないなどとすることができます。より良い翻訳を探してください同じslugおよびtitleについては、言語が好ましい言語の場合は、blocks.language <> 'nl'のため一致がありません。そうしないと、結合は、優先変換('b1.language = 'nl')を「検索」します。

WHERE節では、より良い翻訳が見つからない場合にのみ行を返すように指示しています(b1.id is null)。

私は雄弁にクエリを変換するために何ができるベストです:

$prefered = 'nl'; 
$fallback = 'en'; 

$blocks = App\Block::where('blocks.slug', '=', 'home') 
    ->whereIn('blocks.language', [$prefered, $fallback]) 
    ->leftJoin('blocks as b1', function($join) { 
     $join->on('b1.slug', '=', 'blocks.slug') 
      ->on('b1.title', '=', 'blocks.title') 
      ->on('b1.language', '=', DB::raw('?')) 
      ->on('blocks.language', '<>', DB::raw('?')) 
     ; 
    }) 
    ->whereNull('b1.id') 
    ->addBinding([$prefered, $prefered], 'join') 
    ->select(DB::raw('blocks.*')) 
    ->get() 
; 

注:私はtitleは、すべての言語でのブロックについても同様であると仮定しています。それ以外の場合は、ブロックを識別するために別の列(block_idなど)が必要になります。

+0

私は左の結合でこのようにします。存在するかどうかをチェックしないでください。ブロックが存在しない場合は、ブロックを挿入してください。ありがとう! – FeatherNL

1

これを行うにはGROUP_CONCATを使用できます。

  1. グループby title、1行にすべての関連する文字列を取得する。
  2. GROUP_CONCATORDER BYを使用して、最初に希望の言語を入力します。
  3. SUBSTRING_INDEXを使用して、最初の文字列のみを抽出します。

例クエリ:

私が正しくあなたを理解していれば、すでに同じ slugtitle用れる好ましい言語を持つ行がある場合、あなたはフォールバック言語で行を「削除」したい
SELECT SUBSTRING_INDEX(GROUP_CONCAT(content ORDER BY IF(language='nl',1,IF(language='en',2,3))),',',1) 
FROM block 
GROUP BY title; 
+0

この回答もテストしました。それは完全に動作し、コードは非常にシンプルで非常にエレガントです!私は@Paulの答えを好む。その答えは、雄弁に使用するために翻訳するほうがずっと簡単です。ありがとう! – FeatherNL

関連する問題