View source
<?php
namespace Drupal\views_simple_math_field\Plugin\views\field;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\field\NumericField;
use Drupal\views\ResultRow;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Component\Render\FormattableMarkup;
use Tramasec\Util\EvalMath\EvalMath;
use Drupal\Core\Url;
use Tramasec\Util\EvalMath\Exception\DivisionByZeroException;
class SimpleMathField extends NumericField implements ContainerFactoryPluginInterface {
protected $entityTypeManager;
protected $commerce_price_fields = [
'commerce_price_default',
'commerce_product_variation',
'commerce_price_plain',
'commerce_price_calculated',
'commerce_order_total_summary',
];
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManager $entityTypeManager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entityTypeManager;
}
public function query() {
}
protected function defineOptions() {
$options = parent::defineOptions();
$this->field_alias = 'field_views_simple_math_field';
$options['fieldset_one']['default'] = NULL;
$options['fieldset_one']['data_fields'] = [
'default' => NULL,
];
$options['fieldset_one']['formula'] = [
'default' => NULL,
];
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$fieldDelta = preg_replace('[\\D]', '', $this->options['id']);
$fieldList = $this->displayHandler
->getFieldLabels();
foreach ($fieldList as $key => $value) {
if ($this->field_alias === $key && $fieldDelta < preg_replace('[\\D]', '', $key)) {
unset($fieldList[$key]);
}
else {
$fieldList[$key] .= new FormattableMarkup(". Formula token: @%field", [
"%field" => $key,
]);
}
}
unset($fieldList[$this->options['id']]);
$form['fieldset_one'] = [
'#type' => 'fieldset',
'#title' => t('Select the fields to use in the formula.'),
'#collapsible' => FALSE,
'#collapsed' => FALSE,
'#weight' => -10,
'#required' => TRUE,
];
$form['fieldset_one']['data_field'] = [
'#type' => 'checkboxes',
'#title' => t('Data Fields'),
'#options' => $fieldList,
'#default_value' => $this->options['fieldset_one']['data_field'],
'#weight' => -10,
];
$form['fieldset_one']['formula'] = [
'#type' => 'textarea',
'#title' => t('Formula'),
'#default_value' => $this->options['fieldset_one']['formula'],
'#weight' => -8,
'#description' => t('Enter the formula to give this field its value. You can use any fields specified in the checkboxes above, using the formula token listed beside the field name. It uses the EvalMath library, refer to this web to see math expressions: <a href=":url">project in github</a>.', [
':url' => Url::fromUri('https://github.com/Tramasec/eval-math')
->toString(),
]),
];
return $form;
}
protected function getFieldType($field) {
$field_handler = $this->displayHandler
->getHandler('field', $field)->options;
if (!empty($field_handler['type'])) {
$field_type = $field_handler['type'];
}
else {
$field_type = 'undefined';
}
return $field_type;
}
protected function getFieldPlugin($field) {
$field_handler = $this->displayHandler
->getHandler('field', $field)->options;
if (!empty($field_handler['plugin_id'])) {
$field_plugin = $field_handler['plugin_id'];
}
else {
$field_plugin = 'undefined';
}
return $field_plugin;
}
protected function getFieldRelationship($field) {
$field_handler = $this->displayHandler
->getHandler('field', $field)->options;
if (!empty($field_handler['relationship']) && $field_handler['relationship'] !== 'none') {
$relationship = $field_handler['relationship'];
}
else {
$relationship = NULL;
}
return $relationship;
}
protected function getRewriteStatus($field) {
$field_handler = $this->displayHandler
->getHandler('field', $field)->options;
if (isset($field_handler['alter']['alter_text']) && !empty($field_handler['alter']['text'])) {
$alter = $field_handler['alter']['text'];
}
else {
$alter = NULL;
}
return $alter;
}
protected function getRelationshipEntity($values, $field, $relationship) {
$relationship_entity = NULL;
$relationship_entity_type = $this->displayHandler
->getHandler('field', $field)
->getEntityType();
$relationship_entities = $values->_relationship_entities;
if (isset($relationship_entities[$relationship])) {
$entity_id = $relationship_entities[$relationship]
->id();
$relationship_entity = $this->entityTypeManager
->getStorage($relationship_entity_type)
->load($entity_id);
}
return $relationship_entity;
}
protected function removeSeparator($field, $data) {
if (!empty($this->view->field[$field]->options['separator'])) {
$separator = $this->view->field[$field]->options['separator'];
}
if (!empty($separator)) {
if (strpos($data, $separator)) {
$data = str_replace($separator, '', $data);
}
}
else {
if (strpos($data, ',')) {
$data = str_replace(',', '', $data);
}
if (strpos($data, ' ')) {
$data = str_replace(' ', '', $data);
}
}
return $data;
}
protected function getFieldValue(ResultRow $values, $entity, $field) {
$field_type = $this
->getFieldType($field);
$field_plugin = $this
->getFieldPlugin($field);
$rewritten = $this
->getRewriteStatus($field);
$data = NULL;
if ($field_plugin === 'entity_form_field') {
$field_handler = $this->displayHandler
->getHandler('field', $field)->options;
if (!empty($field_handler['plugin']['type'])) {
$field_type = $field_handler['plugin']['type'];
}
if (!empty($field_handler['field'])) {
$form_field = $field_handler['field'];
$prefix = 'form_field_';
if (0 === strpos($form_field, $prefix)) {
$field = substr($form_field, strlen($prefix));
}
}
$relationship = $this
->getFieldRelationship($field);
if ($relationship) {
$entity = $this
->getRelationshipEntity($values, $field, $relationship);
}
if (in_array($field_type, $this->commerce_price_fields)) {
$commerce_field_id = $this->displayHandler
->getHandler('field', 'form_field_' . $field)->options['id'];
if ($entity
->hasField($commerce_field_id) && !empty($entity
->get($commerce_field_id)
->getValue())) {
$data = $entity
->get($commerce_field_id)
->first()
->toPrice();
}
}
else {
$data = $entity
->get($field)
->getValue()[0]['value'];
}
return $data;
}
if ($field_type !== 'undefined') {
$relationship = $this
->getFieldRelationship($field);
if ($relationship) {
$entity = $this
->getRelationshipEntity($values, $field, $relationship);
}
if (isset($entity) && $rewritten) {
if (is_numeric($rewritten) == TRUE) {
$data = $rewritten;
}
else {
if (\Drupal::routeMatch()
->getRouteName() == 'entity.view.preview_form') {
\Drupal::service('messenger')
->addMessage(t('It appears that <em>@field</em> is rewritten and requires advanced rendering. Do not use the Views Simple Math Field sort handler for this View.', [
'@field' => $field,
]), 'warning');
}
$data = $this->view->field[$field]
->advancedRender($values);
}
}
if (isset($entity) && !$rewritten) {
$data = $this->view->field[$field]
->getValue($values);
if (in_array($field_type, $this->commerce_price_fields)) {
$commerce_field_id = $this->displayHandler
->getHandler('field', $field)->options['id'];
if ($entity
->hasField($commerce_field_id) && !empty($entity
->get($commerce_field_id)
->getValue())) {
$data = $entity
->get($commerce_field_id)
->first()
->toPrice();
}
}
}
}
else {
if (isset($this->view->field[$field]->original_value)) {
$data = $this->view->field[$field]->original_value;
}
else {
$data = $this->view->field[$field]
->getValue($values);
}
if ($rewritten) {
if (\Drupal::routeMatch()
->getRouteName() == 'entity.view.preview_form') {
\Drupal::service('messenger')
->addMessage(t('Views Simple Math Field sometimes has difficulty rendering the correct value for rewritten fields. You may want to double check that field ID <em>@field</em> is properly outputting a value.', [
'@field' => $field,
]), 'warning');
}
$data = $this->displayHandler
->getHandler('field', $field)
->advancedRender($values);
}
}
if (!isset($data)) {
$data = 0;
}
$data = $this
->removeSeparator($field, $data);
return $data;
}
public function getValue(ResultRow $values, $field = NULL) {
parent::getValue($values, $field);
$entity = $this
->getEntity($values);
$fields_in_formula = [];
foreach ($this->options['fieldset_one']['data_field'] as $key => $value) {
if ($value) {
$raw_field_in_formula = $this
->getFieldValue($values, $entity, $key);
if (preg_match('/^.*?([\\d]+(?:\\.[\\d]+)?).*?$/', $raw_field_in_formula, $match)) {
$raw_field_in_formula_sane = floatval($match[0]);
$fields_in_formula['@' . $key] = filter_var($raw_field_in_formula_sane, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
}
else {
$fields_in_formula['@' . $key] = 0;
}
}
}
$formula = new FormattableMarkup($this->options['fieldset_one']['formula'], $fields_in_formula);
$m = new EvalMath();
try {
$result = $m
->evaluate($formula);
} catch (DivisionByZeroException $e) {
\Drupal::logger('views_simple_math_field')
->error('DivisionByZeroException');
}
return $result ?? NULL;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('entity_type.manager'));
}
}