Usando OR con EntityFieldQuery

26

Nunca he tenido la necesidad de hacer esto antes, pero no parece que pueda hacer consultas OR EntityFieldQuery, ya que db_orse utiliza para consultas seleccionadas.

Un ejemplo llegaría a todas las entidades que tienen un campo de fecha donde el valor es nulo o posterior a hoy.

¿Me estoy perdiendo algo o algún truco o simplemente no es compatible?

googletorp
fuente
También puede dividir una consulta en dos, ejecutarlas y luego unir resultados.
Vadym Myrgorod
El impacto en el rendimiento de esto es bastante horrible si las consultas o la cantidad de datos son incluso remotamente mayores.
Tommi Forsström
1
Esto es antiguo pero está en lo alto de mis resultados de Google. Cabe señalar que puede usar orConditionGroup para esto en Drupal 8.
ognockocaten

Respuestas:

22

He visto una solución a este problema . La idea es usar addTag()en consultas e implementar hook_query_TAG_alter(), donde tiene un buen SelectQueryobjeto antiguo .

Miguel
fuente
Propondría seleccionar esto como la respuesta correcta. La publicación del blog proporciona un método para agregar la condicionalidad OR a EntityFieldQueries. El único problema es que usted realmente construye dependencia de SQL con ese método, lo cual es un poco contra el punto completo de los EFQ, pero al menos hace el trabajo. Gracias por el buen enlace @Michael.
Tommi Forsström
2
Dado que esta es una respuesta de la comunidad, y la mayor parte consiste en un enlace externo, creo que el código, o al menos algunos de los contenidos del artículo, deberían incluirse en esta respuesta. Porque los enlaces mueren. Discusión de Meta StackExchange sobre este tema
D. Visser
El artículo original es bastante largo y la idea se puede resumir como "use addTag () en la consulta e implemente hook_query_TAG_alter ()". Después de eso, la pregunta se ha reducido a "Cómo usar OR con el objeto SelectQuery", que es un tema conocido.
Michael
Recomiendo encarecidamente convertir el EFQ en una consulta de selección regular en este escenario. En comparación, seguir con el EFQ y usar alteraciones basadas en etiquetas para meterse con lo que produce es horrible.
PHILS
12

Puede subclasificar EntityFieldQueryy anular algunos métodos.

Las condiciones que se agregan a un objeto de clase EntityFieldQuery(por ejemplo, una condición de propiedad) se agregan a una matriz.

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

Cuando se genera la consulta, esa matriz se usa en un bucle similar al siguiente (el código está presente en EntityFieldQuery :: propertyQuery () ):

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

$select_querycontiene el valor devuelto por una llamada a db_select().

kiamlaluno
fuente
5

No me temo, los OR no son compatibles de forma nativa con la EntityFieldQueryclase.

Una forma de evitarlo podría ser agregar una etiqueta a la consulta con with ->addTag(), luego implementar hook_query_TAG_alter()para cambiar la estructura interna de la consulta manualmente para consultas que contengan esa etiqueta.

Al hacer esto, podrá recorrer las condiciones existentes y realizar las modificaciones necesarias para agregar su ORlógica. Sin embargo, no es una forma bonita de hacerlo; Puedes encontrar un ejemplo aquí .

Clive
fuente
5

No es necesario dividir las consultas en 2 y fusionar ni nada de eso. Solo necesito alterar la consulta

Considere el escenario: tenía 2 tipos de entidad con nombres de máquina: declaraciones de tincan y tincan_agents

5 campos de referencia de entidad en la entidad

4 de ellos son campos de referencia de entidad regular y el quinto (tincan_object) es un campo de referencia de tipo multi-entidad, cada campo de referencia hace referencia a entidades de tipo 'Agente'.

El campo de referencia tincan_object puede hacer referencia a Agentes y Actividades (un tercer tipo de entidad). Un agente tiene una propiedad object_type, que puede ser Agent o Group.

Quiero encontrar cualquier Declaración que haga referencia a uno de varios Agentes posibles, en cualquiera de los campos de referencia. Necesitamos un operador OR entre las condiciones de campo, pero también debemos verificar el tipo de objeto del campo de referencia de tipo de entidad múltiple y asegurarnos de que sea una de las dos posibilidades.

El siguiente código representa el más simple posible, en nuestra solución, la consulta tenía muchas otras condiciones, campos, etc., por lo que el código no debía contar con el orden de las condiciones, o incluso si se consultaban todos estos campos.

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

Solución: Aviso en el EntityFieldQuery anterior

 $query->addTag('tincan_statement_get_agents');

Esto etiqueta la consulta, permitiendo la implementación de 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
fuente
2

El OP quiere consultar entidades con fecha nula O mayor que x, quería consultar nodos sin lenguaje definido O el idioma del usuario. addTag()es la mejor solución para agregar una declaración OR real, pero sería excesivo en mi caso. Mi OR muy simple se puede lograr buscando la propiedad del lenguaje en una matriz usando:

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