To be a little bit nitpicky: if you want to stick to common coding style guidelines, the sooner you go for it, the better it is. (Because later on, when more people participate and propose patches, their patches will break likely, just because you've done the changes at that time). Eg just on scanning through your classes, I saw multiple line breaks very often,...

Reported by @agoradesign https://www.drupal.org/node/2618906#comment-11179689

Comments

guy_schneerson created an issue. See original summary.

agoradesign’s picture

Assigned: Unassigned » agoradesign

I'll assign this to me, as I'm currently reviewing and tidying up a little bit. A patch + comments will follow :)

agoradesign’s picture

Assigned: agoradesign » Unassigned
StatusFileSize
new91.47 KB

Woooo, reviewing all the code took quite more time than a thought (plus a bottle of wine :D)

Attached is a patch, that mostly...

  • does formatting / code style adjustments like removing empty lines or replacing "array()" with "[]"...
  • ...or removing unused use statements or variables
  • fixes typos in comments, variable names, etc
  • adds missing doc comments (function parameters and return values,..)
  • adds stronger typing to function parameters (declare types like array or specific interfaces)
  • sometimes does some tiny refactorings... e.g. inject a service within another service rather than using \Drupal::service() call

(some kind of) disclaimer:

What I did, was some very low-level work. I may have missed intentionally or unintentionally some other potential improvements. I'm also not sure, if that meets 100% Drupal coding standards, but it's at least a big leap towards to that.
I haven't installed and used commerce_stock at all. All changes and adjustments I propose in the patch are untested, but I did it cautiously and with good tool support. But reviewing and testing the changes before committing would be a good idea anyway.

Some general impressions gained during review

One impression, that I've got on several places is that the module hasn't fully arrived in D8/Symfony2 yet. This is evident in many function definitons, where several IDs and arrays are passed around, or especially in the storage submodule: until now, I haven't really seen hook_schema implementations in D8, most likely as it's easy as it can be to define custom entity types in D8 -> if you define an custom entity type e.g. for cs_inventory_transaction, you wouldn't have to write much more lines of code in D8. instead of the database-centric hook_schema implementation, you would define your own entity class, add the necessary annoations, base field definitions and additionally all the getter/setter and additional methods you want to have -> with actually not really much more work you'll get a fully typed entitiy class, that you can enrich with any custom functions you like. Additionally, all the parts, where you use the deprecated db_insert() calls, would be even easier to replace - you could use YOUR_ENTITY_CLASS::create([values])->save(); instead and would be fully D8.2 compatible.

Another point, that I've already mentioned before, is that the whole structure of all the classes, services, interfaces, etc defined here, is quite confusing. Of course, the whole topic and the targeted goals of this module cover a very complex scenario, so it's clear, that the structure can get quite complicated of course. But especially in this constellation, one should try to not add too much unnecessary complexity.

This was even hardened by the fact, that the current state of code comments is quite weak. In my patch, I tried to add at least a basic documentation - at least I had to find out the data types of the involved variables, etc. And then I really sometimes got lost somewhere in between all the different classes, that are partly quite tightly knit to each other. Some examples:

On the one hand, I saw some strange circular dependencies: e.g. the StockManager initializes a property holding a StockManagerConfig object in its constructor, which itself takes the StockManager as reference. I don't know, if Symfony2 would find a way to handle this, if both would be services and dependency injection would be used - but e.g. Spring Framework wouldn't compile due to a CircularDependencyException.

And finally one thing, that confused me really very much, is a combination of naming and structure. On the one hand, there's one big directory containing all different interface and class implmentations (see #2726581: better organisation of classes and interfaces), and some classes are implementing several of these interfaces (which is basically ok). On the other hand there some unclear namings, like "AlwaysInStock" and "AlwaysInStockService", which seemed to me to be directly connected to each other, but aren't. I believe, in this case, not the class names are bad, but the fact that they are in the same namespace, listed directly one after another, together with lots of XxxInterface and Xxx class defintions within the same directory/namespace.

And last but not least a very important hint, that I don't want to hide: after having it a very long time on my personal radar without ever trying, we recently decided to try and soon after decide to switch from Eclipse/Aptana to PhpStorm as our preferred IDE. We really fell in love with that tool!!! I believe, that so far I only used a small portion of the possibilites the software offers, but the gain of time, comfort and especially code quality is incredible! I tell you because many of the harmless code style adjustments, as well as problems you wouldn't see without a sophisticated IDE, wouldn't be possible without the usage PhpStorm at all -> I'll open up a separate issue right after this commit about a problem I've found only because of the great validation of that IDE...

agoradesign’s picture

Status: Active » Needs review
guy_schneerson’s picture

Wow agoradesign that's amazing.
Great timing I just upgraded my Coder Sniffer yesterday, will review and test before committing. I will especially pay attention to any refactoring you have made.

Let me start by addressing what I consider the most important point in your comments as it relates to a core design concept I have made and wish to defend it, however please let me know if you are are not convinced.
The choice of using the schema and not the entity API was an intentional one. The transactional approach I have taken has a number of important design concepts that I think are not suitable for the entity API.
The transactional system was designed to suport high volume and high performance with theoretically no possibility of deadlocks & performance bottlenecks.
The concepts are:

User activity:
- Each transaction (sale & new stock) will use a single insert & each stock movement will use two inserts.
- Transactions are not updatable. Same as traditional accounting, you made a mistake, create a transaction to balance the mistake. Example: creating a credit note.
- Stock checking involves two select queries one is a transaction sum using the primary index (transaction ID).

Cron:
- Regularly sums up transactions into the "inventory location level" (cron job not written yet so have to use the dev module to run this) this makes sure the second select query can run on a manageable dataset instead of infinitely growing.
- Cron jobs are the only process allowed updates on the "inventory location level" so not introducing a chance of deadlocks.

A similar approach is used by other ERP and inventory systems. Looks to me like the Drupal entities as amazing as it is, is not the correct tool for the job but I am open to be convinced otherwise if you think such is the case.
The locations are the only table that I think should probably be converted to an entity but it is of low importance for me at this stage as it has a mainly a passive part.

As far as the developer tools module go, as the module description states, this is a temporary module that is only used for the early development stage and should be viewed as a sandbox. It is possible that it contains code that's no longer relevant. However I will review all your code changes including this module.

I am totally in agreement about better organisation of the code as I believe all the complexity it holds is for a good reason and that good naming and folder structure are important to make the code easier to understand and work with.

I am aware I am speeding through things and taking some compromises along the way however I am close to having a working version of the module and hopefully the architectural decisions I have taken are all sound and the rest can be cleaned up.

I will review all your other comments and code changes and get back to you but I am amazed at the amount of work and excited about improvements to the code. thanks a lot for this patch and the effort you have put into it it is really appreciated.

agoradesign’s picture

You're welcome. I also configured Code Sniffer in my PHP Storm yesterday and was looking for a "victim" to test, if it's working. I remembered our discussion and thought checking this module would be a nice idea :)

regarding the architecture: these are very good arguments :) I must confess that I don't have very much low-level knowledge about what is going on in the Entity API behind the scenes. As there are pre and post save hooks on inserts and updates, the entity approach of course can't be better in performance theoretically. On the other hand, if you design your system so, that none of these hooks are really implemented, there shouldn't be too much difference in performance. And I also don't know, if there's more risk of deadlocks!?? But I'm definitely not the right person to estimate this. Maybe you could ask Wim Leers about that, as he is a real performance ninja, or fago, as he is the founder of Entity API, or bojanz,...

One thing I know about D8 entities - based on my own experience - that you can design them to only reside in a single table. If your entities are non translatable, not fieldable, not revisionable and do not have one to many relationships in their base field definitions, you only will have one table in the database, no extra "data" table like the ones we know from nodes, terms,...

If you don't want entities to be updateable, you can prevent this easily - you could even prevent saving in a pre-save hook :D

In the end, for me as potential developer using the module, it shouldn't make too much difference whether or not you use low-level schema definitions and SQL queries or not, as long as the module's API is powerful enough to cover all the needs and works as expected :-)

Anyhow, I'm looking forward to this module. I was concentrating yesterday much on commenting, removing unneeded blank lines, imports, etc, so that I didn't look too much into the actual semantics of all the bits and bytes I saw, but after all I saw there and read from your posts and comments, I'm pretty sure that it will be a giant evolution step in comparison to the much simpler D7 version...

guy_schneerson’s picture

Hi agoradesign jsut applied the patch and tested and looks like functionality all working as expected. the only thing that failed was the StockAvailabilityChecker code you identified in the other issue, a good sign.
Will do so more testing and go over all the code to make sure I understand changes before committing.
Interesting observations about the entity API and if I can validate it can bee as safe to use as the schema code then it can be swooped at a later stage.

guy_schneerson’s picture

Assigned: Unassigned » guy_schneerson
Status: Needs review » Needs work

agoradesign fantastic patch committed.
You know what they say "The devil is in the details" and I just created a code sniffer report and it produced a report that is 666 line long, I kid you not. So I will do my best to chase him out of earth.
looks like mostly superficial changes so I am sure not as bad as it looks.

agoradesign’s picture

haha, I believe you. The module is quite big. If code sniffer always complains about the same few superficial rule violations, the report will grow quickly

  • guy_schneerson committed 7018c70 on 8.x-1.x
    Issue #2726587 by agoradesign, guy_schneerson: Coding standards
    
guy_schneerson’s picture

Assigned: guy_schneerson » Unassigned
Status: Needs work » Fixed

fixed all coding standards issues with the exception of the dev module (as it will go).
We should check again wen the module is closer to being ready.

agoradesign’s picture

Great work!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

steveoliver’s picture

Since the latest commits, we've accrued some coding standards issues, if we are to use Drupal coding standards officially. See https://travis-ci.org/steveoliver/commerce_stock/jobs/175921201.

Can we re-open this issue and resume work?

steveoliver’s picture

Title: Coding standards » Start following coding standards
guy_schneerson’s picture

Status: Closed (fixed) » Needs work

cosmicdreams’s picture

Ran a check today using phpcs and found over 3k recommended code standards cleanup. I think I'll start here if that's ok.

cosmicdreams’s picture

StatusFileSize
new25.89 KB

First pass. Fixes all but the dependency injection code formatting standards. Those will require some more extensive changes to the construct and create methods of the objects.

guy_schneerson’s picture

Thanks @cosmicdreams will review.

guy_schneerson’s picture

Title: Start following coding standards » Coding standards

Thanks @cosmicdreams
I committed all your changes except the StockTransactions1 & StockTransactions2 forms as those contained an important @todo item. Thanks again, great work :)

cosmicdreams’s picture

StatusFileSize
new13.67 KB

Second pass. This one fixes a number of places where container injection is recommended to be used (by our coding standards)

cosmicdreams’s picture

StatusFileSize
new12.48 KB

After reviewing how that looked once it got up here I noticed I'm rewriting the docblock on a few functions and some curious things came along for the ride. Here's a new patch and let's discuss:

cosmicdreams’s picture

  1. +++ b/modules/field/src/Plugin/Field/FieldFormatter/SimpleStockLevelFormatter.php
    @@ -18,7 +22,30 @@ use Drupal\Core\Field\FieldItemListInterface;
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, StockServiceManager $stock_manager) {
    +    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
    +    $this->stockServiceManager = $stock_manager;
    +  }
    +
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    +    return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'],
    +      $container->get('commerce_stock.service_manager'));
    +  }
    

    This is the bulk of what I'm trying to do for the SimpleStockLevelFormatter and the SimpleStockLevelWidget. Inject the services that have been brought in by \Drupal

  2. +++ b/src/Form/StockConfigForm.php
    @@ -158,6 +181,8 @@ class StockConfigForm extends ConfigFormBase {
    +   * @throws \Drupal\Component\Plugin\Exception\PluginException
    

    My IDE tells me about Exceptions that we should include in the docblocks all the time. Well, usually for cases where we haven't handled an Exception that could come up.

  3. +++ b/src/Form/StockConfigForm.php
    @@ -193,4 +218,13 @@ class StockConfigForm extends ConfigFormBase {
    +  /**
    +   * {@inheritdoc}
    +   */
    +  protected function getEditableConfigNames() {
    +    return [
    +      'commerce_stock.service_manager',
    +    ];
    +  }
    +
    

    I wasn't expecting StockConfigForm to be missing methods that the ConfigFormBase requires extended objects to override.

olafkarsten’s picture

thanks @cosmicdreams. Some nitpicks from me

  1. +++ b/modules/field/src/Plugin/Field/FieldFormatter/SimpleStockLevelFormatter.php
    @@ -18,7 +22,30 @@ use Drupal\Core\Field\FieldItemListInterface;
    +  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, StockServiceManager $stock_manager) {
    +    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
    +    $this->stockServiceManager = $stock_manager;
    
    +++ b/modules/field/src/Plugin/Field/FieldWidget/SimpleStockLevelWidget.php
    @@ -20,7 +24,29 @@ use Drupal\Core\Link;
    +  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, StockServiceManager $stock_manager) {
    +    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    +    $this->stockServiceManager = $stock_manager;
    
    +++ b/modules/ui/src/Form/StockTransactions2.php
    @@ -26,10 +30,24 @@ class StockTransactions2 extends FormBase {
    +  public function __construct(EntityTypeManagerInterface $entity_type_manager, StockServiceManager $stock_manager, Request $request) {
    +    $this->productVariationStorage = $entity_type_manager->getStorage('commerce_product_variation');
    +    $this->stockServiceManager = $stock_manager;
    

    Should be $stock_service_manager

  2. +++ b/modules/ui/src/Form/StockTransactions2.php
    @@ -26,10 +30,24 @@ class StockTransactions2 extends FormBase {
    +    $this->request = $request->getCurrentRequest();
    

    I don't see the declaration of the $request class property?

olafkarsten’s picture

+++ b/modules/field/src/Plugin/Field/FieldFormatter/SimpleStockLevelFormatter.php
@@ -18,7 +22,30 @@ use Drupal\Core\Field\FieldItemListInterface;
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, StockServiceManager $stock_manager) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
+    $this->stockServiceManager = $stock_manager;
+  }
...
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'],
+      $container->get('commerce_stock.service_manager'));
+  }

+++ b/src/Form/StockConfigForm.php
@@ -62,6 +65,26 @@ class StockConfigForm extends ConfigFormBase {
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('entity_type.manager'),
+      $container->get('entity_type.bundle.info'),
+      $container->get('commerce_stock.service_manager'),
+      $container->get('plugin.manager.stock_events')
+    );

As far as I know coding standard has no rule for wrapping function declarations and calls. In times of DI the arguments list tends to be undreadable awful long.

I think we should always wrap them if long. But thats Guys decision, i guess.

See #1539712: [policy, no patch] Coding standards for breaking function calls and language constructs across lines for more background on this.

At least we should be consistent. Wrap or not to Wrap - @guy_schneerson - that's your turn

olafkarsten’s picture

Which one looks better :D

+++ b/modules/field/src/Plugin/Field/FieldFormatter/SimpleStockLevelFormatter.php
@@ -18,7 +22,30 @@ use Drupal\Core\Field\FieldItemListInterface;
 
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, StockServiceManager $stock_manager) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
    $this->stockServiceManager = $stock_manager;
}

/**
   * {@inheritdoc}
   */
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'],
      $container->get('commerce_stock.service_manager'));
}
public function __construct(
  $plugin_id, 
  $plugin_definition, 
  FieldDefinitionInterface $field_definition, 
  array $settings, 
  $label, 
  $view_mode,
  array $third_party_settings, 
  StockServiceManager $stock_manager
) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
    $this->stockServiceManager = $stock_manager;
}

public static function create( 
   ContainerInterface $container, 
   array $configuration, 
   $plugin_id, 
   $plugin_definition
) {
   return new static(
     $plugin_id, 
     $plugin_definition,
     $configuration['field_definition'], 
     $configuration['settings'], 
     $configuration['label'], 
     $configuration['view_mode'], 
     $configuration['third_party_settings'],
     $container->get('commerce_stock.service_manager')
   );
}
cosmicdreams’s picture

I do prefer the second. I don't mind long documents. I may have an opportunity to make these changes tonight.

cosmicdreams’s picture

StatusFileSize
new12.77 KB

I've included those recommendations @olafkarsten. I eventually declined the change to vertical-ize the __construct methods but added the changed to the create methods.

cosmicdreams’s picture

cosmicdreams’s picture

Status: Needs work » Needs review
olafkarsten’s picture

Status: Needs review » Reviewed & tested by the community

Rerolled the patch and created a PR
https://github.com/BBGuy/commerce_stock/pull/45

otherwise it wouldn't apply after the changes introduced in PR 43/44 if they gets commited before the patch. Otherwise works.

guy_schneerson’s picture

Thanks @cosmicdreams and @olafkarsten for an excellent job.
Looks good and i also prefer warping long lines. I was just never sure about the coding standards.
Will need to spend some time reviewing this as it does make some more significance changes beyond just wrapping line and spaces. but all look like good changes. So will start with this issue next time I get a chance.

guy_schneerson’s picture

Status: Reviewed & tested by the community » Needs work

All changes but 1 are now committed (I think I may have committed the more straightforward changes in a separate commit).
The changes to StockTransactions2 was not committed as it was causing an error "Recoverable fatal error: Argument 3 passed to Drupal\commerce_stock_ui\Form\StockTransactions2::__construct() must be an instance of Symfony\Component\HttpFoundation\Request"
will be happy to push that one file as a patch or a new PR.

cosmicdreams’s picture

Cool, I'll take a look. In the future, would you prefer a PR via github or a patch via D.O?

guy_schneerson’s picture

A PR is preferable but if its a small change a patch is ok.

cosmicdreams’s picture

guy_schneerson’s picture

Thanks @cosmicdreams merged and pushed.

olafkarsten’s picture

Status: Needs work » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.