Projet

Général

Profil

Paste
Télécharger (18,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / entity / views / handlers / entity_views_field_handler_helper.inc @ 503b3f7b

1
<?php
2

    
3
/**
4
 * @file
5
 * Contains the EntityFieldHandlerHelper class.
6
 */
7

    
8
/**
9
 * Helper class containing static implementations of common field handler methods.
10
 *
11
 * Used by the data selection entity field handlers to avoid code duplication.
12
 *
13
 * @see entity_views_table_definition()
14
 */
15
class EntityFieldHandlerHelper {
16

    
17
  /**
18
   * Provide appropriate default options for a handler.
19
   */
20
  public static function option_definition($handler) {
21
    if (entity_property_list_extract_type($handler->definition['type'])) {
22
      $options['list']['contains']['mode'] = array('default' => 'collapse');
23
      $options['list']['contains']['separator'] = array('default' => ', ');
24
      $options['list']['contains']['type'] = array('default' => 'ul');
25
    }
26
    $options['link_to_entity'] = array('default' => FALSE);
27

    
28
    return $options;
29
  }
30

    
31
  /**
32
   * Provide an appropriate default option form for a handler.
33
   */
34
  public static function options_form($handler, &$form, &$form_state) {
35
    if (entity_property_list_extract_type($handler->definition['type'])) {
36
      $form['list']['mode'] = array(
37
        '#type' => 'select',
38
        '#title' => t('List handling'),
39
        '#options' => array(
40
          'collapse' => t('Concatenate values using a seperator'),
41
          'list' => t('Output values as list'),
42
          'first' => t('Show first (if present)'),
43
          'count' => t('Show item count'),
44
        ),
45
        '#default_value' => $handler->options['list']['mode'],
46
      );
47
      $form['list']['separator'] = array(
48
        '#type' => 'textfield',
49
        '#title' => t('List seperator'),
50
        '#default_value' => $handler->options['list']['separator'],
51
        '#dependency' => array('edit-options-list-mode' => array('collapse')),
52
      );
53
      $form['list']['type'] = array(
54
        '#type' => 'select',
55
        '#title' => t('List type'),
56
        '#options' => array(
57
          'ul' => t('Unordered'),
58
          'ol' => t('Ordered'),
59
        ),
60
        '#default_value' => $handler->options['list']['type'],
61
        '#dependency' => array('edit-options-list-mode' => array('list')),
62
      );
63
    }
64
    $form['link_to_entity'] = array(
65
      '#type' => 'checkbox',
66
      '#title' => t('Link this field to its entity'),
67
      '#description' => t("When using this, you should not set any other link on the field."),
68
      '#default_value' => $handler->options['link_to_entity'],
69
    );
70
  }
71

    
72
  /**
73
   * Add the field for the entity ID (if necessary).
74
   */
75
  public static function query($handler) {
76
    // Copied over from views_handler_field_entity::query().
77
    // Sets table_alias (entity table), base_field (entity id field) and
78
    // field_alias (the field's alias).
79
    $handler->table_alias = $base_table = $handler->view->base_table;
80
    $handler->base_field = $handler->view->base_field;
81

    
82
    if (!empty($handler->relationship)) {
83
      foreach ($handler->view->relationship as $relationship) {
84
        if ($relationship->alias == $handler->relationship) {
85
          $base_table = $relationship->definition['base'];
86
          $handler->table_alias = $relationship->alias;
87

    
88
          $table_data = views_fetch_data($base_table);
89
          $handler->base_field = empty($relationship->definition['base field']) ? $table_data['table']['base']['field'] : $relationship->definition['base field'];
90
        }
91
      }
92
    }
93

    
94
    // Add the field if the query back-end implements an add_field() method,
95
    // just like the default back-end.
96
    if (method_exists($handler->query, 'add_field')) {
97
      $handler->field_alias = $handler->query->add_field($handler->table_alias, $handler->base_field, '');
98
    }
99
    else {
100
      // To ensure there is an alias just set the field alias to the real field.
101
      $handler->field_alias = $handler->real_field;
102
    }
103
  }
104

    
105
  /**
106
   * Extracts the innermost field name from a data selector.
107
   *
108
   * @param $selector
109
   *   The data selector.
110
   *
111
   * @return
112
   *   The last component of the data selector.
113
   */
114
  public static function get_selector_field_name($selector) {
115
    return ltrim(substr($selector, strrpos($selector, ':')), ':');
116
  }
117

    
118
  /**
119
   * Adds a click-sort to the query.
120
   *
121
   * @param $order
122
   *   Either 'ASC' or 'DESC'.
123
   */
124
  public static function click_sort($handler, $order) {
125
    // The normal orderby() method for this usually won't work here. So we need
126
    // query plugins to provide their own method for this.
127
    if (method_exists($handler->query, 'add_selector_orderby')) {
128
      $selector = self::construct_property_selector($handler, TRUE);
129
      $handler->query->add_selector_orderby($selector, $order);
130
    }
131
  }
132

    
133
  /**
134
   * Load the entities for all rows that are about to be displayed.
135
   *
136
   * Automatically takes care of relationships, including data selection
137
   * relationships. Results are written into @code $handler->wrappers @endcode
138
   * and @code $handler->entity_type @endcode is set.
139
   */
140
  public static function pre_render($handler, &$values, $load_always = FALSE) {
141
    if (empty($values)) {
142
      return;
143
    }
144
    if (!$load_always && empty($handler->options['link_to_entity'])) {
145
      // Check whether we even need to load the entities.
146
      $selector = self::construct_property_selector($handler, TRUE);
147
      $load = FALSE;
148
      foreach ($values as $row) {
149
        if (empty($row->_entity_properties) || !array_key_exists($selector, $row->_entity_properties)) {
150
          $load = TRUE;
151
          break;
152
        }
153
      }
154
      if (!$load) {
155
        return;
156
      }
157
    }
158

    
159
    if (method_exists($handler->query, 'get_result_wrappers')) {
160
      list($handler->entity_type, $handler->wrappers) = $handler->query->get_result_wrappers($values, $handler->relationship, $handler->real_field);
161
    }
162
    else {
163
      list($handler->entity_type, $entities) = $handler->query->get_result_entities($values, $handler->relationship, $handler->real_field);
164
      $handler->wrappers = array();
165
      foreach ($entities as $id => $entity) {
166
        $handler->wrappers[$id] = entity_metadata_wrapper($handler->entity_type, $entity);
167
      }
168
    }
169
  }
170

    
171
  /**
172
   * Return an Entity API data selector for the given handler's relationship.
173
   *
174
   * A data selector is a concatenation of properties which should be followed
175
   * to arrive at a desired property that may be nested in related entities or
176
   * structures. The separate properties are herein concatenated with colons.
177
   *
178
   * For instance, a data selector of "author:roles" would mean to first
179
   * access the "author" property of the given wrapper, and then for this new
180
   * wrapper to access and return the "roles" property.
181
   *
182
   * Lists of entities are handled automatically by always returning only the
183
   * first entity.
184
   *
185
   * @param $handler
186
   *   The handler for which to construct the selector.
187
   * @param $complete
188
   *   If TRUE, the complete selector for the field is returned, not just the
189
   *   one for its parent. Defaults to FALSE.
190
   *
191
   * @return
192
   *   An Entity API data selector for the given handler's relationship.
193
   */
194
  public static function construct_property_selector($handler, $complete = FALSE) {
195
    $return = '';
196
    if ($handler->relationship) {
197
      $current_handler = $handler;
198
      $view = $current_handler->view;
199
      $relationships = array();
200
      // Collect all relationships, keyed by alias.
201
      foreach ($view->relationship as $key => $relationship) {
202
        $key = $relationship->alias ? $relationship->alias : $key;
203
        $relationships[$key] = $relationship;
204
      }
205
      while (!empty($current_handler->relationship) && !empty($relationships[$current_handler->relationship])) {
206
        $current_handler = $relationships[$current_handler->relationship];
207
        $return = $current_handler->real_field . ($return ? ":$return" : '');
208
      }
209
    }
210

    
211
    if ($complete) {
212
      $return .= ($return ? ':' : '') . $handler->real_field;
213
    }
214
    elseif ($pos = strrpos($handler->real_field, ':')) {
215
      // If we have a selector as the real_field, append this to the returned
216
      // relationship selector.
217
      $return .= ($return ? ':' : '') . substr($handler->real_field, 0, $pos);
218
    }
219

    
220
    return $return;
221
  }
222

    
223
  /**
224
   * Extracts data from several metadata wrappers based on a data selector.
225
   *
226
   * All metadata wrappers passed to this function have to be based on the exact
227
   * same property information. The data will be returned wrapped by one or more
228
   * metadata wrappers.
229
   *
230
   * Can be used in query plugins for the get_result_entities() and
231
   * get_result_wrappers() methods.
232
   *
233
   * @param array $wrappers
234
   *   The EntityMetadataWrapper objects from which to extract data.
235
   * @param $selector
236
   *   The selector specifying the data to extract.
237
   *
238
   * @return array
239
   *   An array with numeric indices, containing the type of the extracted
240
   *   wrappers in the first element. The second element of the array contains
241
   *   the extracted property value(s) for each wrapper, keyed to the same key
242
   *   that was used for the respecive wrapper in $wrappers. All extracted
243
   *   properties are returned as metadata wrappers.
244
   */
245
  public static function extract_property_multiple(array $wrappers, $selector) {
246
    $parts = explode(':', $selector, 2);
247
    $name = $parts[0];
248

    
249
    $results = array();
250
    $entities = array();
251
    $type = '';
252
    foreach ($wrappers as $i => $wrapper) {
253
      try {
254
        $property = $wrapper->$name;
255
        $type = $property->type();
256
        if ($property instanceof EntityDrupalWrapper) {
257
          // Remember the entity IDs to later load all at once (so as to
258
          // properly utilize multiple load functionality).
259
          $id = $property->getIdentifier();
260
          // Only accept valid ids. $id can be FALSE for entity values that are
261
          // NULL.
262
          if ($id) {
263
            $entities[$type][$i] = $id;
264
          }
265
        }
266
        elseif ($property instanceof EntityStructureWrapper) {
267
          $results[$i] = $property;
268
        }
269
        elseif ($property instanceof EntityListWrapper) {
270
          foreach ($property as $item) {
271
            $results[$i] = $item;
272
            $type = $item->type();
273
            break;
274
          }
275
        }
276
        // Do nothing in case it cannot be applied.
277
      }
278
      catch (EntityMetadataWrapperException $e) {
279
        // Skip single empty properties.
280
      }
281
    }
282

    
283
    if ($entities) {
284
      // Map back the loaded entities back to the results array.
285
      foreach ($entities as $type => $id_map) {
286
        $loaded = entity_load($type, $id_map);
287
        foreach ($id_map as $i => $id) {
288
          if (isset($loaded[$id])) {
289
            $results[$i] = entity_metadata_wrapper($type, $loaded[$id]);
290
          }
291
        }
292
      }
293
    }
294

    
295
    // If there are no further parts in the selector, we are done now.
296
    if (empty($parts[1])) {
297
      return array($type, $results);
298
    }
299
    return self::extract_property_multiple($results, $parts[1]);
300
  }
301

    
302
  /**
303
   * Get the value of a certain data selector.
304
   *
305
   * Uses $values->_entity_properties to look for already extracted properties.
306
   *
307
   * @param $handler
308
   *   The field handler for which to return a value.
309
   * @param $values
310
   *   The values for the current row retrieved from the Views query, as an
311
   *   object.
312
   * @param $field
313
   *   The field to extract. If no value is given, the field of the given
314
   *   handler is used instead. The special "entity object" value can be used to
315
   *   get the base entity instead of a special field.
316
   * @param $default
317
   *   The value to return if the entity or field are not present.
318
   */
319
  public static function get_value($handler, $values, $field = NULL, $default = NULL) {
320
    // There is a value cache on each handler so parent handlers rendering a
321
    // single field value from a list will get the single value, not the whole
322
    // list.
323
    if (!isset($field) && isset($handler->current_value)) {
324
      return $handler->current_value;
325
    }
326
    $field = isset($field) ? $field : self::get_selector_field_name($handler->real_field);
327
    $selector = self::construct_property_selector($handler);
328
    $selector = $selector ? "$selector:$field" : $field;
329
    if (!isset($values->_entity_properties)) {
330
      $values->_entity_properties = array();
331
    }
332
    if (!array_key_exists($selector, $values->_entity_properties)) {
333
      if (!isset($handler->wrappers[$handler->view->row_index])) {
334
        $values->_entity_properties[$selector] = $default;
335
      }
336
      elseif (is_array($handler->wrappers[$handler->view->row_index])) {
337
        $values->_entity_properties[$selector] = self::extract_list_wrapper_values($handler->wrappers[$handler->view->row_index], $field);
338
      }
339
      else {
340
        $wrapper = $handler->wrappers[$handler->view->row_index];
341
        try {
342
          if ($field === 'entity object') {
343
            $values->_entity_properties[$selector] = $wrapper->value();
344
          }
345
          else {
346
            $values->_entity_properties[$selector] = isset($wrapper->$field) ? $wrapper->$field->value(array('identifier' => TRUE, 'sanitize' => TRUE)) : $default;
347
          }
348
        }
349
        catch (EntityMetadataWrapperException $e) {
350
          $values->_entity_properties[$selector] = $default;
351
        }
352
      }
353
    }
354
    return $values->_entity_properties[$selector];
355
  }
356

    
357
  /**
358
   * Helper method for extracting the values from an array of wrappers.
359
   *
360
   * Nested arrays of wrappers are also handled, the values are returned in a
361
   * flat (not nested) array.
362
   */
363
  public static function extract_list_wrapper_values(array $wrappers, $field) {
364
    $return = array();
365
    foreach ($wrappers as $wrapper) {
366
      if (is_array($wrapper)) {
367
        $values = self::extract_list_wrapper_values($wrapper, $field);
368
        if ($values) {
369
          $return = array_merge($return, $values);
370
        }
371
      }
372
      else {
373
        try {
374
          if ($field == 'entity object') {
375
            $return[] = $wrapper->value();
376
          }
377
          elseif (isset($wrapper->$field)) {
378
            $return[] = $wrapper->$field->value(array('identifier' => TRUE));
379
          }
380
        }
381
        catch (EntityMetadataWrapperException $e) {
382
          // An exception probably signifies a non-present property, so we just
383
          // ignore it.
384
        }
385
      }
386
    }
387
    return $return;
388
  }
389

    
390
  /**
391
   * Render the field.
392
   *
393
   * Implements the entity link functionality and list handling. Basic handling
394
   * of the single values is delegated back to the field handler.
395
   *
396
   * @param $handler
397
   *   The field handler whose field should be rendered.
398
   * @param $values
399
   *   The values for the current row retrieved from the Views query, as an
400
   *   object.
401
   *
402
   * @return
403
   *   The rendered value for the field.
404
   */
405
  public static function render($handler, $values) {
406
    $value = $handler->get_value($values);
407
    if (is_array($value)) {
408
      return self::render_list($handler, $value, $values);
409
    }
410
    return self::render_entity_link($handler, $value, $values);
411
  }
412

    
413
  /**
414
   * Render a list of values.
415
   *
416
   * @param $handler
417
   *   The field handler whose field is rendered.
418
   * @param $list
419
   *   The list of values to render.
420
   * @param $values
421
   *   The values for the current row retrieved from the Views query, as an
422
   *   object.
423
   *
424
   * @return
425
   *   The rendered value for the given list.
426
   */
427
  public static function render_list($handler, $list, $values) {
428
    // Allow easy overriding of this behaviour in the specific field handler.
429
    if (method_exists($handler, 'render_list')) {
430
      return $handler->render_list($list, $values);
431
    }
432
    $mode = isset($handler->options['list']['mode']) ? $handler->options['list']['mode'] : NULL;
433
    switch ($mode) {
434
      case 'first':
435
        $list = count($list) ? array_shift($list) : NULL;
436
        if (is_array($list)) {
437
          return self::render_list($handler, $list, $values);
438
        }
439
        elseif (isset($list)) {
440
          return self::render_entity_link($handler, $list, $values);
441
        }
442
        return NULL;
443

    
444
      case 'count':
445
        return count($list);
446

    
447
      // Handles both collapse and list output. Fallback is to collapse.
448
      default:
449
        $inner_values = array();
450
        foreach ($list as $value) {
451
          $value = is_array($value) ? self::render_list($handler, $value, $values) : self::render_entity_link($handler, $value, $values);
452
          if ($value) {
453
            $inner_values[] = $value;
454
          }
455
        }
456

    
457
        // Format output as list.
458
        if ($mode == 'list') {
459
          $type = isset($handler->options['list']['type']) ? $handler->options['list']['type'] : 'ul';
460
          return theme('item_list', array(
461
            'items' => $inner_values,
462
            'type' => $type,
463
          ));
464
        }
465

    
466
        $separator = isset($handler->options['list']['separator']) ? $handler->options['list']['separator'] : ', ';
467
        return implode($separator, $inner_values);
468
    }
469
  }
470

    
471
  /**
472
   * Render a single value as a link to the entity if applicable.
473
   *
474
   * @param $handler
475
   *   The field handler whose field is rendered.
476
   * @param $value
477
   *   The single value to render.
478
   * @param $values
479
   *   The values for the current row retrieved from the Views query, as an
480
   *   object.
481
   *
482
   * @return
483
   *   The rendered value.
484
   */
485
  public static function render_entity_link($handler, $value, $values) {
486
    // Allow easy overriding of this behaviour in the specific field handler.
487
    if (method_exists($handler, 'render_entity_link')) {
488
      return $handler->render_entity_link($value, $values);
489
    }
490
    $render = self::render_single_value($handler, $value, $values);
491
    if (!$handler->options['link_to_entity']) {
492
      return $render;
493
    }
494
    $entity = $handler->get_value($values, 'entity object');
495
    if (is_object($entity) && ($url = entity_uri($handler->entity_type, $entity))) {
496
      return l($render, $url['path'], array('html' => TRUE) + $url['options']);
497
    }
498
    return $render;
499
  }
500

    
501
  /**
502
   * Render a single value.
503
   *
504
   * @param $handler
505
   *   The field handler whose field is rendered.
506
   * @param $value
507
   *   The single value to render.
508
   * @param $values
509
   *   The values for the current row retrieved from the Views query, as an
510
   *   object.
511
   *
512
   * @return
513
   *   The rendered value.
514
   */
515
  public static function render_single_value($handler, $value, $values) {
516
    // Try to use the method in the specific field handler.
517
    if (method_exists($handler, 'render_single_value')) {
518
      $handler->current_value = $value;
519
      $return = $handler->render_single_value($value, $values);
520
      unset($handler->current_value);
521
      return $return;
522
    }
523
    // Default fallback in case the field handler doesn't provide the method.
524
    return is_scalar($value) ? check_plain($value) : nl2br(check_plain(print_r($value, TRUE)));
525
  }
526

    
527
}