Sử dụng OR với EntityFieldQuery


26

Tôi chưa bao giờ có nhu cầu làm điều này trước ngày hôm nay, nhưng có vẻ như bạn không thể thực hiện các truy vấn OR EntityFieldQuery, vì db_orđược sử dụng cho các truy vấn chọn lọc.

Một ví dụ sẽ nhận được tất cả các thực thể có trường ngày trong đó giá trị là null hoặc sau ngày hôm nay.

Tôi có thiếu một cái gì đó hoặc một số mẹo hay đơn giản là nó không được hỗ trợ?


Bạn cũng có thể chia một truy vấn thành hai, chạy chúng và sau đó tham gia kết quả.
Vadym Myrgorod

Tác động hiệu suất của điều này là khá khủng khiếp nếu các truy vấn hoặc lượng dữ liệu thậm chí còn lớn hơn từ xa.
Tommi Forsström

1
Điều này đã cũ nhưng cao trong kết quả google của tôi - cần lưu ý rằng bạn có thể sử dụng orConditiongroup cho việc này trong Drupal 8.
ognockocaten

Câu trả lời:


22

Tôi đã thấy một giải pháp của vấn đề này . Ý tưởng là sử dụng addTag()trong truy vấn và thực hiện hook_query_TAG_alter(), nơi bạn có SelectQueryđối tượng cũ tốt .


Tôi đề nghị chọn đây là câu trả lời đúng. Bài đăng trên blog cung cấp một phương pháp để thêm điều kiện OR vào EntityFieldQueries. Vấn đề duy nhất là bạn thực sự xây dựng sự phụ thuộc SQL với phương thức đó, điều này chống lại toàn bộ quan điểm của EFQ, nhưng ít nhất là hoàn thành công việc. Cảm ơn vì liên kết tốt @Michael.
Tommi Forsström

2
Vì đây là câu trả lời của cộng đồng và hầu hết bao gồm một liên kết bên ngoài, tôi cảm thấy giống như mã, hoặc ít nhất là một số nội dung của bài viết, nên được đưa vào câu trả lời này. Vì liên kết làm chết. Thảo luận về Meta StackExchange về chủ đề này
D. Visser

Bài viết gốc khá dài và ý tưởng có thể được tóm tắt là "sử dụng addTag () trong truy vấn và triển khai hook_queryiah_alter ()". Sau đó, câu hỏi đã được rút gọn thành "Cách sử dụng OR với đối tượng SelectQuery" là chủ đề đã biết.
Michael

Tôi thực sự khuyên bạn nên chuyển đổi EFQ sang truy vấn chọn thông thường trong kịch bản này. Gắn bó với EFQ và sử dụng các thay đổi dựa trên thẻ để gây rối với những gì nó tạo ra thật kinh khủng khi so sánh.
phils

12

Bạn có thể sublass EntityFieldQueryvà ghi đè một số phương thức.

Các điều kiện được thêm vào một đối tượng của lớp EntityFieldQuery(ví dụ: điều kiện thuộc tính) được thêm vào một mảng.

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

Khi truy vấn được xây dựng, mảng đó sẽ được sử dụng trong một vòng lặp tương tự như sau (mã có trong EntityFieldQuery :: propertyQuery () ):

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

$select_querychứa giá trị được trả về từ một cuộc gọi đến db_select().


5

Bạn không thể sợ, OR không được EntityFieldQuerylớp hỗ trợ .

Một cách có thể là thêm một thẻ vào truy vấn ->addTag(), sau đó thực hiện hook_query_TAG_alter()để thay đổi cấu trúc bên trong của truy vấn theo cách thủ công đối với các truy vấn có chứa thẻ đó.

Làm điều này bạn sẽ có thể lặp qua các điều kiện hiện có và thực hiện các thay đổi cần thiết để thêm ORlogic của bạn . Đó không phải là một cách hay để làm điều đó; bạn có thể tìm thấy một ví dụ ở đây .


5

Không cần phải chia các truy vấn thành 2 và hợp nhất hoặc bất cứ điều gì như thế. Chỉ cần thay đổi truy vấn

Hãy xem xét kịch bản: Tôi có 2 loại thực thể với tên máy: câu lệnh tincan và tincan_agents

5 trường tham chiếu thực thể trên thực thể

4 trong số đó là các trường tham chiếu thực thể thông thường và thứ 5 (tincan_object) là trường tham chiếu nhiều loại thực thể, mỗi trường tham chiếu tham chiếu một thực thể loại 'Tác nhân'.

Trường tham chiếu tincan_object có thể tham chiếu Đại lý và Hoạt động (loại thực thể thứ ba). Một Tác nhân có thuộc tính object_type, có thể là Đại lý hoặc Nhóm.

Tôi muốn tìm bất kỳ Tuyên bố nào tham chiếu một trong một số Đại lý có thể, trong bất kỳ trường tham chiếu nào. Chúng ta cần một toán tử OR giữa các trườngConditions, nhưng chúng ta cũng cần kiểm tra object_type của trường tham chiếu loại đa thực thể và đảm bảo rằng đó là một trong hai khả năng.

Mã dưới đây đại diện cho đơn giản nhất có thể, trong giải pháp của chúng tôi, truy vấn có nhiều điều kiện, trường khác, v.v ... vì vậy mã cần thiết để không tính theo thứ tự các điều kiện hoặc ngay cả khi tất cả các trường này được truy vấn.

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

Giải pháp: Lưu ý trong EntityFieldQuery ở trên

 $query->addTag('tincan_statement_get_agents');

Điều này gắn thẻ truy vấn, cho phép thực hiện hook_queryiah_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 muốn truy vấn các thực thể có ngày null HOẶC lớn hơn x, tôi muốn truy vấn các nút không có ngôn ngữ được xác định HOẶC ngôn ngữ của người dùng. addTag()là giải pháp tốt nhất để thêm một câu lệnh OR thực tế, nhưng sẽ là quá mức trong trường hợp của tôi. HOẶC rất đơn giản của tôi có thể được thực hiện bằng cách tra cứu thuộc tính ngôn ngữ trong một mảng bằng cách sử dụng:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.