web-dev-qa-db-ja.com

JOINされたテーブルからの2つの列が同じ名前を持つときにSELECT句でエイリアスを適用する方法

ゲームリストを作成しようとしていますが、現在、詳細なチーム情報を取得するのに苦労しています。

#__ game

id     team_home     team_guest
1      1             2
2      2             3

#__ team

id     name
1      My Team Name
2      Another Team Name
3      The third Team

マイコード:

// Get the user object.
$user = JFactory::getUser();
// Get the databse object.
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('a.*');
$query->select('b.name', 'home_name');
$query->select('c.name', 'guest_name');
$query->from($db->quoteName('#__game', 'a'));
$query->join('LEFT', $db->quoteName('#__team', 'b') . ' ON ' . $db->quoteName('a.team_home') . ' = ' . $db->quoteName('b.id'));
$query->join('LEFT', $db->quoteName('#__team', 'c') . ' ON ' . $db->quoteName('a.team_guest') . ' = ' . $db->quoteName('c.id'));
$query->where($db->quoteName('a.published') . ' = 1');
$query->order('a.kickoff ASC');
// Implement View Level Access (if set in table)
if (!$user->authorise('core.options', 'com_component'))
{
    $columns = $db->getTableColumns('#__game');
    if(isset($columns['access']))
    {
        $groups = implode(',', $user->getAuthorisedViewLevels());
        $query->where('a.access IN (' . $groups . ')');
    }
}
$db->setQuery((string)$query);
$items = $db->loadObjectList();
echo '<pre>' . var_export($items,true).'</pre>';
$options = array();
if ($items)
{
    $options[] = JHtml::_('select.option', '', 'Select an option');
    foreach($items as $item)
    {
        //$options[] = JHtml::_('select.option', $item->id, $item->home_name . ' vs ' . $item->guest_name);
    }
}
return $options;

これは、戻りオブジェクト内に「名前」を1つだけ作成し(確かに、家とゲストで同じキーです)、ゲストは家の値を上書きします。

家とゲストの名前が異なるオブジェクトキーに配置されるようにするために、結合用に何を記述する必要がありますか?

1
Proximate

問題の詳細説明:

発見したように、クエリのSELECT句の2つの列が同じ列名(またはエイリアス)を共有すると、後者の値が結果セットの前者を上書きします。これを克服するには、一意のエイリアスを割り当てて、2つの繰り返される列名を区別します。

Joomlaには、quoteName()ヘルパーメソッド内でエイリアスを割り当てる(または割り当てない)ための既製のテクニックがあります。最初のパラメーターとして列名の配列をフィードし、2番目のパラメーターとして対応するエイリアス(またはnull)の配列をフィードするだけです。これが 既存のデモ です。

コードレビュー:

  • 結果セットでid列と2つのチーム名のみを使用しているため、貪欲な_*_を使用して_#__game_テーブルのすべての列値を収集する必要はありません。返される列の数が減ると、単一のselect()呼び出しを含む単一のquoteName()呼び出しを使用するほうが魅力的になります。
  • qn()は、quoteName()メソッドのエイリアスです。これらを使用すると、メソッドの完全な単語のスペルと比較して、スクリプト全体の幅/膨張を減らすのに役立ちます。
  • ヘルパーメソッドチェーンは、クエリの全体的なサイズを減らすのにも役立ちます。これは、SQL文字列を構築するときに_$query_を再入力し続ける必要がないことを意味します。
  • ゲームテーブルのIDとチームテーブルのホーム/ゲストIDの関係は絶対に不可欠です(一方が存在しないと意味がありません)。つまり、FOREIGN KEY関係があるため、LEFT JOINではなくJOIN(INNER JOIN)を使用する方が適切です。 LEFT JOINは、クエリが欠落しているIDを返して欠落しているテーブル行にアタッチし、NULL値を提示できるため、より寛容です。投稿のタイトル(私が編集する前)に基づいて、正しい結合を使用することを確信していないようです。
  • ASCがデフォルトのソート方向なので、これらの文字を削除して同じ機能を楽しむことができます。一方、この宣言型スタイルを使用する場合は、そのままにしておいても害はありません。
  • ゲームテーブルの完全なスキーマは表示されませんが、isset()の呼び出しから、テーブルに列が呼び出される場合とされない場合があるという可能性に対応しているという印象を受けました。このため、getTableColumns()の賢明な使い方のようです。ただし、列が常にテーブルに存在する場合、phpのアクセス列チェックを削除し、ユーザーのauthorise()チェックが失敗したときにwhere()句をクエリに追加するだけです。
  • _$query_を文字列型データとしてキャストする必要はありません。これはJoomlaの以前のバージョンでしばしば実証されましたが、単に不必要です。
  • loadObjectList()は、結果セットに行がない場合に空の配列を生成するため、構文エラーがないと想定して、foreach()の前の条件を省略できます。
  • 空のオプション配列を宣言してから、「オプションの選択」オプションをそれにプッシュする代わりに、最初の要素を内部に持つ配列を宣言することにより、1つのステップでこれを行うことができます。静的なデフォルトオプションを無条件にロードすると、selectに常にオプションがある(値のマークアップを行う)ことが保証されます。そうしないと、「根性」のない_<select>_タグが含まれる可能性があります。

実装された提案:

_$db = JFactory::getDBO();
$query = $db
    ->getQuery(true)
    ->select($db->qn(['a.id', 'b.name', 'c.name'], [null, 'home_name', 'guest_name']))
    ->from($db->qn('#__game', 'a'))
    ->innerJoin($db->qn('#__team', 'b') . ' ON ' . $db->qn('a.team_home') . ' = ' . $db->qn('b.id'))
    ->innerJoin($db->qn('#__team', 'c') . ' ON ' . $db->qn('a.team_guest') . ' = ' . $db->qn('c.id'))
    ->where($db->qn('a.published') . ' = 1')
    ->order('a.kickoff');

$user = JFactory::getUser();
if (!$user->authorise('core.options', 'com_component'))
{
    $columns = $db->getTableColumns('#__game');
    if(isset($columns['access']))
    {
        $groups = implode(',', $user->getAuthorisedViewLevels());
        $query->where('a.access IN (' . $groups . ')');
    }
}

$db->setQuery($query);
$options = [JHtml::_('select.option', '', 'Select an option')];
foreach ($db->loadObjectList() as $item)
{
    $options[] = JHtml::_('select.option', $item->id, $item->home_name . ' vs ' . $item->guest_name);
}
return $options;
_
1
mickmackusa

わかりました。SELECTでASを配列として設定する必要がありました。

    $db = JFactory::getDBO();
    $query = $db->getQuery(true);
    $query->select('a.*');
    $query->select($db->quoteName(array('b.name'),array('home_name')));
    $query->select($db->quoteName(array('c.name'),array('guest_name')));
...
1
Proximate