Classes for objects and Drupal altering

One of the goals of the Ubercore Initiative and thus Drupal Commerce was a better definition and use of objects. With the code in development mimicking node types / nodes, we're no worse off than Drupal core, but we're also no better. In fact, I'll be going through the product type code here in a minute to correct the ever-present problem of the same data being represented sometimes as an object and sometimes as an associative array. I again want to raise this topic, because I think it would be a great benefit to leave behind the ol' stdClass and make some well-defined classes.

For example, to create a product type on the fly, you'd have to do something like:

<?php
$product_type
= array(
 
'type' => 'product',
 
'name' => t('Basic product'),
 
'description' => t('Add a basic product with no additional fields.'),
);
commerce_product_type_save((object) $product_type);
?>

You could always define it as a stdClass up front or cast it to an object elsewhere, but that's the gist of it. And then if you wanted to load the existing product types, you'd get back an array of product type arrays. I want to leave this behind.

Instead, I'm wanting to convert the API to have a product type class (CommerceProductType) with protected class properties for the type, name, description, and (soon to be supported) help text... and whatever else needs to be there. I'd like to consider using the __get and __set Magic Methods as well, though I'm happy to be persuaded otherwise. Naturally a simple __construct method would be utilized to make it easy to create a new product type on the fly...

<?php
$product_type
= new CommerceProductType(
 
'product',
 
t('Basic product'),
 
t('Add a basic product with no additional fields.')
);
$product_type->save();
?>

The main problem I don't have the brainpower to solve at this time is how this would mesh with drupal_alter()ing. If you can't just add properties to an object willy-nilly, how would you alter fields onto it? If this is a problem, I suppose it wouldn't be the end of the world to accommodate Drupal and leave alterable objects as stdClass, but I still think it would behoove us to start to define the rest of our classes and trust that Drupal core will catch up eventually.

1.. 2.. 3.. Debate!

Comments

To help start the debate

I was chuckling at the "@todo: Remove this in Drupal 7" notation in the Drupal_alter documentation before making another dent in studying up on D7. I still need to look at the Fields API *sigh*.

My initial, very preliminary thoughts are partially on a slightly "devil's advocate" stance: what is the likelihood of Drupal core catching up in the way you described in a timely manner? My understanding is Drupal 7 is what it is at this point and the goal of Drupal Commerce is a release for D7?

I'm apparently tired because I've already started, stopped, and erased text in this comment a few times. So, I'm leaving out some other thoughts - I think this is really interesting and wanted to help spur a discussion about it.

No real debate

Frankly, there is no real debate here.

Question: Do we want to add visibility modifiers to our objects?

Answer: no, that will break the drupal_alter() pattern, and this is not how Drupal works.

Question: Do we want to group some of the object methods with that object?

Answer: we might. But (1) because PHP doesn't support either multiple inheritance nor dynamic object altering, this pattern will have very limited power (ie. other modules that want to add a method to that object will be back to the method($object) pattern), and (2) as a consequence, this has very little advantage except that the syntax $object->method() is slightly better looking that method($object).

Question: Does the field API support working on non-anonymous objects?

Answer: yes, it does. Fago's Entity API proves that. That said, I don't believe this has a lot of added-value.

Good feedback. What about

Good feedback. What about initialization functions that return stdClass objects with the core properties all in place and standardizing on them in the code? So, to create a new product type on the fly, no...

<?php
(object) array('type' = $product_type['type'], 'name' => '')
?>

but...

<?php
$product_type
= commerce_product_type_new('type_string');
?>

Another benefit

I recently wrote up an OOP version of products available at http://github.com/mattfarina/drupalcommerce/tree/oop/modules/product/

The reason for it is swappable entity storage. In the current setup you cannot have you entities be somewhere else, like a mangodb instance. The only swappable element is the loader.

By moving to OOP for the save and delete operations entities become swappable.

Question: Do we want to group

Question: Do we want to group some of the object methods with that object?
I'm not a fan of this idea. I have never done any php OO ( in fact - if it was not drupal i might have never got my hands on php) but in my experience, mostly java, i prefer to have plain domain objects and not tie any behavior to them that would implicitly tie them to any API. For CRUD, a widely used pattern is the Domain Access Objects, objects that take care of creating saving and deleting your domain objects. The reason is mostly about keeping your objects lightweight and being able to reuse them in a different context.

One case where it would've

One case where it would've been easily advantageous for an object to have methods in Ubercart was the shopping cart. Instead of an object with a ->total or ->itemCount method, we always had to loop through this random array of cart items to derive these totals. I suppose we can just add these as properties of the cart object, but then we'll also have to make sure these properties are updated any time something in the cart changes... which we just can't control without protected properties. So we end up having to have API functions that return the total item count or total value of items based on a cart object passed in as a parameter. Not the end of the world, but it sure would be nice to take advantage of classes, even as Drupal has done w/ the database API.