While helping a user in the forum I discovered some anomaly in the module. It's probably not a "bug" per se, but it's a nuisance we can easily get rid of.

SUMMARY:

When a node is rendered, the rendition of each field, including label and all, is saved in $node->content['field_somefield'][0]['#value']. However, users are accustomed to finding the essense in $node->field_somefield[0]['view'] as well. In fact, ConTemplate's default templates suggest exactly this --because there's no "easy" way to get the "pure" rendition otherwise; that is, without the label.

However, ComputedField doesn't pupulate the 'view' cell.

I'm attaching a patch. I simply removed the $op==view handling. It's my understanding that CCK fields nowadays seldom implement this $op.

CommentFileSizeAuthor
#11 cf.diff3.39 KBmooffie
#3 pomputed_field-populate-view.patch.txt729 bytessin
#1 cf.diff815 bytesmooffie

Comments

mooffie’s picture

StatusFileSize
new815 bytes

Ooops. Here it is

sin’s picture

Without 'view' it is impossible to calculate field on every node view due to caching. To do it and populate 'view' I use:

 case 'view':
      // Added 2007-11-07 by SDSheridan to solve non-compute on View.
      // 'load' above is not called on every view because of caching in cache_content table.
      _computed_field_compute_value($node, $field, $node_field);
      if ($field['display']) {
        $items = array();
        foreach ($node_field as $delta => $item) {
          $view = content_format($field, $item, 'default', $node);
          $items[$delta]['view'] = $view;
          // Populate 'view' unformatted value for theming and other modules
          $node_field[$delta]['view'] = $view;
        }
        return theme('field', $node, $field, $items, $teaser, $page);
      }
      break;
sin’s picture

StatusFileSize
new729 bytes

Here it is. I examined how content.module sets 'view', all we need to do is add $node_field[$delta]['view'] = ...
It works, I've got value without label in theme as $node->field_my_computed[0]['view']

karens’s picture

There's a simpler way to do this. I don't have access to create a patch right now, but but just change the 'view' function to the following so the value gets stored in $node_field instead of $items:

    case 'view':
      if ($field['display']) {
        foreach ($node_field as $delta => $item) {
          $node_field[$delta]['view'] = content_format($field, $item, 'default', $node);
        }
        return theme('field', $node, $field, $node_field, $teaser, $page);
      }

karens’s picture

As I look at this more, I think the better solution would be the patch at http://drupal.org/node/207443, which will accomplish the same thing with less code and more flexibility.

mooffie’s picture

Status: Needs review » Needs work

(Marking #207443: CF should allow for other formatters a duplicate of this.)

I'll summarize the progress so far:

Mark Fredrickson and I suggest to simply remove $op=='view' handling so that CCK does everyhting. This also has the advantage that various formatters can be applied to the field.

But Sergey (sin) finds a problem in our solution:

CCK caches loaded nodes (see content_load()). Now, a ComputedField --when it isn't stored in the DB-- calculates its value when it's being loaded. And without being loaded --due to the aforementioned caching-- calculation doesn't occur...

So Sergey's solution is to use $op=='view' as a place to trigger a calculation (that is, a call to _computed_field_compute_value()).

Questions:

  • Perhaps we could trigger this calculation in some other place instead? E.g,, ComputedField could implement hook_nodeapi($op=='view') and calculate the field there.
  • Perhaps we could simply sabotage CCK's caching the node? This has the advantage that a $node loaded by node_load() would instantly include the calculated field.
  • Note: Right now ComputedField has a property users probably like: The calculation shows in 'Preview' mode. That's because it's triggered in $op=='view' too. We should try to preserve this feature.
  • Any more ideas?
karens’s picture

Sorry I missed the beginning of the thread where you did propose this :)

CCK runs widget_invoke('process form values') before every preview, so we can put a calculation in that operation to fix the preview.

To trigger a calc on non-stored fields, I think the best idea would be to clear the node cache to force a new node to be created. Computed field's node_load should come before content module's node_load, so that should force CCK to get a fresh copy.

Then we can leave op='view' handled by CCK.

Would this work? Someone could play around and test that it does, but I think it should.

mooffie’s picture

CCK runs widget_invoke('process form values') before every preview
[...]
I think the best idea would be to clear the node cache to force a new node to be created.

Karen, these are two excellent ideas! I'll give it a shot next week.

As for clearing the cache: perhaps I can avoid this by making ComputedField heavier than Content, and then by returning the field form ComputedField's hook_nodeapi($op==load).

mooffie’s picture

Assigned: Unassigned » mooffie

(Assigning to myself. But I'll be away till monday, so, if somebody wants to handle this instead of me: be my guest, assign to yourself.)

mooffie’s picture

CCK runs widget_invoke('process form values') before every preview, so we can put a calculation in that operation to fix the preview.

There's a subtle problem with this idea: the $node the calculation will see is still that correspoding to the form. (It depends on the order the widget hooks are called.) So, for example, a calculation that inspects a Date field will see select boxes instead of pure data. A problem.

Karen, do you see a solution to this?

Note: Right now ComputedField has a property users probably like: The calculation shows in 'Preview' mode.

I erred here: the module has no such feature right now. This feature is introduced in Sergey's (comment #2) patch.

So it's not a regression to not have this 'Preview' feature right now.

mooffie’s picture

Assigned: mooffie » Unassigned
Status: Needs work » Needs review
StatusFileSize
new3.39 KB

Here's the patch.

It fixes two problems with the module:

  • We now have a 'view' slot, and various formatters can be applied to the field.
  • Non db-stored computed field are calculated anew whenever the node is loaded. The calculation occurs even if the node is cached by CCK.

Problems yet to be solved:

  • How to trigger a calculation on node preview? I explained in previous comment why the 'process form values' idea probably isn't a good one.

====

Some explanation on how I circumvented CCK's caching mechanism:

The original idea was to clear the cached fields, in hook_nodeapi('load'). Since I felt it'd be nice to have this cache, I simply return the caculated fields in hook_nodeapi('load'). There's one complication: the calculation may depend on other CCK fields, so we need to pull them in. It isn't an elegant solution, so I can revert to Karen's original "cache clearing" idea (which isn't elegant either, but to a lesser extent).

Raving’s picture

Subscribing. The posted patch in 11 seems to have fixed my issue @ http://drupal.org/node/246269 :)

mooffie’s picture

The posted patch in 11 seems to have fixed my issue @ http://drupal.org/node/246269

Yes, and that isn't accidental: "the calculation may depend on other CCK fields, so we need to pull them in."

sin’s picture

Status: Needs review » Needs work

#11 loads CCK field values from cache manually:

     // If might be that some computation involves other CCK fields.
     // So we pull them in.
     $cid = 'content:'. $node->nid .':'. $node->vid;
     if ($cached_cck_fields = cache_get($cid, 'cache_content')) {
       foreach (unserialize($cached_cck_fields->data) as $key => $value) {
         $node->$key = $value;
       } 
     }

It is unreliable because at any time cache_content can be cleared by developer or by some modules, which hapens rarely but is pretty legal. In this case calculation fails, so code needs work.

--------------
I'l try to summarize again...

Recalculate on preview feature was introduced by sdsheridan bugfix #134661: Recompute on every display not working?. This issue (titled "'view' cell not populated") is solved without 'view' op removal and do not block #134661: Recompute on every display not working? bugfix. But current feature #207443: CF should allow for other formatters implementation conflicts with current #134661: Recompute on every display not working? bugfix. I think #207443: CF should allow for other formatters is not a duplicate feature, it just touches the same fragment of code.

So we have 1 bug and 3 separate features sharing code:
1. Bug #134661: Recompute on every display not working?, current fix adds new feature to calculate on node preview too.
2. Feature #202384: 'view' cell not populated (current)
3. Feature #207443: CF should allow for other formatters
We might try to fix a bug and implement 3 features at once or fix a bug and decline some features. Final solution sould also calculate fields after CCK fields get loaded and do not break a new great feature committed to CF HEAD -- token support. Looks like a lot of work :)

mooffie’s picture

#11 loads CCK field values from cache manually

The purpose of my code is to make the following feature possible:

$node = node_load(123);
print $node->field_something[0]['value'];

Surprisingly, it's not possible to do this simple thing in current ComputedField (when the field is a non-db-stored one).

#11 loads CCK field values from cache manually

Because I don't see any other way to implement that feature.

When Drupal loads a node it asks all modules to contribute their additions. While this is happening each module doesn't see any others' additions[1]. That's why our ComputedField's computation doesn't see any other fields. The purpose of my code is to make the computation see the other fields. I know that piece of code is ugly, but it is ugly only to the extent that Drupal itself is ugly.

It is unreliable because at any time cache_content can be cleared by developer or by some modules

Give me an example, any example, of clearing cache_content.

footnotes:

[1] Note that CCK physically adds the fields to $node while it loads them --it doesn't wait for Drupal to do this-- so theoretically our computation should be able to see these fields[2] but this only happens when the node was not cached.

[2] in PHP5 only, because in PHP4 $node is effectively passed 'by value'.

mooffie’s picture

I'l try to summarize again...
[...]
So we have 1 bug and 3 separate features sharing code:
[...]

If I understand your summary correctly, the only thing you lament is that 'preview' doesn't show the computed value.

Looks like a lot of work :)

No, there isn't much work to do. People have done work on this module before, but it was in vain: no patch was commited in a year (one was commited in April, but, ironically, it was commited in error). So it's not very motivating. Perhaps the only course is to fork.

sin’s picture

>The purpose of my code is to make the following feature possible:
>$node = node_load(123);
>print $node->field_something[0]['value'];
>Because I don't see any other way to implement that feature.

Its not the reason to introduce unpredictable behaviour.

> ComputedField's computation doesn't see any other fields

I use patched version which recomputes on view and get your feature like this:

$node = node_load(123);
content_view($node);
print $node->field_something[0]['value'];

>Give me an example, any example, of clearing cache_content.

1. While I export/import/backup sites I never touch caching tables content. I rely on ability to restore a working site at any time with empty cache due to semantics of a cache -- all code using cache must fall back to general load procedure if cache is empty. Caching just speed up things, it do not stores, lets keep this simple principle.

2. I have a production setup -- 2 sites share CCK content types and nodes (2 sites run from one database with some tables shared and some separated with prefixes) but dynamically differently compute fields on view, so the cache tables is not shared between sites. When a node is modified from site 1 all this node cache on site 2 must be revalidated, so I clear modified node cache on site 2 when node is changed on site 1 and vice versa.

So you see, there are some issues in different usecases one can't observe. But we are a community, by "a lot of work" I meen not commiting patches with some workarounds working for me or for you and breaking other's people sites but fixing bugs and add new features without breaking old things people rely on. More testing and feedback needed. Thats a lot of work CF authors may not have time for.

>Perhaps the only course is to fork.
Actually I have a fork of CF for my own needs -- recalculate on preview and on every node view -- 2 lines change. I belive my fork exists not because Agileware do not commit patches but because noone have time to implement this feature and bugfix in a good quality patch.

sin’s picture

We have:
1. Bug #134661: Recompute on every display not working?
2. Feature calculate CF on node preview, added by 1.
3. Feature #202384: 'view' cell not populated (current)
4. Feature #207443: CF should allow for other formatters

Lets work on 3 here. #4 code by KarenS looks beautyfull. Can anyone create a patch and help to test?

mooffie’s picture

Sergey, let's put rhetoric aside and concentrate on the facts.

I provided a code that reads CCK's cache.

In your response you said there are cases that fail this scheme.

I asked you to give me examples for such cases.

You gave me two use cases.

However, contrary to what you believe, these two use cases don't fail my scheme.

I'll now explain my scheme so you see why it isn't 'unpredictable' as you think:

CCK writes to the cache in its hook_nodeapi('load').
Immediately after that my hook_nodeapi('load') kicks in and reads this cache.[1]

That's why your two use cases don't fail it: you can erase the cache as many times as you wish. My hook_nodeapi('load') will see the cached data, because it was just created by CCK's own hook_nodeapi('load').

I admited my code isn't beautiful, but I see no alternative. Drupal isn't perfect.[2]

#4 code by KarenS looks beautyfull.

Karen's code simply duplicates CCK's work then the field provides no 'view' handler. My patch, and Mark Fredrickson's, is better, because it simply removes this unneeded view handler. Karen herself admits that in #5.

footnotes:

[1] I didn't provide a hook_install that sets the module's weight because it'd be in an 'install' file, which is new so isn't in a patch.

[2] PHP itself isn't perfect. In PHP5 we could implement $node->field_whatever as an attribute with a 'getter' and thus solve the race problem, but this option isn't even on the horizon with Drupal's current architecture,

sin’s picture

Ok, thanks for explaination, now I see. Actually I like the idea to have all non db stored CF computed on hook_load, just don't like your implementation. I suggest to call content_load() or duplicate more CCK code to get reliable CCK fields loading.

Triskelion’s picture

I use a computed field to provide a link to ubercart to publish a node if it is unpublished. If I activate the field in the node page view, it works perfectly without database involvement. However, I don't display it on the node (for obvious reasons), but on a table view for the user showing his published and unpublished nodes. Therefore I have to use table storage to get this to work.

I used the cf.diff patch (It does not apply clean. I had to do most by hand.) It is almost there. The problem arises if a module sets or unsets $node->status using a query rather than through nodeapi. The computed field is not updated. An example is the admin > content list of nodes. If an administrator changes the 'published' status of a node through this interface, the computed field is not updated, and the old value of the computed field is retained in my view. It is necessary to edit the node, and submit it, to get the field to update in the view, which is an obvious hassle when dealing with many nodes!

Using the stored value in a view suffers from two vulnerabilities. First, this functions which bypass nodeapi, and, second, if the computed field uses values that are updated after the computed field is calculated, hook_load and hook_save have to be run again to insert the correct value in the computed field.

What would be really good, would be a computed field which could be included in a view without being attached to a node. It would allow us to use a php snippet to define a computed column in a view table, using values obtained in the view query. It would also require a <hidden> display property on the field display dropdown, because you might want to make fields available to the php snippet that you did not want to display in the view.

This is a great module if a computed field is needed in a node. The problem arises with views, and every solution I have seen is a bit of a kludge. Might I humbly suggest that the CCK computed field be used in the node, and that you might consider working on a Views computed field as a separate module?

mooffie’s picture

might consider working on a Views computed field as a separate module

I had this in mind a couple of times.

It would allow us to use a php snippet to define a computed column in a view table, using values obtained in the view query

A PHP snippet alone won't really work: People would want to filter and sort by this 'computed' field, and since Views works in the SQL level, PHP alone won't be very helpful. (But PHP is always good for theming.) A mix of SQL and tokens (only the subset that's valid to put after a SELECT), or maybe using PHP to generate temporary DB tables, is certainly possible.

However, from my experience in the support forums here I know that people always want "more"; Soon they'll want to show this 'computed' field on the node too. That's a problem.

Triskelion’s picture

A view is a summary of several logical rows, grouped according to conditions (filters). If I do an invoice, it may be looked at as an embedded view of several line items, each with a unique quantity and unit selling price. As a database designer, it is anathema to store a computed value in a table when the raw information is already there. For reporting, I would retrieve the quantity and price, and calculate the extended total.

Views does not implement this. Whether it is done within the query by defining a calculated field, or done as a php snippet using the raw information retrieved for each row, it is definitely something that would be useful. If the people wish to do a similar calculation in a node, they already have the cck computed field, which works best when the calculated information is not saved to a table.

Elementary separation of structure and function, static information and business process. The whole idea of a computed field is to not have to store it. Less is more.

If we continue to kludge together view compatibility for the cck module, we are going to end up with a bug-ridden maintenance nightmare. Sin is right, it is not elegant. Restrict the cck module to nodes (eliminating the table dependency), and create a similar calculated entity for views... Filtering would not be a problem; thats just a binary decision (display the row or not). Sorting might be a little more difficult, but if the field does not have to appear in nodes, I am sure that sorting could be done. Indeed, if it is a calculated query field, wrap it in an ORDER BY clause, and let the database engine do the sorting.

deekayen’s picture

Status: Needs work » Closed (won't fix)

5.x is unsupported. re-open for 6.x if applicable

kevinob11’s picture

Version: 5.x-1.2 » 6.x-1.x-dev
Priority: Normal » Major
Status: Closed (won't fix) » Active

I'm using 6.x-1.x-dev, though I also tested with 6.x-1.x-beta5.

My computed fields are not being printed anywhere. I don't see them on nodes, or in views. When I look at the node object I see that the value has been set correctly. When I check out the node object in a preprocess function I see that the "value" is set but the "view" is not. My fields themselves are very simple, just copying another field if it exists. I'm using a custom function rather than defining the php in the interface.

From what I can see in the standard drupal content-field.tpl.php (http://drupalcontrib.org/api/drupal/contributions--cck--theme--content-f...) the var that gets printed is the "view". I also noticed that the "empty" var for that item is being set to true. It seems like I must be missing something significant, as I'm pretty sure I have used computed field before.

Based on this it would seem as though I would have to preprocess each one of these fields and push them into the "view" variable as well as toggle the "empty" variable.

I'm hoping someone can point out some small thing I'm doing wrong so I can get things working!

Thanks,

Kevin

kevinob11’s picture

Status: Active » Closed (won't fix)

Well this is awkward...

I opened things back up today and the fields were displaying correctly. I'm going to close this up again I guess.