Menggunakan ATAU dengan EntityFieldQuery

26

Saya belum pernah memiliki kebutuhan untuk melakukan ini sebelum hari ini, tetapi sepertinya Anda tidak dapat membuat ATAU dengan EntityFieldQuery, karena db_ordigunakan untuk pertanyaan pilihan.

Contoh akan sampai ke semua entitas yang memiliki bidang tanggal di mana nilainya nol atau setelah hari ini.

Apakah saya kehilangan sesuatu atau beberapa trik atau apakah ini tidak didukung?

googletorp
sumber
Anda juga dapat membagi satu kueri menjadi dua, menjalankannya dan kemudian bergabung dengan hasilnya.
Vadym Myrgorod
Dampak kinerja ini cukup mengerikan jika kueri atau jumlah data bahkan lebih besar.
Tommi Forsström
1
Ini sudah lama tetapi tinggi di hasil google saya - perlu dicatat bahwa Anda dapat menggunakan orConditionGroup untuk ini di Drupal 8.
ognockocaten

Jawaban:

22

Saya telah melihat solusi dari masalah ini . Idenya adalah untuk digunakan addTag()dalam kueri dan implementasi hook_query_TAG_alter(), di mana Anda memiliki SelectQueryobjek lama yang baik .

Michael
sumber
Saya akan mengusulkan memilih ini sebagai jawaban yang tepat. Posting blog menyediakan metode untuk menambahkan persyaratan ATAU ke EntityFieldQueries. Satu-satunya masalah adalah bahwa Anda benar-benar membangun ketergantungan SQL dengan metode itu, yang agak bertentangan dengan seluruh poin EFQ, tetapi setidaknya menyelesaikan pekerjaan. Terima kasih atas tautan bagusnya @Michael.
Tommi Forsström
2
Karena ini adalah jawaban komunitas, dan sebagian besar terdiri dari tautan eksternal, saya merasa seperti kodenya, atau setidaknya sebagian isi artikel, harus dimasukkan dalam jawaban ini. Karena tautan mati. Diskusi Meta StackExchange tentang topik ini
D. Visser
Artikel asli agak panjang dan idenya dapat diringkas sebagai "menggunakan addTag () dalam kueri dan mengimplementasikan hook_query_TAG_alter ()". Setelah itu pertanyaannya direduksi menjadi "Bagaimana cara menggunakan ATAU dengan objek SelectQuery" yang dikenal sebagai subjek.
Michael
Saya akan sangat menyarankan mengonversi EFQ ke kueri pemilihan reguler dalam skenario ini. Tetap menggunakan EFQ dan menggunakan perubahan berbasis tag untuk mengacaukan apa yang dihasilkannya mengerikan jika dibandingkan.
phils
12

Anda dapat subklas EntityFieldQuerydan mengganti beberapa metode.

Kondisi yang ditambahkan ke objek kelas EntityFieldQuery(misalnya kondisi properti) ditambahkan ke array.

  public function propertyCondition($column, $value, $operator = NULL) {
    // The '!=' operator is deprecated in favour of the '<>' operator since the
    // latter is ANSI SQL compatible.
    if ($operator == '!=') {
      $operator = '<>';
    }
    $this->propertyConditions[] = array(
      'column' => $column, 
      'value' => $value, 
      'operator' => $operator,
    );
    return $this;
  }

Ketika kueri dibuat, array itu kemudian digunakan dalam satu loop yang mirip dengan yang berikut (kode hadir di EntityFieldQuery :: propertyQuery () ):

foreach ($this->propertyConditions as $property_condition) {
  $this->addCondition($select_query, "$base_table." . $property_condition['column'], $property_condition);
}

$select_queryberisi nilai yang dikembalikan dari panggilan ke db_select().

kiamlaluno
sumber
5

Anda tidak dapat saya takut, OR tidak didukung secara alami oleh EntityFieldQuerykelas.

Salah satu caranya adalah menambahkan tag ke kueri dengan ->addTag(), lalu menerapkannya hook_query_TAG_alter()untuk mengubah struktur internal kueri secara manual untuk kueri yang berisi tag itu.

Dengan melakukan ini, Anda dapat mengulangi kondisi yang ada dan melakukan perubahan yang diperlukan untuk menambahkan ORlogika Anda . Ini bukan cara yang cantik untuk melakukannya; Anda dapat menemukan contoh di sini .

Clive
sumber
5

Tidak perlu membagi kueri menjadi 2 dan menggabungkan atau semacamnya. Hanya perlu mengubah kueri

Pertimbangkan skenario: Saya memiliki 2 tipe entitas dengan nama mesin: pernyataan tincan, dan tincan_agents

5 bidang referensi entitas pada entitas

4 di antaranya adalah bidang referensi entitas reguler dan bidang ke-5 (tincan_object) adalah bidang referensi multi-entitas, setiap bidang referensi mereferensikan entitas tipe 'Agen'.

Bidang referensi tincan_object dapat merujuk Agen dan Aktivitas (tipe entitas ketiga). Agen memiliki object_type properti, yang bisa berupa Agen atau Grup.

Saya ingin menemukan Pernyataan yang merujuk salah satu dari beberapa Agen yang mungkin ada, di bidang referensi mana pun. Kami membutuhkan operator ATAU antara fieldConditions, tetapi kami juga perlu memeriksa object_type dari bidang referensi jenis multi-entitas, dan pastikan itu salah satu dari dua kemungkinan.

Kode di bawah ini mewakili yang paling sederhana yang mungkin, dalam solusi kami kueri memiliki banyak kondisi, bidang, dll ... sehingga kode yang diperlukan tidak mengandalkan urutan kondisi, atau bahkan jika semua bidang ini sedang ditanyakan.

    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'tincan_statement');

    $all_agents = array(4,10); //entity_ids to search for
    $query->addTag('tincan_statement_get_agents');
    $query->fieldCondition('tincan_actor', 'target_id', $all_agents, 'IN'); 
    //need OR between fields conditions
    $query->fieldCondition('tincan_authority', 'target_id', $all_agents, 'IN');
//need OR between fields conditions
    $query->fieldCondition('tincan_instructor', 'target_id', $all_agents, 'IN');
//need OR between fields conditions
    $query->fieldCondition('tincan_team', 'target_id', $all_agents, 'IN');
//need OR between fields conditions
//but then nested in the OR structure we need an AND for two columns of the multientity type reference field tincan_object
    $query->fieldCondition('tincan_object', 'target_id', $all_agents, 'IN');
    $query->fieldCondition('tincan_object', 'object_type', array('Agent', 'Group'), 'IN');
    $results = $query->$execute();

Solusi: Pemberitahuan di EntityFieldQuery di atas

 $query->addTag('tincan_statement_get_agents');

Ini menandai kueri, memungkinkan penerapan hook_query_TAG_alter ()

/**
 * Implements hook_query_TAG_alter()
 * alters the query for finding agents with or without the related_agents flag
 * used for Statement API Get processor EntityFieldQuery
 */
function tincan_lrs_query_tincan_statement_get_agents_alter(QueryAlterableInterface $query) {
  //need to or the search for all the fields (actor, object, authority, instructor, team)
  // the object_type of the object field needs to be Agent OR Group

  $conditions =& $query->conditions();
  // dsm($conditions);  //dsm() is your friend! comes with devel module
  $agent_grouping_condition = db_or(); 
  $object_parameters = array();
  $x = 0;
  foreach ($conditions as $key => $condition) {
    if (is_numeric($key) && isset($condition['field']) && is_scalar($condition['field'])) {
      if ( (strpos($condition['field'], 'tincan_object_object_type') !== FALSE  ||
          strpos($condition['field'], 'tincan_object_target_id') !== FALSE ) && $condition['operator'] == 'IN') {
  //u
            unset($conditions[$key]);
            $object_parameters[$x]['field'] = $condition['field'];
            $object_parameters[$x]['value'] = $condition['value'];
            $object_parameters[$x]['operator'] = $condition['operator'];
            $x += 1;
          }

       if(strpos($condition['field'], 'tincan_actor_target_id') !== FALSE ||
          strpos($condition['field'], 'tincan_instructor_target_id') !== FALSE ||
          strpos($condition['field'], 'tincan_team_target_id') !== FALSE ||
          strpos($condition['field'], 'tincan_authority_target_id') !== FALSE ) {
            unset($conditions[$key]);
            $agent_grouping_condition->condition($condition['field'], $condition['value'], $condition['operator']);

      } 
    }
  }

  // create new AND condition to nest in our OR condition set for the object parameters
  $object_condition = db_and();
  foreach($object_parameters as $key => $param) {
    $object_condition->condition($param['field'], $param['value'], $param['operator']);
  }

  $agent_grouping_condition->condition($object_condition);

  $query->condition($agent_grouping_condition);

  //By default EntityFieldQuery uses inner joins, change to left
  $tables =& $query->getTables();

  foreach($tables as $key => $table) {
    if (strpos($key, 'field_data_tincan_object') !== FALSE ||
        strpos($key, 'field_data_tincan_actor') !== FALSE ||
        strpos($key, 'field_data_tincan_authority') !== FALSE ||
        strpos($key, 'field_data_tincan_instructor') !== FALSE ||
        strpos($key, 'field_data_tincan_team') !== FALSE ) {
          if(!is_null($table['join type'])) {
            $tables[$key]['join type'] = 'LEFT';
          }
    }
  }

}
jackrabbithanna
sumber
2

OP ingin menanyakan entitas dengan tanggal null ATAU lebih besar dari x, saya ingin meminta node tanpa bahasa yang ditentukan ATAU bahasa pengguna. addTag()adalah solusi terbaik untuk menambahkan pernyataan ATAU aktual, tetapi akan berlebihan dalam kasus saya. ATAU yang sangat sederhana dapat dilakukan dengan mencari properti bahasa dalam array menggunakan:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
lmeurs
sumber