"Fossies" - the Fresh Open Source Software Archive

Member "drupal-8.9.9/core/modules/views/src/Plugin/views/join/JoinPluginBase.php" (18 Nov 2020, 12765 Bytes) of package /linux/www/drupal-8.9.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "JoinPluginBase.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 
    3 namespace Drupal\views\Plugin\views\join;
    4 
    5 use Drupal\Core\Database\Query\SelectInterface;
    6 use Drupal\Core\Plugin\PluginBase;
    7 
    8 /**
    9  * @defgroup views_join_handlers Views join handler plugins
   10  * @{
   11  * Handler plugins for Views table joins.
   12  *
   13  * Handler plugins help build the view query object. Join handler plugins
   14  * handle table joins.
   15  *
   16  * Views join handlers extend \Drupal\views\Plugin\views\join\JoinPluginBase.
   17  * They must be annotated with \Drupal\views\Annotation\ViewsJoin annotation,
   18  * and they must be in namespace directory Plugin\views\join.
   19  *
   20  * Here are some examples of configuration for the join plugins.
   21  *
   22  * For this SQL:
   23  * @code
   24  * LEFT JOIN {two} ON one.field_a = two.field_b
   25  * @endcode
   26  * Use this configuration:
   27  * @code
   28  * $configuration = array(
   29  *   'table' => 'two',
   30  *   'field' => 'field_b',
   31  *   'left_table' => 'one',
   32  *   'left_field' => 'field_a',
   33  *   'operator' => '=',
   34  * );
   35  * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
   36  * @endcode
   37  * Note that the default join type is a LEFT join when 'type' is not supplied in
   38  * the join plugin configuration.
   39  *
   40  * If an SQL expression is needed for the first part of the left table join
   41  * condition, 'left_formula' can be used instead of 'left_field'.
   42  * For this SQL:
   43  * @code
   44  * LEFT JOIN {two} ON MAX(one.field_a) = two.field_b AND one.field_c = 'some_val'
   45  * @endcode
   46  * Use this configuration:
   47  * @code
   48  * $configuration = array(
   49  *   'table' => 'two',
   50  *   'field' => 'field_b',
   51  *   'left_table' => 'one',
   52  *   'left_formula' => 'MAX(one.field_a)',
   53  *   'operator' => '=',
   54  *   'extra' => array(
   55  *     0 => array(
   56  *       'left_field' => 'field_c',
   57  *       'value' => 'some_val',
   58  *     ),
   59  *   ),
   60  * );
   61  * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
   62  * @endcode
   63  *
   64  * For this SQL:
   65  * @code
   66  * INNER JOIN {two} ON one.field_a = two.field_b AND one.field_c = 'some_val'
   67  * @endcode
   68  * Use this configuration:
   69  * @code
   70  * $configuration = array(
   71  *   'type' => 'INNER',
   72  *   'table' => 'two',
   73  *   'field' => 'field_b',
   74  *   'left_table' => 'one',
   75  *   'left_field' => 'field_a',
   76  *   'operator' => '=',
   77  *   'extra' => array(
   78  *     0 => array(
   79  *       'left_field' => 'field_c',
   80  *       'value' => 'some_val',
   81  *     ),
   82  *   ),
   83  * );
   84  * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
   85  * @endcode
   86  *
   87  * For this SQL:
   88  * @code
   89  * INNER JOIN {two} ON one.field_a = two.field_b AND two.field_d = 'other_val'
   90  * @endcode
   91  * Use this configuration:
   92  * @code
   93  * $configuration = array(
   94  *   'type' => 'INNER',
   95  *   'table' => 'two',
   96  *   'field' => 'field_b',
   97  *   'left_table' => 'one',
   98  *   'left_field' => 'field_a',
   99  *   'operator' => '=',
  100  *   'extra' => array(
  101  *     0 => array(
  102  *       'field' => 'field_d',
  103  *       'value' => 'other_val',
  104  *     ),
  105  *   ),
  106  * );
  107  * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
  108  * @endcode
  109  *
  110  * For this SQL:
  111  * @code
  112  * INNER JOIN {two} ON one.field_a = two.field_b AND one.field_c = two.field_d
  113  * @endcode
  114  * Use this configuration:
  115  * @code
  116  * $configuration = array(
  117  *   'type' => 'INNER',
  118  *   'table' => 'two',
  119  *   'field' => 'field_b',
  120  *   'left_table' => 'one',
  121  *   'left_field' => 'field_a',
  122  *   'operator' => '=',
  123  *   'extra' => array(
  124  *     0 => array(
  125  *       'left_field' => 'field_c',
  126  *       'field' => 'field_d',
  127  *     ),
  128  *   ),
  129  * );
  130  * $join = Views::pluginManager('join')->createInstance('standard', $configuration);
  131  * @endcode
  132  *
  133  * Here is an example of a more complex join:
  134  * @code
  135  * class JoinComplex extends JoinPluginBase {
  136  *   public function buildJoin($select_query, $table, $view_query) {
  137  *     // Add an additional hardcoded condition to the query.
  138  *     $this->extra = 'foo.bar = baz.boing';
  139  *     parent::buildJoin($select_query, $table, $view_query);
  140  *   }
  141  * }
  142  * @endcode
  143  *
  144  * @ingroup views_plugins
  145  * @see plugin_api
  146  */
  147 
  148 /**
  149  * Represents a join and creates the SQL necessary to implement the join.
  150  *
  151  * Extensions of this class can be used to create more interesting joins.
  152  */
  153 class JoinPluginBase extends PluginBase implements JoinPluginInterface {
  154 
  155   /**
  156    * The table to join (right table).
  157    *
  158    * @var string
  159    */
  160   public $table;
  161 
  162   /**
  163    * The field to join on (right field).
  164    *
  165    * @var string
  166    */
  167   public $field;
  168 
  169   /**
  170    * The table we join to.
  171    *
  172    * @var string
  173    */
  174   public $leftTable;
  175 
  176   /**
  177    * The field we join to.
  178    *
  179    * @var string
  180    */
  181   public $leftField;
  182 
  183   /**
  184    * A formula to be used instead of the left field.
  185    *
  186    * @var string
  187    */
  188   public $leftFormula;
  189 
  190   /**
  191    * An array of extra conditions on the join.
  192    *
  193    * Each condition is either a string that's directly added, or an array of
  194    * items:
  195    *   - table(optional): If not set, current table; if NULL, no table. If you
  196    *     specify a table in cached configuration, Views will try to load from an
  197    *     existing alias. If you use realtime joins, it works better.
  198    *   - field(optional): Field or formula. In formulas we can reference the
  199    *     right table by using %alias.
  200    *   - left_field(optional): Field or formula. In formulas we can reference
  201    *     the left table by using %alias.
  202    *   - operator(optional): The operator used, Defaults to "=".
  203    *   - value: Must be set. If an array, operator will be defaulted to IN.
  204    *   - numeric: If true, the value will not be surrounded in quotes.
  205    *
  206    * @see SelectQueryInterface::addJoin()
  207    *
  208    * @var array
  209    */
  210   public $extra;
  211 
  212   /**
  213    * The join type, so for example LEFT (default) or INNER.
  214    *
  215    * @var string
  216    */
  217   public $type;
  218 
  219   /**
  220    * The configuration array passed by initJoin.
  221    *
  222    * @var array
  223    *
  224    * @see \Drupal\views\Plugin\views\join\JoinPluginBase::initJoin()
  225    */
  226   public $configuration = [];
  227 
  228   /**
  229    * How all the extras will be combined. Either AND or OR.
  230    *
  231    * @var string
  232    */
  233   public $extraOperator;
  234 
  235   /**
  236    * Defines whether a join has been adjusted.
  237    *
  238    * Views updates the join object to set the table alias instead of the table
  239    * name. Once views has changed the alias it sets the adjusted value so it
  240    * does not have to be updated anymore. If you create your own join object
  241    * you should set the adjusted in the definition array to TRUE if you already
  242    * know the table alias.
  243    *
  244    * @var bool
  245    *
  246    * @see \Drupal\views\Plugin\HandlerBase::getTableJoin()
  247    * @see \Drupal\views\Plugin\views\query\Sql::adjustJoin()
  248    * @see \Drupal\views\Plugin\views\relationship\RelationshipPluginBase::query()
  249    */
  250   public $adjusted;
  251 
  252   /**
  253    * Constructs a Drupal\views\Plugin\views\join\JoinPluginBase object.
  254    */
  255   public function __construct(array $configuration, $plugin_id, $plugin_definition) {
  256     parent::__construct($configuration, $plugin_id, $plugin_definition);
  257     // Merge in some default values.
  258     $configuration += [
  259       'type' => 'LEFT',
  260       'extra_operator' => 'AND',
  261     ];
  262     $this->configuration = $configuration;
  263 
  264     if (!empty($configuration['table'])) {
  265       $this->table = $configuration['table'];
  266     }
  267 
  268     $this->leftTable = $configuration['left_table'];
  269     $this->leftField = $configuration['left_field'];
  270     $this->field = $configuration['field'];
  271 
  272     if (!empty($configuration['left_formula'])) {
  273       $this->leftFormula = $configuration['left_formula'];
  274     }
  275 
  276     if (!empty($configuration['extra'])) {
  277       $this->extra = $configuration['extra'];
  278     }
  279 
  280     if (isset($configuration['adjusted'])) {
  281       $this->adjusted = $configuration['adjusted'];
  282     }
  283 
  284     $this->extraOperator = strtoupper($configuration['extra_operator']);
  285     $this->type = $configuration['type'];
  286   }
  287 
  288   /**
  289    * {@inheritdoc}
  290    */
  291   public function buildJoin($select_query, $table, $view_query) {
  292     if (empty($this->configuration['table formula'])) {
  293       $right_table = $this->table;
  294     }
  295     else {
  296       $right_table = $this->configuration['table formula'];
  297     }
  298 
  299     if ($this->leftTable) {
  300       $left_table = $view_query->getTableInfo($this->leftTable);
  301       $left_field = $this->leftFormula ?: "$left_table[alias].$this->leftField";
  302     }
  303     else {
  304       // This can be used if left_field is a formula or something. It should be used only *very* rarely.
  305       $left_field = $this->leftField;
  306       $left_table = NULL;
  307     }
  308 
  309     $condition = "$left_field = $table[alias].$this->field";
  310     $arguments = [];
  311 
  312     // Tack on the extra.
  313     if (isset($this->extra)) {
  314       $this->joinAddExtra($arguments, $condition, $table, $select_query, $left_table);
  315     }
  316 
  317     $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
  318   }
  319 
  320   /**
  321    * Adds the extras to the join condition.
  322    *
  323    * @param array $arguments
  324    *   Array of query arguments.
  325    * @param string $condition
  326    *   The condition to be built.
  327    * @param array $table
  328    *   The right table.
  329    * @param \Drupal\Core\Database\Query\SelectInterface $select_query
  330    *   The current select query being built.
  331    * @param array $left_table
  332    *   The left table.
  333    */
  334   protected function joinAddExtra(&$arguments, &$condition, $table, SelectInterface $select_query, $left_table = NULL) {
  335     if (is_array($this->extra)) {
  336       $extras = [];
  337       foreach ($this->extra as $info) {
  338         $extras[] = $this->buildExtra($info, $arguments, $table, $select_query, $left_table);
  339       }
  340 
  341       if ($extras) {
  342         if (count($extras) == 1) {
  343           $condition .= ' AND ' . array_shift($extras);
  344         }
  345         else {
  346           $condition .= ' AND (' . implode(' ' . $this->extraOperator . ' ', $extras) . ')';
  347         }
  348       }
  349     }
  350     elseif ($this->extra && is_string($this->extra)) {
  351       $condition .= " AND ($this->extra)";
  352     }
  353   }
  354 
  355   /**
  356    * Builds a single extra condition.
  357    *
  358    * @param array $info
  359    *   The extra information. See JoinPluginBase::$extra for details.
  360    * @param array $arguments
  361    *   Array of query arguments.
  362    * @param array $table
  363    *   The right table.
  364    * @param \Drupal\Core\Database\Query\SelectInterface $select_query
  365    *   The current select query being built.
  366    * @param array $left
  367    *   The left table.
  368    *
  369    * @return string
  370    *   The extra condition
  371    */
  372   protected function buildExtra($info, &$arguments, $table, SelectInterface $select_query, $left) {
  373     // Do not require 'value' to be set; allow for field syntax instead.
  374     $info += [
  375       'value' => NULL,
  376     ];
  377     // Figure out the table name. Remember, only use aliases provided
  378     // if at all possible.
  379     $join_table = '';
  380     if (!array_key_exists('table', $info)) {
  381       $join_table = $table['alias'] . '.';
  382     }
  383     elseif (isset($info['table'])) {
  384       // If we're aware of a table alias for this table, use the table
  385       // alias instead of the table name.
  386       if (isset($left) && $left['table'] == $info['table']) {
  387         $join_table = $left['alias'] . '.';
  388       }
  389       else {
  390         $join_table = $info['table'] . '.';
  391       }
  392     }
  393 
  394     // Convert a single-valued array of values to the single-value case,
  395     // and transform from IN() notation to = notation
  396     if (is_array($info['value']) && count($info['value']) == 1) {
  397       $info['value'] = array_shift($info['value']);
  398     }
  399     if (is_array($info['value'])) {
  400       // We use an SA-CORE-2014-005 conformant placeholder for our array
  401       // of values. Also, note that the 'IN' operator is implicit.
  402       // @see https://www.drupal.org/node/2401615.
  403       $operator = !empty($info['operator']) ? $info['operator'] : 'IN';
  404       $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder() . '[]';
  405       $placeholder_sql = "( $placeholder )";
  406     }
  407     else {
  408       // With a single value, the '=' operator is implicit.
  409       $operator = !empty($info['operator']) ? $info['operator'] : '=';
  410       $placeholder = $placeholder_sql = ':views_join_condition_' . $select_query->nextPlaceholder();
  411     }
  412     // Set 'field' as join table field if available or set 'left field' as
  413     // join table field is not set.
  414     if (isset($info['field'])) {
  415       $join_table_field = "$join_table$info[field]";
  416       // Allow the value to be set either with the 'value' element or
  417       // with 'left_field'.
  418       if (isset($info['left_field'])) {
  419         $placeholder_sql = "$left[alias].$info[left_field]";
  420       }
  421       else {
  422         $arguments[$placeholder] = $info['value'];
  423       }
  424     }
  425     // Set 'left field' as join table field is not set.
  426     else {
  427       $join_table_field = "$left[alias].$info[left_field]";
  428       $arguments[$placeholder] = $info['value'];
  429     }
  430     // Render out the SQL fragment with parameters.
  431     return "$join_table_field $operator $placeholder_sql";
  432   }
  433 
  434 }