การใช้ OR กับ EntityFieldQuery


26

ฉันไม่เคยมีความต้องการที่จะทำสิ่งนี้มาก่อนในวันนี้ แต่ดูเหมือนว่าคุณจะไม่สามารถค้นหาหรือใช้แบบสอบถามได้EntityFieldQueryเนื่องจากdb_orจะใช้สำหรับแบบสอบถามแบบใช้เลือกข้อมูล

ตัวอย่างจะไปที่เอนทิตีทั้งหมดที่มีฟิลด์วันที่ซึ่งค่าเป็นโมฆะหรือหลังจากวันนี้

ฉันทำบางสิ่งบางอย่างผิดเพี้ยนไปหรือไม่ได้รับการสนับสนุนหรือไม่?


นอกจากนี้คุณยังสามารถแยกแบบสอบถามหนึ่งรายการเป็นสองแบบสอบถามเรียกใช้แล้วเข้าร่วมผลลัพธ์ได้
Vadym Myrgorod

ผลกระทบต่อประสิทธิภาพของสิ่งนี้ค่อนข้างน่ากลัวหากข้อความค้นหาหรือปริมาณข้อมูลมีขนาดใหญ่ขึ้นจากระยะไกล
Tommi Forsström

1
นี่เก่า แต่สูงในผลลัพธ์ google ของฉัน - ควรสังเกตว่าคุณสามารถใช้ orConditionGroup สำหรับสิ่งนี้ใน Drupal 8
ognockocaten

คำตอบ:


22

ฉันเห็นวิธีแก้ไขปัญหานี้แล้ว แนวคิดคือใช้addTag()ในการสืบค้นและนำไปใช้hook_query_TAG_alter()ซึ่งคุณมีSelectQueryวัตถุเก่าที่ดี


ฉันเสนอให้เลือกสิ่งนี้เป็นคำตอบที่ถูกต้อง บล็อกโพสต์มีวิธีการเพิ่มหรือเป็นเงื่อนไขให้กับ EntityFieldQueries ปัญหาเดียวคือคุณสร้างการพึ่งพาของ SQL ด้วยวิธีนั้นซึ่งเป็นสิ่งที่ต่อต้าน EFQ ทั้งหมด แต่อย่างน้อยก็ทำให้งานเสร็จ ขอบคุณสำหรับลิงค์ที่ดี @Michael
Tommi Forsström

2
เนื่องจากนี่เป็นคำตอบของชุมชนและส่วนใหญ่ประกอบด้วยลิงก์ภายนอกฉันรู้สึกเหมือนรหัสหรืออย่างน้อยเนื้อหาบางส่วนของบทความจึงควรรวมอยู่ในคำตอบนี้ เพราะลิงก์ตาย การอภิปราย Meta StackExchange ในหัวข้อนี้
D. Visser

บทความต้นฉบับค่อนข้างยาวและแนวคิดสามารถสรุปได้ว่า "ใช้ addTag () ในการสืบค้นและใช้ hook_query_TAG_alter ()" หลังจากนั้นคำถามจะถูกลดลงเป็น "วิธีใช้หรือกับวัตถุ SelectQuery" ซึ่งเป็นที่รู้จักกันในหัวเรื่อง
ไมเคิล

ฉันขอแนะนำให้แปลง EFQ เป็นแบบสอบถามแบบใช้เลือกข้อมูลทั่วไปในสถานการณ์นี้ การเกาะติดกับ EFQ และใช้การดัดแปลงตามแท็กเพื่อยุ่งกับสิ่งที่สร้างขึ้นนั้นน่ากลัวโดยการเปรียบเทียบ
phils

12

คุณสามารถ sublass EntityFieldQueryและแทนที่บางวิธี

เงื่อนไขที่ถูกเพิ่มลงในวัตถุของคลาสEntityFieldQuery(เช่นเงื่อนไขคุณสมบัติ) จะถูกเพิ่มเข้าไปในอาร์เรย์

  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;
  }

เมื่อแบบสอบถามถูกสร้างขึ้นอาร์เรย์นั้นจะถูกใช้ในลูปคล้ายกับโค้ดต่อไปนี้ (มีรหัสอยู่ในEntityFieldQuery :: propertyQuery () ):

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

$select_querydb_select()มีค่าที่ส่งกลับจากการเรียก


5

คุณไม่สามารถกลัวได้ ORs ไม่ได้รับการสนับสนุนโดยEntityFieldQueryชั้นเรียน

วิธีหนึ่งในรอบนั้นอาจเป็นการเพิ่มแท็กในแบบสอบถามด้วยด้วย->addTag()จากนั้นนำhook_query_TAG_alter()ไปใช้เพื่อเปลี่ยนโครงสร้างภายในของแบบสอบถามด้วยตนเองสำหรับแบบสอบถามที่มีแท็กนั้น

การทำเช่นนี้คุณจะสามารถวนซ้ำตามเงื่อนไขที่มีอยู่และทำการเปลี่ยนแปลงที่จำเป็นเพื่อเพิ่มORตรรกะของคุณ มันไม่ใช่วิธีที่ดีที่จะทำ คุณสามารถหาตัวอย่างที่นี่


5

ไม่จำเป็นต้องแยกคิวรีออกเป็น 2 และผสานหรืออะไรทำนองนั้น เพียงแค่ต้องเปลี่ยนแบบสอบถาม

พิจารณาสถานการณ์: ฉันมีเอนทิตี 2 ประเภทพร้อมชื่อเครื่อง: คำสั่ง tincan และ tincan_agents

5 ช่องอ้างอิงเอนทิตีบนเอนทิตี

4 ของพวกเขาเป็นเขตข้อมูลอ้างอิงเอนทิตีปกติและที่ 5 (tincan_object) เป็นเขตข้อมูลอ้างอิงเอนทิตีหลายประเภทแต่ละเขตข้อมูลอ้างอิงอ้างอิงเอนทิตีประเภท 'ตัวแทน'

ฟิลด์อ้างอิง tincan_object สามารถอ้างอิงตัวแทนและกิจกรรม (ประเภทเอนทิตีที่สาม) ตัวแทนมีคุณสมบัติ object_type ซึ่งสามารถเป็น Agent หรือ Group

ฉันต้องการค้นหาคำแถลงใด ๆ ที่อ้างอิงหนึ่งในหลาย ๆ ตัวแทนที่เป็นไปได้ในฟิลด์อ้างอิงใด ๆ เราต้องการตัวดำเนินการ OR ระหว่าง fieldConditions แต่เราต้องตรวจสอบ object_type ของเขตข้อมูลอ้างอิงประเภทเอนทิตีหลายแห่งและตรวจสอบให้แน่ใจว่ามันเป็นหนึ่งในสองทางที่เป็นไปได้

รหัสด้านล่างแสดงถึงความเป็นไปได้ที่ง่ายที่สุดในการแก้ปัญหาของเราแบบสอบถามมีเงื่อนไขอื่น ๆ อีกมากมายเขตข้อมูล ฯลฯ ... ดังนั้นรหัสที่จำเป็นต้องไม่นับตามลำดับของเงื่อนไขหรือแม้ว่าเขตข้อมูลทั้งหมดเหล่านี้จะถูกสอบถาม

    $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();

วิธีแก้ปัญหา: แจ้งให้ทราบใน EntityFieldQuery ข้างต้น

 $query->addTag('tincan_statement_get_agents');

แท็กนี้เป็นแบบสอบถามทำให้สามารถใช้ 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';
          }
    }
  }

}

2

OP ต้องการสอบถามเอนทิตีที่มีวันที่เป็นโมฆะหรือใหญ่กว่า x ฉันต้องการค้นหาโหนดที่ไม่มีภาษาที่กำหนดไว้หรือภาษาของผู้ใช้ addTag()เป็นทางออกที่ดีที่สุดสำหรับการเพิ่มคำสั่ง OR จริง แต่จะเกินจริงในกรณีของฉัน ง่ายมากหรือสามารถทำได้โดยการค้นหาคุณสมบัติภาษาในอาร์เรย์โดยใช้:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.