I would like to continue pushing the issue of improving ubercart's code base to include API's to expose functionality to outside developers, like the issue: #488422: Cleanup uc_attribute's product/class API, and add test coverage for the new API, as well as the UI.

In this case I was specifically looking for a way delete product features via code (ie: do what happens when you delete it via the link on the site). I could just copy what uc_product is doing on this event, but it would be far better if I could be certain I was always doing the same thing (even if ubercart changes this logic). While I was at it I also added two function to load the features.

I have created patches for both D6 and D7.

Comments

univate’s picture

Version: 6.x-2.x-dev » 7.x-3.x-dev

Wondering if I should focus on getting these changes into 7.x first then have them backported?

tr’s picture

I haven't had a chance to review your patch yet, but let me say in general the place for new APIs / changes to the existing API is going to be in the D7 branch. I'm perfectly willing to also include them in the D6 branch provided they don't break or change the existing API, and simply provide extensions to what we already have in D6. Any changes to function names/call parameter etc. should be only D7, as those changes have a high probability of breaking D6 contributed code. Same holds true for the attributes API.

I would also like to have these API changes tested by someone other than me, to make sure that they're not going to break things.

It would be nice to see a list of all the Product API functions in one issue, so we could discuss if there needs to be some renaming to make things consistent, or additions/deletions to provide additional functionality. IMO this would be better than just adding one or two functions at a time as we discover missing functionality. A little work on the big picture at this point in the D7 development will save us a lot of problems further down the road.

univate’s picture

Title: uc_product API » Product Feature API
StatusFileSize
new5.39 KB
new5.48 KB

Firstly I have attached new patches as I noticed a couple of minor typos.

Just to explain the changes being made here further...

Products in ubercart don't actually need an API as they are nodes and therefore you can just use node_load() and hook_nodeapi() function or the D7 equivalents to work with those object.

The patches here are actually for the Product feature data which is part of the uc_product module.

My definition of the API's that ubercart require are that there should be dedicated functions that deal with the database/SQL interface and anytime a features wants to interact with the database it ideally should go through one of these functions. I understand that there are cases where this is not always possible (e.g. complex one off SELECT's that you want to be able to optimize rather then having lots of database queries) but in general there should be functions available that provide the CRUD (create, read, update, delete) operations for each data object that ubercart has in its database.

So in this patch instead of using SQL directly we replace with a call to a function that executes the same SQL (ie: the SQL query now becomes re-usable by other function include third party modules/developers via a simple function call):

+++ uc_product/uc_product.admin.inc
@@ -404,18 +404,11 @@ function uc_product_features($node) {
-      $result = db_query("SELECT * FROM {uc_product_features} WHERE pfid = :pfid AND fid = :fid", array(
-        ':pfid' => arg(5),
-        ':fid' => arg(4),
-      ));
-      if ($feature = $result->fetchAssoc()) {
+      $feature = uc_product_feature_load(arg(5), arg(4));
+      if (isset($feature)) {

Instead now that the code is in its own function and you can see the code hasn't really changed, its still performing the same SQL query and returning the result.

+++ uc_product/uc_product.module
@@ -1999,6 +1999,66 @@ function uc_product_feature_save($data) {
+function uc_product_feature_load($pfid, $fid = NULL) {
+  $sql = "SELECT * FROM {uc_product_features} WHERE pfid = :pfid";
+  $arg = array(':pfid' => $nid);
+  if (isset($fid)) {
+    $sql .= ' AND fid = :fid';
+    $arg[':fid'] = $fid;
+  }
+  $feature = db_query($sql, $arg)->fetchAssoc();
+  return $feature;
+}

There is another problem with that SELECT query, any database person will tell you that you should never do a "SELECT *" - depending on the database it can be inefficient as it can require extra query(s) to retrieve the schema information before it can perform the actual query. When the list of each of the fields could easily be included in the original query there is no reason to use a "SELECT *".

The functions I am adding with this patch provide the R(ead) and D(elete) functions. There is already a uc_product_feature_save() function, which provides the last two required CRUD operations - C(reate) and U(pdate).

I think some work could be done to make that *_save function better, but an API is better then no API and I am looking for the easiest way to get an API in place and I think if we just refactor the code so that there are CRUD functions for each data object in ubercart we can then look to focus on making the API better.

The changes I'm proposing here in this patch I think are minor as they do not really change any code they just move it around into specific function for each of the operations to make up an API.

Powered by Dreditor.

tr’s picture

Awesome writeup.

Sounds like exactly the right thing to do. I'm going to defer to Lyle and let him commit this to D7 first, then I'll put it into D6.

The functions I am adding with this patch provide the R(ead) and D(elete) functions. There is already a uc_product_feature_save() function, which provides the last two required CRUD operations - C(reate) and U(pdate).

This is exactly what I meant by re-examining the Product API, because missing functionality like this should be pretty obvious if we write down all the functions on one sheet of paper and ask simple questions like "how do I delete a feature", or "how do I find out the options available for an attribute" etc.

univate’s picture

if we write down all the functions on one sheet of paper

Any suggestions where I could start writing such a document? somewhere on ubercart.org? or should I just use something like google docs for this?

Island Usurper’s picture

StatusFileSize
new9.6 KB

@univate, I'm going to add the 3.x branch to api.ubercart.org real soon, so it'll get automatically updated with the function docs. The API docs on Ubercart.org look like they need to be expanded to include regular API functions in addition to hooks and themes. Let me know if you don't have access to change that.

It might also be a good idea to write up some files for Advanced Help, but I guess that's really more for user documentation than developer.

After looking at uc_product_feature_load(), I don't think it actually makes sense to make the $fid optional. $fid is really the feature type, and if $pfid isn't optional, then you have to make sure that you got the right $fid before you try to load it. And in general, to do that, you have to load the feature to begin with. It's kind of like trying to load a term while specifying its tid and vocabulary. If you meant it to be a kind of sanity check, then I guess I need to see where the code could be insane and what could come of it.

I've also cleaned up the menu callbacks concerning product features, since I couldn't stand looking at all of those arg() calls. This just involves adding menu items and splitting up some callback functions as needed. I expect it to just behave the same as before, but we'll need to make sure of that during testing.

Island Usurper’s picture

Hm. I'm going to have to figure something else out for api.ubercart.org. I don't have a good way to keep the code updated on that site, so I don't really want to put the current code up there yet.

Grumble, grumble.

univate’s picture

Totally agree with you on the arg()'s, it was painful looking at them - what you have done looks good to me, although I haven't done any formal testing.

Also the only reason I left in the $fid argument was it was previously being used in the original query, I was presuming it was only there as a sanity check myself.

Island Usurper’s picture

StatusFileSize
new16.82 KB

Did some testing, and found that editing didn't work right because the pfid wasn't being saved in uc_roles_products correctly. I have also changed it so that uc_product_feature_form() is used as a form wrapper to add the 'nid' and 'pfid' values and the submit button to feature forms automatically. This means that the feature modules shouldn't call uc_product_feature_form() from their own form functions any more.

I'm not sure if checkout works right now, so I can't see if the actual role promotion works. But adding, editing, and deleting role features on the product works well. If someone wants to test the file download feature, that's probably a good next step.

univate’s picture

StatusFileSize
new15.76 KB

Re-rolled this patch.

Island Usurper’s picture

D'oh! I forgot about this patch while I was working on the Rules integration, so now I've done something different on that branch. After comparing the two, I think this patch is better. Since checkout works on the Rules branch, I'll apply this patch to it and make sure it works. Once that's good, I'll commit it to HEAD.

Island Usurper’s picture

Status: Needs review » Fixed

Yeah, all the paths work, and the features still do their jobs. Committed.

Status: Fixed » Closed (fixed)

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

univate’s picture

Version: 7.x-3.x-dev » 6.x-2.x-dev
Status: Closed (fixed) » Needs review

So this is been committed to D7, can it also be considered for D6.

The D6 patch in #3 above still applies, although does not include all the additional changes made in subsequent patches - I'm going to review to see if they are needed in D6 as well.

tr’s picture

Status: Needs review » Patch (to be ported)

The D7 patch from #3 was changed quite a bit before it reached the version in #10 that got committed. I think the D7 patch from #3 should have the same changes so that what goes into D6 is consistent with what went into D7. Can you back port #10 to D6?

univate’s picture

Status: Patch (to be ported) » Needs review

The changes between #3 and #10 are related to the following refactoring work and not specific to the actual issue of adding functions to load and delete product features:

I've also cleaned up the menu callbacks concerning product features, since I couldn't stand looking at all of those arg() calls. This just involves adding menu items and splitting up some callback functions as needed. I expect it to just behave the same as before, but we'll need to make sure of that during testing.

That said I did attempt to try and port the code, but there are things being done in "new D7 ways" which means it can't be achieved with a straight port and required some refactoring itself. Doing any major refactoring like this in D6 is probably best avoided unless it provides some benefit - in this case it doesn't and I would like to keep the patch reasonably simple to read/review.

It should be easy to see from just reading the patch in #3 that no functionality is being changed with this issue. The only difference is a couple of blocks of code are moved to their own function, making them re-usable and providing a full CRUD api for product features.

So I would like to propose we review the current patch as is and get it committed, if there is agreement that the refactoring of the menu handlers for the products features should be done like in D7 then we can follow that up with another issue/patch.

torgospizza’s picture

I agree with univate. Let's at least get the small bit of functionality added first, and expand upon it later if we think that's needed. (I'm not sure it is at this point but to be honest I haven't been keeping up with everything D7 related.) This patch is somewhat essential to where I want to go with our site and I think it would make the transition to D7 easier later on if some of this new hotness was made available to us now in D6.

I'll test this out in dev and then see about how it behaves in production.

univate’s picture

Anyone had a go at testing this patch for D6?

univate’s picture

StatusFileSize
new5.54 KB

Would be good to get this in before the next release. Its been in the issue queue now for almost a year. It doesn't change any functionality. It cleans up some code making it more readable and exposes a couple of functions that make it possible for developers to work with product features outside the UI.

torgospizza’s picture

Checked it out, patch applied fine (with a small offset). Tested deleting and creating features, everything seems to be fine. I don't see a reason why it can't get committed. I say let's get it in there.

tr’s picture

Status: Needs review » Needs work

What's this?

+  return SAVED_DELETED;
univate’s picture

tr’s picture

Status: Needs review » Fixed

Thanks - I learned something about Drupal ... Committed.

torgospizza’s picture

And there was much rejoicing! ;)

Thanks, TR!

tr’s picture

@univate, @torgosPizza, @Island Usurper: Speaking of the product features API, can you look at #434676: uc_product_feature_save, possible to pass by reference instead? and re-open that if you think it's something that still needs to be done?

torgospizza’s picture

@TR: Will check that out and add comments to that issue. Thanks for bringing it up.

Status: Fixed » Closed (fixed)
Issue tags: -Ubercart API

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