The new node (or entity) object layout

KarenS's picture

Here is what our node object looks like now:

stdClass Object
(
    [nid] => 13
    [type] => story
    [language] =>
    [uid] => 1
    [status] => 1
    [created] => 1202300504
    [changed] => 1202300759
    [comment] => 2
    [promote] => 1
    [moderate] => 0
    [sticky] => 0
    [tnid] => 0
    [translate] => 0
    [vid] => 27
    [revision_uid] => 1
    [title] => My new node
    [body] => Blah blah blah
    [teaser] => Blah blah blah
    [log] =>
    [revision_timestamp] => 1202300759
    [format] => 1
    [name] => admin
    [picture] =>
    [data] => a:0:{}
    [last_comment_timestamp] => 1202300504
    [last_comment_name] =>
    [comment_count] => 0
    [taxonomy] => Array
        (
            [1] => stdClass Object
                (
                    [tid] => 1
                    [vid] => 1
                    [name] => Art
                    [description] =>
                    [weight] => 0
                )

            [2] => stdClass Object
                (
                    [tid] => 2
                    [vid] => 1
                    [name] => Drupal
                    [description] =>
                    [weight] => 0
                )

        )

)

We have said we want all the fields on a node to start to prefix their names with the name of the module that put them there.

If we make all fields multiple, storing each in its own table, we should also combine related elements into a single field instead of making every single item on the current node into a separate field.

There is still a question about whether some of these items should become multiple values, but the following is a general representation of what the new object might look like.

stdClass Object
(
    [node_nid] => 13
    [node_vid] => 27
    [node_tnid] => 0
    [node_type] => story
    [node_title] => My new node
    [node_created] => 1202300504
    [node_changed] => 1202300759
    [node_settings] => array
      (
        [0] => array
          (
            [status] => 1
            [promote] => 1
            [moderate] => 0
            [sticky] => 0
            [language] =>
            [translate] => 0

          )
        )
      )
    [node_body] => array
      (
        [0] => array
          (
            [body] => 'Blah blah blah'
            [teaser] => 'Blah'
            [format] => 1
            [log] =>
            [revision_timestamp] => 1202300759
          )
      )
    [user_data] => array
      (
        [0] => array
          (
            [uid] => 1
            [name] => 'admin'
            [picture] =>
            [data] => a:0:{}
          }
      )
    [comment_data] => array
      (
        [0] => array
          (
            [last_comment_timestamp] => 1202300504
            [last_comment_name] =>
            [allow_comment] => 2
            [comment_count] => 0
          }
      )
    [taxonomy] => Array
        (
            [1] => array
                (
                    [vid] => 1
                    [tid] => 1
                    [name] => Art
                    [description] =>
                    [weight] => 0
                )

            [2] => array
                (
                    [vid] => 1
                    [tid] => 2
                    [name] => Drupal
                    [description] =>
                    [weight] => 0
                )

        )

)

Retrieving values from this new format will be more complex (no more $node->updated or $node->body), so we will want a helper function to make it easy to retrieve either an entire multi-column field array, a single delta value of that field, or an individual delta and column.

<?php
/**
* Helper function to retrieve a field's data from a node.
*/
node_field_value($node, $field_name = 'node_body', $delta = NULL, $column = NULL) {
  if (
$null === NULL) {
    return
$node->$field_name;
  }
  elseif (empty(
$column)) {
    return
$node->$field_name[$delta];
  }
  else {
    return
$node->$field_name[$delta][$column];
  }
}
?>

Comments

Nice!

nedjo's picture

This helps bring things into focus.

You said: We have said we

bjaspan's picture

You said:

We have said we want all the fields on a node to start to prefix their names with the name of the module that put them there.

We definitely want to organize the node fields somehow but I wonder if "by module" is right and "with a name prefix" is right. If I have a text field named description, today we get $node->field_description. Is $node->text_description what we want? I do not see why the module name should be in there. What if I change the description to provided by the wysiwyg_editor_field.module but that still provides the same ['value'] and ['format'] items? (Maybe that would be a new formatter on a text field but I think the point still holds.)

A year ago in some schema api posts I suggested that node fields be organized by table they came from: $node->content[0] = data from the comment table. I now also think that is wrong. Why should the table names be exposed? Not all fields will even have tables.

I'm thinking the right way to organize is node fields (in the PHP stdClass object member variables sense) is by field (currently in the CCK sense).

So, if I add a 'description' field from text.module to a node type, that node gets $node->description. If I use amazon_related_products_field.module (uses Amazon API which takes node body and returns relevant products) to add a field "related_products" to a node type, that node gets $node->related_products.

Every field has a unique name.

You said:

If we make all fields multiple, storing each in its own table, we should also combine related elements into a single field instead of making every single item on the current node into a separate field.

I think this is a very good idea, just the way you showed [node_settings] as a single field; we do not need status, promoted, etc. to be separated versioned.

Would it be accurate to say

Chris Johnson's picture

Would it be accurate to say the node fields are attributes of the node object, and that modules can add additional attributes (e.g. "related_products" from Barry's post)? Then, if that's true, it seems like this might be a question of composition in the modeling sense. Or are we talking an aggregate or something else (meaning I've probably missed the boat)?

beware top-level namespace collisions

pwolanin's picture

I don't think you can rely on the field names being unique for the entire node object, especially in the transition period. And what if I want a field named 'uid'? Are you going to make a big list of blacklisted field names? That seems fragile.

At the least, something like:

$node->fields['description'];

and
$node->fields['related_products'];

We already have this problem.

bjaspan's picture

We already have this exact namespace problem. Nothing prevents modules from overriding each other's data fields. From node_load:

<?php
   
if ($extra = node_invoke($node, 'load')) {
      foreach (
$extra as $key => $value) {
       
$node->$key = $value;
      }
    }

    if (
$extra = node_invoke_nodeapi($node, 'load')) {
      foreach (
$extra as $key => $value) {
       
$node->$key = $value;
      }
    }
?>

So in fact we already have a big list of blacklisted names which is "everything used by core or any other contrib module." This is why we call nodes a dumping ground. ;-)

Field names will be unique, enforced the the field API, so modules using fields to attach data to nodes will be safe from this problem (or they will fail at install/field creation time). Accessing a field will already require a lot of syntax, e.g.

<?php
$node
->fieldname[0]['value']
?>
so making it even longer e.g.
<?php
$node
->fields['fieldname'][0]['value']
?>
will result in some pretty annoying code to type, so I'd vote against it.

OTOH, I think we have not yet fully fleshed out how the "core fields" of an object, e.g. for nodes, nid, uid, created, etc., are treated. Are they really going to be fields in the current CCK sense? We discussed the difference between fields (like CCK, versioned, can be multiple, etc.) and properties (like nid, uid, created, not versioned, can't be multiple). If we go with that model, clearly we do not want to say "no module can create a field whose name is the same as a property of any object" because we do not know what all those property names might be. So maybe a

<?php
$node
->fields['fieldname']
?>
solution will be required. Don't know yet.

I think the intended

yched's picture

I think the intended 'module' prefix was not the name of the module defining the field type ('text', 'nodereference', 'date'...), but the name of the module that said 'oh, and please add a 'start date' field to 'conference' node type (that currently is CCK 'UI', and could be event.module in the future)

We need to agree on some unambiguous terms here.
If event.module sets a date field on 'conference' nodes, event.module 'defines' the field (is the 'owner'), while date.module 'implements' the field type (is the 'implementor').
Better suggestions welcome by english-speaking people :-)

Just to be clear on the

yched's picture

Just to be clear on the field names clashing issue, main problem is this :
We want to make it possible for modules to add fields to node types at install time. We can assume it's the job of module authors not to add fields with a name used by another module. Not really diferent from what we have now - this probably translates into 'prefix with your module's name', as a 'best practice' rule of thumb that might not need to be enforced in the code.

But site admins still can add their own custom fields through CCK UI, and a module can't make assumptions about the fields that are already there when they are uinstalled.
Those custom fields will probably need to use an enforced prefix, like 'custom_' or something.

Fields in Core

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: