class StockLevel in Commerce Stock 8
Plugin implementation of the 'commerce_stock_field' field type.
Plugin annotation
@FieldType(
id = "commerce_stock_level",
label = @Translation("Stock level"),
module = "commerce_stock_field",
description = @Translation("Stock level"),
default_widget = "commerce_stock_level_simple_transaction",
default_formatter = "commerce_stock_level_simple",
cardinality = 1,
)
Hierarchy
- class \Drupal\Core\TypedData\TypedData implements PluginInspectionInterface, TypedDataInterface uses DependencySerializationTrait, StringTranslationTrait, TypedDataTrait
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\commerce_stock_field\Plugin\Field\FieldType\StockLevel uses ContextCreatorTrait
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
Expanded class hierarchy of StockLevel
1 file declares its use of StockLevel
- StockLevelTest.php in modules/
field/ tests/ src/ Kernel/ StockLevelTest.php
File
- modules/
field/ src/ Plugin/ Field/ FieldType/ StockLevel.php, line 26
Namespace
Drupal\commerce_stock_field\Plugin\Field\FieldTypeView source
class StockLevel extends FieldItemBase {
use ContextCreatorTrait;
/**
* {@inheritdoc}
*
* Originally we had to define a real db field, because cores implementation
* of computed fields was brittle. During development of the module, we
* found, that we can "misuse" this to provide the possibility to enter
* initial stock values for newly created product variations.
*
* Currently we use the column 'value' for exactly this one purpose. Don't get
* fooled by this. The calculation of the stock level is transaction based.
* The transactions have their own table.
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'value' => [
'type' => 'numeric',
'size' => 'normal',
'precision' => 19,
'scale' => 4,
'not null' => FALSE,
],
],
];
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('float')
->setLabel(t('Available stock'));
$properties['available_stock'] = DataDefinition::create('float')
->setLabel(t('Available stock'))
->setComputed(TRUE)
->setInternal(FALSE)
->setReadOnly(TRUE)
->setClass('Drupal\\commerce_stock_field\\StockLevelProcessor')
->setSetting('stock level', 'summary');
return $properties;
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this
->get('value')
->getValue();
return $value === NULL;
}
/**
* @inheritdoc
*
* This updates the stock based on parameters set by the stock widget.
*
* For computed fields we didn't find a chance to trigger the transaction,
* other than in ::setValue(). ::postSave() is not called for computed fields.
*
* If you pass in a single value programmatically, note that we do not support
* the setting of a absolute stock levels here. We assume a stock adjustment
* if we get a singe value here. As usual a negative value decreases the
* stock level and a positive value increases the stock level.
*
* @throws \InvalidArgumentException
* In case of a invalid stock level value.
*/
public function setValue($values, $notify = TRUE) {
// Supports absolute values being passed in directly, i.e.
// programmatically.
if (!is_array($values)) {
$value = filter_var($values, FILTER_VALIDATE_FLOAT);
if ($value !== FALSE) {
$values = [
'adjustment' => $value,
];
}
else {
throw new \InvalidArgumentException('Values passed to the commerce stock level field must be floats');
}
}
// Set the value so it is not recognized as empty by isEmpty() and
// postSave() is called.
if (isset($values['value'])) {
$values['value'] = $values['value'];
}
elseif (isset($values['adjustment'])) {
$values['value'] = $values['adjustment'];
}
else {
$values['value'] = 0.0;
}
parent::setValue($values, $notify);
}
/**
* {@inheritdoc}
*/
public function postSave($update) {
// Retrieve entity and saved stock.
$entity = $this
->getEntity();
$values = $entity->{$this
->getFieldDefinition()
->getName()}
->getValue();
$values = reset($values);
// Create transaction.
$this
->createTransaction($entity, $values);
}
/**
* Internal method to create transactions.
*/
private function createTransaction(EntityInterface $entity, array $values) {
// To prevent multiple stock transactions, we need to track the processing.
static $processed = [];
// This is essential to prevent triggering of multiple transactions.
if (isset($processed[$entity
->getEntityTypeId() . $entity
->id()])) {
return;
}
$processed[$entity
->getEntityTypeId() . $entity
->id()] = TRUE;
$stockServiceManager = \Drupal::service('commerce_stock.service_manager');
$transaction_qty = empty($values['adjustment']) ? 0 : $values['adjustment'];
// Some basic validation and type coercion.
$transaction_qty = filter_var((double) $transaction_qty, FILTER_VALIDATE_FLOAT);
if ($transaction_qty) {
$transaction_type = $transaction_qty > 0 ? StockTransactionsInterface::STOCK_IN : StockTransactionsInterface::STOCK_OUT;
// @todo Add zone and location to form.
/** @var \Drupal\commerce_stock\StockLocationInterface $location */
$location = $stockServiceManager
->getTransactionLocation($this
->getContext($entity), $entity, $transaction_qty);
if (empty($location)) {
// If we have no location, something isn't properly configured.
throw new \RuntimeException('The StockServiceManager didn\'t return a location. Make sure your store is set up correctly?');
}
$zone = empty($values['zone']) ? '' : $values['zone'];
$unit_cost = NULL;
if (isset($values['unit_cost']['amount'])) {
$unit_cost = filter_var((double) $values['unit_cost']['amount'], FILTER_VALIDATE_FLOAT);
$unit_cost ?: NULL;
}
$currency_code = empty($values['unit_cost']['currency_code']) ? NULL : $values['unit_cost']['currency_code'];
$transaction_note = empty($values['stock_transaction_note']) ? '' : $values['stock_transaction_note'];
$metadata = [
'data' => [
'message' => $transaction_note,
],
];
if (!empty($values['user_id'])) {
$metadata['related_uid'] = $values['user_id'];
}
else {
$metadata['related_uid'] = \Drupal::currentUser()
->id();
}
$stockServiceManager
->createTransaction($entity, $location
->getId(), $zone, $transaction_qty, (double) $unit_cost, $currency_code, $transaction_type, $metadata);
}
}
/**
* @inheritDoc
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
// Hint: These are our hardcoded values from the schema definitiion.
// We could use a decimal with 15 digits, but lets keep it closer to the
// 99% use cases. A random float between -999 and +999 should do it.
$scale = 4;
// (mt_rand() / $r_max) = A number between 0 and 1.
$random_decimal = mt_rand() / mt_getrandmax() * 999 * 2 - 999;
// @see Drupal\Core\Field\Plugin\Field\FieldTypeNumericItemBase::truncateDecimal()
$values['value'] = floor($random_decimal * pow(10, $scale)) / pow(10, $scale);
return $values;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ContextCreatorTrait:: |
public static | function | Creates a commerce context object. | |
ContextCreatorTrait:: |
public | function | Returns the active commerce context. | |
ContextCreatorTrait:: |
private | function | Get context details. | |
ContextCreatorTrait:: |
public | function | Checks that the context returned is valid for $entity. | |
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 | |
FieldItemBase:: |
public static | function |
Calculates dependencies for field items. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Calculates dependencies for field items on the storage level. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Defines the field-level settings for this plugin. Overrides FieldItemInterface:: |
7 |
FieldItemBase:: |
public static | function |
Defines the storage-level settings for this plugin. Overrides FieldItemInterface:: |
10 |
FieldItemBase:: |
public | function |
Defines custom delete behavior for field values. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Defines custom revision delete behavior for field values. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Returns a form for the field-level settings. Overrides FieldItemInterface:: |
7 |
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Gets the entity that field belongs to. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the field definition. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the langcode of the field values held in the object. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function | Returns the value of a field setting. | |
FieldItemBase:: |
protected | function | Returns the array of field settings. | |
FieldItemBase:: |
public static | function |
Returns the name of the main property, if any. Overrides FieldItemInterface:: |
8 |
FieldItemBase:: |
public static | function |
Informs the plugin that a dependency of the field will be deleted. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Defines custom presave behavior for field values. Overrides FieldItemInterface:: |
7 |
FieldItemBase:: |
public | function |
Returns a form for the storage-level settings. Overrides FieldItemInterface:: |
8 |
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Returns a renderable array for a single field item. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function |
Different to the parent Map class, we avoid creating property objects as
far as possible in order to optimize performance. Thus we just update
$this->values if no property object has been created yet. Overrides Map:: |
|
FieldItemBase:: |
public | function |
Constructs a TypedData object given its definition and context. Overrides TypedData:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Gets a property value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Magic method: Determines whether a property is set. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Magic method: Sets a property value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Unsets a property. Overrides FieldItemInterface:: |
|
Map:: |
protected | property |
The data definition. Overrides TypedData:: |
|
Map:: |
protected | property | The array of properties. | |
Map:: |
protected | property | An array of values for the contained properties. | |
Map:: |
public | function |
Applies the default value. Overrides TypedData:: |
4 |
Map:: |
public | function |
Gets a property object. Overrides ComplexDataInterface:: |
|
Map:: |
public | function | ||
Map:: |
public | function |
Gets an array of property objects. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns a string representation of the data. Overrides TypedData:: |
|
Map:: |
public | function |
Gets the data value. Overrides TypedData:: |
1 |
Map:: |
public | function |
Overrides TraversableTypedDataInterface:: |
4 |
Map:: |
public | function |
Sets a property value. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns an array of all property values. Overrides ComplexDataInterface:: |
1 |
Map:: |
public | function | Magic method: Implements a deep clone. | |
StockLevel:: |
private | function | Internal method to create transactions. | |
StockLevel:: |
public static | function |
@inheritDoc Overrides FieldItemBase:: |
|
StockLevel:: |
public | function |
Determines whether the data structure is empty. Overrides Map:: |
|
StockLevel:: |
public | function |
Defines custom post-save behavior for field values. Overrides FieldItemBase:: |
|
StockLevel:: |
public static | function |
Defines field item properties. Overrides FieldItemInterface:: |
|
StockLevel:: |
public static | function |
Originally we had to define a real db field, because cores implementation
of computed fields was brittle. During development of the module, we
found, that we can "misuse" this to provide the possibility to enter
initial stock values for… Overrides FieldItemInterface:: |
|
StockLevel:: |
public | function |
@inheritdoc Overrides FieldItemBase:: |
|
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. | |
TypedData:: |
protected | property | The property name. | |
TypedData:: |
protected | property | The parent typed data object. | |
TypedData:: |
public static | function |
Constructs a TypedData object given its definition and context. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets a list of validation constraints. Overrides TypedDataInterface:: |
9 |
TypedData:: |
public | function |
Gets the data definition. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the name of a property or item. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the parent data structure; i.e. either complex data or a list. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Returns the property path of the data. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the root of the typed data tree. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Sets the context of a property or item via a context aware parent. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Validates the currently set data value. Overrides TypedDataInterface:: |
|
TypedDataTrait:: |
protected | property | The typed data manager used for creating the data types. | |
TypedDataTrait:: |
public | function | Gets the typed data manager. | 2 |
TypedDataTrait:: |
public | function | Sets the typed data manager. | 2 |