EDIT: download the module that encapsulates this behaviour and does a lot more besides: read this blog.
The new field structure in Drupal 7 is very clever but also quite irritating when you want to get data out of a field in an entity, so I have written this handy routine which extracts field data:
/**
* Returns field values as actual entities where possible,
* also allows selection of individual items to be returned
*/
function field_fetch_field_values($entity_type, $entity, $field_name, $get_delta = NULL, $get_key = NULL) {
$values = array();
if (isset($entity->$field_name) && !empty($entity->$field_name)) {
foreach (field_get_items($entity_type, $entity, $field_name) as $delta => $item) {
$value = $item;
$keys = array_keys($item);
if (count($keys)==1) {
$key = $keys[0];
switch ($key) {
case 'nid':
$value = array_shift(entity_load('node', array($item[$key])));
break;
case 'uid':
$value = array_shift(entity_load('user', array($item[$key])));
break;
case 'tid':
$value = array_shift(entity_load('taxonomy_term', array($item[$key])));
break;
case 'vid':
$value = array_shift(entity_load('taxonomy_vocabulary', array($item[$key])));
break;
case 'value':
$value = $item['value'];
break;
}
}
else {
if ($get_key && isset($item[$get_key])) {
$value = $item[$get_key];
}
elseif (array_key_exists('value', $item)) {
$value = isset($item['safe_value']) ? $item['safe_value'] : $item['value'];
}
}
$values[$delta] = $value;
}
}
if (is_numeric($get_delta)) {
return isset($values[$get_delta]) ? $values[$get_delta] : NULL;
}
return $values;
}
Use it like this:
$terms = field_fetch_field_values('node', $node, 'field_myterms');
Extracts all the tids from the field and returns the actual terms, not the tids.
$term = field_fetch_field_values('node', $node, 'field_myterms', 0);
Returns just the first term.
$summary = field_fetch_field_values('node', $node, 'body', 0, 'safe_summary');
Returns only the summary from the body field, but does not create a summary if there isn't one.
$info = field_fetch_field_values('node', $node, 'field_myfiles', 0);
If field_myfiles contains uploaded file data you get the full file info array back, in this example just the first one.
There are probably cleverer ways of doing this (and you could do multiple entity_loads to make it more efficient) but I find this works well enough for normal day-to-day use.
16 comments:
Nice work! it's redonculously frustrating that you can't get a value using a node id and a field name with the field api. Maybe there is but this is the best solution I've found so far.
One thing to note is that you must pass the function a node object returned from something like node_load(nid) and not an entry returned from entity_load("node",$nidArray)
Thanks for that - of course, now looking at this with a fresh eye there is the nasty redundancy that if you just want a single entry from a list of terms you'll get a whole bunch of unnecessary DB accesses.
I'll fix it some other time.
For people that want to query entities for certain data, it is useful to use EntityFieldQuery (http://api.drupal.org/api/drupal/includes--entity.inc/class/EntityFieldQuery/7).
For example, querying a taxonomy for a specific term, can be done this way:
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'taxonomy_term', '=')
->propertyCondition('vid', 2)
->fieldCondition('field_your_textfield', 'value', array('value to search for'), '=');
dpm($query->execute());
Thanks for the code :) I'm a newbie with this new Fields API stuff, and it's quite confusing to me still.
Using this I've been able to "see" an array that contains a field I want to render, but that's as far as I've got yet. I was wondering if you could show how to accomplish the following scenario (I think having even one functioning use-case within my own context should help me figure out how to apply it in general):
There's an image field on a taxonomy term which I'd like to render on a node which has that term applied to it. It's easy with the Manage Display tab on the Content Type to have the term name itself be displayed on the node, but I've not yet been able to get the fields "of" that term to also display and be properly rendered on the node.
Thanks in advance for your help!
There's no simple solution to this.
You could to take a look at Display Suite - this is my new BFF when it comes to customising displays. It now just needs to be able to add fields of attached entities (a la Search API). But you can add "DS Code" as cusotm fields which may help you to do this.
Hi you wrote a awesome module, but can you explain what I need to pass to $node variable?
$value = field_extract_value('node', $node, 'field_just_a_value');
For example I try to get content of field 'uc_catalog_image' from term 'brand'
Well, from what you say brand is a term not a node. So you use
field_extract_value('taxonomy_term', $brand)
You have to use the entity name with the entity itself so the code knows how it should be processed.
There are lots of examples on the project page.
Would this work with Drupal 6 too?
No, while CCK fields have a similar structure it doesn't include language.
I'm lost. I have a node reference I'm trying to extract:
$values = field_extract_values('node', $node, 'field_invitees');
I get the error:
Notice: Undefined variable: node in __lambda_func()
Not sure if this is a double post. If so, apologies.
Hi Drew
Sorry, been very busy recently (current project is a bit intense).
As I suspected when I saw it, that error message only occurs when using PHP's anonymous functions - which I don't use at all.
Which means the error is somewhere else. I can only assume that "field_invitees" is of a strange field type not supported by my module.
This function already exists in a much more powerful way in the entity API: entity_metadata_wrapper
For example:
$node = node_load(nid);
$node_wrapper = entity_metadata_wrapper('node', $node);
$your_field = $node_wrapper->your_field->value();
Enjoy!
Yes, it does exist in the entity wrapper functionality (I believe I said this in one of my other postings).
But, define "more powerful".
This is a quick and easy solution that works and has very little overhead.
It's "horses for courses". I use the entity wrapper when it's appropriate. Other times, I don't.
More powerful in that it's not limited to the core entities 'node', 'user', 'taxonomy_term', and 'taxonomy_vocabulary'. If you've added a custom entity or are using a module that creates its own entity, this function will not accomplish the goals it sets out to do.
If the goal is to return 'field values as actual entities', don't you think the entity api is exactly what the doctor ordered?
Also, I believe you'll get STRICT errors thrown by passing arguments by reference for array_shift.
Limited entity types? Not especially.
And yes, PHP 5.4 is more picky - I have a corrected version. But there are plenty of places in core and major 3rd party modules where that's a problem too.
Anyway what's your problem? It's well-behaved, extensible and developers don't have to use it if they don't want to.
You have my permission to ignore it.
I have no problem, I was just offering an alternative from a widely used and very powerful module, similar to what E31 posted earlier. I really meant no offense by it.
And you're right, I don't have to use your code. I recommend that people use entity_metadata_wrapper for this functionality.
Post a Comment