class Products in Ubercart 8.4
Manage the products an order contains.
Plugin annotation
@UbercartOrderPane(
id = "products",
title = @Translation("Products"),
weight = 5,
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\uc_order\OrderPanePluginBase implements OrderPanePluginInterface
- class \Drupal\uc_order\EditableOrderPanePluginBase implements EditableOrderPanePluginInterface
- class \Drupal\uc_order\Plugin\Ubercart\OrderPane\Products
- class \Drupal\uc_order\EditableOrderPanePluginBase implements EditableOrderPanePluginInterface
- class \Drupal\uc_order\OrderPanePluginBase implements OrderPanePluginInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Products
13 string references to 'Products'
- NewShipmentForm::buildForm in shipping/
uc_fulfillment/ src/ Form/ NewShipmentForm.php - Form constructor.
- OrderViewsData::getViewsData in uc_order/
src/ OrderViewsData.php - Returns views data for the entity type.
- PackageController::listOrderPackages in shipping/
uc_fulfillment/ src/ Controller/ PackageController.php - Displays a list of an order's packaged products.
- Reports::customers in uc_report/
src/ Controller/ Reports.php - Displays the customer report.
- theme_uc_cart_review_table in uc_cart/
uc_cart.theme.inc - Formats the cart contents table on the checkout page.
File
- uc_order/
src/ Plugin/ Ubercart/ OrderPane/ Products.php, line 24
Namespace
Drupal\uc_order\Plugin\Ubercart\OrderPaneView source
class Products extends EditableOrderPanePluginBase {
/**
* {@inheritdoc}
*/
public function view(OrderInterface $order, $view_mode) {
$build = [
'#type' => 'table',
'#attributes' => [
'class' => [
'order-pane-table',
],
],
'#header' => [
'qty' => [
'data' => $this
->t('Qty'),
'class' => [
'qty',
],
],
'product' => [
'data' => $this
->t('Product'),
'class' => [
'product',
],
],
'model' => [
'data' => $this
->t('SKU'),
'class' => [
'sku',
RESPONSIVE_PRIORITY_LOW,
],
],
'cost' => [
'data' => $this
->t('Cost'),
'class' => [
'cost',
RESPONSIVE_PRIORITY_LOW,
],
],
'price' => [
'data' => $this
->t('Price'),
'class' => [
'price',
],
],
'total' => [
'data' => $this
->t('Total'),
'class' => [
'price',
],
],
],
'#empty' => $this
->t('This order contains no products.'),
];
$account = \Drupal::currentUser();
if (!$account
->hasPermission('administer products')) {
unset($build['#header']['cost']);
}
// @todo Replace with Views.
$rows = [];
foreach ($order->products as $id => $product) {
$rows[$id]['qty'] = [
'data' => [
'#theme' => 'uc_qty',
'#qty' => $product->qty->value,
],
'class' => [
'qty',
],
];
if ($product->nid->entity && $product->nid->entity
->access('view')) {
$title = $product->nid->entity
->toLink()
->toString();
}
else {
$title = $product->title->value;
}
$rows[$id]['product'] = [
'data' => [
'#markup' => $title . uc_product_get_description($product),
],
'class' => [
'product',
],
];
$rows[$id]['model'] = [
'data' => [
'#markup' => $product->model->value,
],
'class' => [
'sku',
],
];
if ($account
->hasPermission('administer products')) {
$rows[$id]['cost'] = [
'data' => [
'#theme' => 'uc_price',
'#price' => $product->cost->value,
],
'class' => [
'cost',
],
];
}
$rows[$id]['price'] = [
'data' => [
'#theme' => 'uc_price',
'#price' => $product->price->value,
'#suffixes' => [],
],
'class' => [
'price',
],
];
$rows[$id]['total'] = [
'data' => [
'#theme' => 'uc_price',
'#price' => $product->price->value * $product->qty->value,
'#suffixes' => [],
],
'class' => [
'total',
],
];
}
$build['#rows'] = $rows;
return $build;
}
/**
* {@inheritdoc}
*/
public function buildForm(OrderInterface $order, array $form, FormStateInterface $form_state) {
$form['add_product_button'] = [
'#type' => 'submit',
'#value' => $this
->t('Add product'),
'#submit' => [
[
$this,
'productSelectSearch',
],
],
'#ajax' => [
'callback' => [
$this,
'ajaxCallback',
],
'wrapper' => 'product-controls',
],
];
$form['add_blank_line_button'] = [
'#type' => 'submit',
'#value' => $this
->t('Add blank line'),
'#submit' => [
[
$this,
'addBlank',
],
],
'#ajax' => [
'callback' => [
$this,
'ajaxCallback',
],
'wrapper' => 'product-controls',
],
];
$form['product_controls'] = [
'#tree' => TRUE,
'#prefix' => '<div id="product-controls">',
'#suffix' => '</div>',
];
$controls = [];
if ($form_state
->has('products_action')) {
switch ($form_state
->get('products_action')) {
case 'select':
$controls = $this
->productSelectForm($form['product_controls'], $form_state, $order);
break;
case 'add_product':
$controls = $this
->addProductForm($form['product_controls'], $form_state, $order, $form_state
->get('node'));
break;
}
}
$form['product_controls'] += $controls;
$form += $this
->editProductsForm($form, $form_state, $order->products);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(OrderInterface $order, array &$form, FormStateInterface $form_state) {
// @todo Decouple stock related code into uc_stock
if (\Drupal::moduleHandler()
->moduleExists('uc_stock')) {
$qtys = [];
foreach ($order->products as $product) {
$qtys[$product
->id()] = intval($product->qty->value);
}
}
if (is_array($form_state
->getValue('products'))) {
foreach ($form_state
->getValue('products') as $product) {
if (isset($order->products[$product['order_product_id']])) {
foreach ([
'qty',
'title',
'model',
'cost',
'price',
] as $field) {
if (!empty($product[$field])) {
$order->products[$product['order_product_id']]->{$field}->value = $product[$field];
}
}
// Weight is stored as an array, so we should handle it another way.
if (!empty($product['weight']) && !empty($product['weight_units'])) {
$weight = [
'value' => $product['weight'],
'units' => $product['weight_units'],
];
$order->products[$product['order_product_id']]->weight
->setValue($weight);
}
if (\Drupal::moduleHandler()
->moduleExists('uc_stock')) {
$temp = $product['qty'];
$order->products[$product['order_product_id']]->qty->value = $product['qty'] - $qtys[$product['order_product_id']];
uc_stock_adjust_product_stock($order->products[$product['order_product_id']], 0, $order);
$order->products[$product['order_product_id']]->qty->value = $temp;
}
}
}
}
}
/**
* Form to choose a product to add to the order.
*
* @ingroup forms
*/
protected function productSelectForm($form, FormStateInterface $form_state, $order) {
$options = $form_state
->get('product_select_options');
$ajax = [
'callback' => [
$this,
'ajaxCallback',
],
'wrapper' => 'product-controls',
];
$form['nid'] = [
'#type' => 'select',
'#options' => $options,
'#size' => 7,
'#ajax' => $ajax + [
'event' => 'dblclick',
'trigger_as' => [
'name' => 'op',
'value' => $this
->t('Select'),
],
],
];
$form['product_search'] = [
'#type' => 'textfield',
'#title' => $this
->t('Search by name or model/SKU (* is the wildcard)'),
];
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['select'] = [
'#type' => 'submit',
'#value' => $this
->t('Select'),
'#validate' => [
[
$this,
'productSelectValidate',
],
],
'#submit' => [
[
$this,
'productSelectSubmit',
],
],
'#ajax' => $ajax,
'#weight' => 0,
];
$form['actions']['search'] = [
'#type' => 'submit',
'#value' => $this
->t('Search'),
'#submit' => [
[
$this,
'productSelectSearch',
],
],
'#ajax' => $ajax,
'#weight' => 1,
];
$form['actions']['close'] = [
'#type' => 'submit',
'#value' => $this
->t('Close'),
'#submit' => [
[
$this,
'productSelectClose',
],
],
'#ajax' => $ajax,
'#weight' => 2,
];
return $form;
}
/**
* Validation handler for self::productSelectForm().
*/
public function productSelectValidate($form, FormStateInterface $form_state) {
if ($form_state
->isValueEmpty([
'product_controls',
'nid',
])) {
$form_state
->setErrorByName('product_controls][nid', $this
->t('Please select a product.'));
}
}
/**
* Sets the quantity and attributes of a product added to the order.
*/
protected function addProductForm($form, FormStateInterface $form_state, $order, $node) {
$data = [];
if ($form_state
->hasValue([
'product_controls',
'qty',
])) {
$data += \Drupal::moduleHandler()
->invokeAll('uc_add_to_cart_data', [
$form_state
->getValue('product_controls'),
]);
}
if (!empty($node->data) && is_array($node->data)) {
$data += $node->data;
}
$node = uc_product_load_variant(intval($form_state
->getValue([
'product_controls',
'nid',
])), $data);
$form['title'] = [
'#markup' => '<h3>' . $node
->label() . '</h3>',
];
$form['nid'] = [
'#type' => 'hidden',
'#value' => $node
->id(),
];
$form['qty'] = [
'#type' => 'uc_quantity',
'#title' => $this
->t('Quantity'),
'#default_value' => 1,
];
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this
->t('Add to order'),
'#submit' => [
[
$this,
'addProductSubmit',
],
],
'#ajax' => [
'callback' => [
$this,
'ajaxCallback',
],
'wrapper' => 'product-controls',
],
];
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => $this
->t('Cancel'),
'#submit' => [
[
$this,
'productSelectSearch',
],
],
'#ajax' => [
'callback' => [
$this,
'ajaxCallback',
],
'wrapper' => 'product-controls',
],
'#limit_validation_errors' => [],
];
$form['node'] = [
'#type' => 'value',
'#value' => $node,
];
uc_form_alter($form, $form_state, __FUNCTION__);
return $form;
}
/**
* Form to allow ordered products' data to be changed.
*/
protected function editProductsForm($form, FormStateInterface $form_state, $products) {
$form['products'] = [
'#type' => 'table',
'#tree' => TRUE,
'#header' => [
$this
->t('Remove'),
$this
->t('Quantity'),
$this
->t('Name'),
$this
->t('SKU'),
$this
->t('Weight'),
$this
->t('Units'),
$this
->t('Cost'),
$this
->t('Price'),
],
'#attributes' => [
'id' => 'order-edit-products',
'class' => [
'order-pane-table',
],
],
'#empty' => $this
->t('This order contains no products.'),
];
$form['data'] = [
'#tree' => TRUE,
'#parents' => [
'products',
],
];
foreach ($products as $i => $product) {
$form['products'][$i]['remove'] = [
'#type' => 'image_button',
'#title' => $this
->t('Remove this product.'),
'#name' => "products[{$i}][remove]",
'#src' => drupal_get_path('module', 'uc_store') . '/images/error.gif',
'#button_type' => 'remove',
'#submit' => [
[
$this,
'removeProduct',
],
'::submitForm',
],
'#return_value' => $product->order_product_id->value,
];
$form['data'][$i]['order_product_id'] = [
'#type' => 'hidden',
'#value' => $product->order_product_id->value,
];
$form['data'][$i]['nid'] = [
'#type' => 'hidden',
'#value' => $product->nid->target_id,
];
$form['products'][$i]['qty'] = [
'#type' => 'uc_quantity',
'#title' => $this
->t('Quantity'),
'#title_display' => 'invisible',
'#default_value' => $product->qty->value,
];
$form['products'][$i]['title'] = [
'#type' => 'textfield',
'#title' => $this
->t('Title'),
'#title_display' => 'invisible',
'#default_value' => $product->title->value,
'#size' => 32,
'#maxlength' => 255,
];
$form['products'][$i]['model'] = [
'#type' => 'textfield',
'#title' => $this
->t('SKU'),
'#title_display' => 'invisible',
'#default_value' => $product->model->value,
'#size' => 6,
];
$form['products'][$i]['weight'] = [
'#type' => 'textfield',
'#title' => $this
->t('Weight'),
'#title_display' => 'invisible',
'#default_value' => $product->weight->value,
'#size' => 3,
];
$units = [
'lb' => $this
->t('Pounds'),
'kg' => $this
->t('Kilograms'),
'oz' => $this
->t('Ounces'),
'g' => $this
->t('Grams'),
];
$form['products'][$i]['weight_units'] = [
'#type' => 'select',
'#title' => $this
->t('Units'),
'#title_display' => 'invisible',
'#default_value' => $product->weight->units,
'#options' => $units,
];
$form['products'][$i]['cost'] = [
'#type' => 'uc_price',
'#title' => $this
->t('Cost'),
'#title_display' => 'invisible',
'#default_value' => $product->cost->value,
'#size' => 5,
];
$form['products'][$i]['price'] = [
'#type' => 'uc_price',
'#title' => $this
->t('Price'),
'#title_display' => 'invisible',
'#default_value' => $product->price->value,
'#size' => 5,
];
$form['data'][$i]['data'] = [
'#type' => 'hidden',
'#value' => serialize($product->data
->first() ? $product->data
->first()
->toArray() : NULL),
];
}
return $form;
}
/**
* Sets the order pane to show the product selection form.
*/
public function productSelectSearch($form, FormStateInterface $form_state) {
$types = uc_product_types();
$options = [];
$query = \Drupal::database()
->select('node_field_data', 'n')
->fields('n', [
'nid',
'title',
])
->condition('n.type', $types, 'IN')
->orderBy('n.title')
->addTag('node_access');
if (!$form_state
->isValueEmpty([
'product_controls',
'product_search',
])) {
$search = strtolower(str_replace('*', '%', $form_state
->getValue([
'product_controls',
'product_search',
])));
$query
->leftJoin('uc_products', 'p', 'n.nid = p.nid');
$query
->condition((new Condition('OR'))
->condition('n.title', $search, 'LIKE')
->condition('p.model', $search, 'LIKE'));
}
$result = $query
->execute();
foreach ($result as $row) {
$options[$row->nid] = $row->title;
}
if (count($options) == 0) {
$options[0] = $this
->t('No products found.');
}
$form_state
->set('products_action', 'select');
$form_state
->set('product_select_options', $options);
$form_state
->set('refresh_products', NULL);
$form_state
->setRebuild();
}
/**
* Sets the order pane to show the add product to order form.
*/
public function productSelectSubmit($form, FormStateInterface $form_state) {
$form_state
->set('products_action', 'add_product');
$form_state
->set('node', Node::load($form_state
->getValue([
'product_controls',
'nid',
])));
$form_state
->set('refresh_products', NULL);
$form_state
->setRebuild();
}
/**
* Hides the form to add another product to the order.
*/
public function productSelectClose($form, FormStateInterface $form_state) {
$form_state
->set('products_action', NULL);
$form_state
->set('refresh_products', NULL);
$form_state
->set('product_select_options', NULL);
$form_state
->setRebuild();
}
/**
* Form submit callback: add a blank line product to an order.
*/
public function addBlank($form, FormStateInterface $form_state) {
$form_state
->set('refresh_products', TRUE);
$form_state
->setRebuild();
$order = $form['#order'];
$product = OrderProduct::create([
'qty' => 1,
'order_id' => $order
->id(),
]);
$product
->save();
$order->products[] = $product;
$order
->logChanges([
'add' => $this
->t('Added new product line to order.'),
]);
}
/**
* Form submit callback: add a product to an order.
*/
public function addProductSubmit($form, FormStateInterface $form_state) {
$form_state
->set('products_action', 'products_select');
$form_state
->set('refresh_products', TRUE);
$form_state
->setRebuild();
$order = $form['#order'];
$data = \Drupal::moduleHandler()
->invokeAll('uc_add_to_cart_data', [
$form_state
->getValue('product_controls'),
]);
$values = uc_product_load_variant(intval($form_state
->getValue([
'product_controls',
'nid',
])), $data)
->toArray();
$values['qty'] = $form_state
->getValue([
'product_controls',
'qty',
]);
$values['order_id'] = $order
->id();
$product = OrderProduct::create($values);
\Drupal::moduleHandler()
->alter('uc_order_product', $product, $order);
$product
->save();
$order->products[] = $product;
$order
->logChanges([
$this
->t('Added (@qty) @title to order.', [
'@qty' => $product->qty->value,
'@title' => $product->title->value,
]),
]);
// Decrement stock.
if (\Drupal::moduleHandler()
->moduleExists('uc_stock')) {
uc_stock_adjust_product_stock($product, 0, $order);
}
// Add this product to the form values for accurate tax calculations.
$products = $form_state
->getValue('products');
$products[] = $product;
$form_state
->setValue('products', $products);
}
/**
* Form submit callback: remove a product from an order.
*/
public function removeProduct($form, FormStateInterface $form_state) {
$form_state
->set('refresh_products', TRUE);
/** @var \Drupal\uc_order\Entity\Order $order */
$order = $form['#order'];
$triggering_element = $form_state
->getTriggeringElement();
$order_product_id = intval($triggering_element['#return_value']);
/** @var \Drupal\uc_order\Entity\OrderProduct $product */
$product = $order->products[$order_product_id];
if (\Drupal::moduleHandler()
->moduleExists('uc_stock')) {
// Replace stock immediately.
uc_stock_adjust($product->model->value, $product->qty->value);
}
$product
->delete();
unset($order->products[$order_product_id]);
$order
->logChanges([
$this
->t('Removed %title from order.', [
'%title' => $product->title->value,
]),
]);
}
/**
* AJAX callback to render the order product controls.
*/
public function ajaxCallback($form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response
->addCommand(new ReplaceCommand('#product-controls', trim(drupal_render($form['product_controls']))));
$status_messages = [
'#type' => 'status_messages',
];
$response
->addCommand(new PrependCommand('#product-controls', drupal_render($status_messages)));
if ($form_state
->get('refresh_products')) {
$response
->addCommand(new ReplaceCommand('#order-edit-products', trim(drupal_render($form['products']))));
$response
->addCommand(new ReplaceCommand('#order-line-items', trim(drupal_render($form['line_items']))));
}
// Remove the field so we only refresh the admin comments item-list.
unset($form['admin_comment_field']);
$response
->addCommand(new ReplaceCommand('#order-pane-admin_comments .item-list', uc_order_pane_admin_comments('edit-theme', $form['#order'], $form, $form_state)));
return $response;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
OrderPanePluginBase:: |
public | function | ||
OrderPanePluginBase:: |
public | function |
Returns the classes used to wrap an order pane. Overrides OrderPanePluginInterface:: |
6 |
OrderPanePluginBase:: |
public | function |
Returns the title of an order pane. Overrides OrderPanePluginInterface:: |
2 |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
public | function | Constructs a \Drupal\Component\Plugin\PluginBase object. | 92 |
Products:: |
public | function | Form submit callback: add a blank line product to an order. | |
Products:: |
protected | function | Sets the quantity and attributes of a product added to the order. | |
Products:: |
public | function | Form submit callback: add a product to an order. | |
Products:: |
public | function | AJAX callback to render the order product controls. | |
Products:: |
public | function |
Form constructor. Overrides EditableOrderPanePluginInterface:: |
|
Products:: |
protected | function | Form to allow ordered products' data to be changed. | |
Products:: |
public | function | Hides the form to add another product to the order. | |
Products:: |
protected | function | Form to choose a product to add to the order. | |
Products:: |
public | function | Sets the order pane to show the product selection form. | |
Products:: |
public | function | Sets the order pane to show the add product to order form. | |
Products:: |
public | function | Validation handler for self::productSelectForm(). | |
Products:: |
public | function | Form submit callback: remove a product from an order. | |
Products:: |
public | function |
Form submission handler. Overrides EditableOrderPanePluginInterface:: |
|
Products:: |
public | function |
Returns the contents of an order pane as a store administrator. Overrides OrderPanePluginInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |