Jump to:
| Project: | Examples for Developers |
| Version: | 8.x-1.x-dev |
| Component: | Field Example |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | active |
Issue Summary
I'm struggling on extending a File.
Let's say you want to add metadata (like a "description", ...) to a File (a simple text field) :
* file_save(), file_load(), ... to store/load/... the "description"
* URL/file/$fid to show the description
* URL/file/$fid/edit to offer a text input.
* URL/admin/content/file to show a "description" colum
We start with a simple field like documented in :
http://api.drupal.org/api/examples/field_example--field_example.module/g...
<?php
function mymodule_schema() {
$schema = array();
$schema['mymodule'] = array(
'description' => 'The table for the descriptions of files.',
'fields' => array(
'fid' => array(
'description' => 'The file id.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'description' => array(
'description' => 'its description.',
'type' => 'varchar',
'length' => 255,
'not null' => FALSE
),
),
'primary key' => array('fid'),
);
return $schema;
}
?>nothing new here, but then the field must be handled when a File is manipulated.
Should we care about widget hooks as we add a simple text field ? (I currently don't think so)
The best example so far seems to be the following, for D6 :
http://drupalcode.org/project/filefield.git/blob/refs/heads/6.x-3.x:/fil...
(my failure to understand file_entity and media codebase, because these modules add abstract concepts while you'd except them to add samples implementation your can learn from)
then let's do it simple, and implement hook_file_load in order to test file_load($existig_nid) :
<?php
function mymodule_file_load($files) {
$datas = db_query("SELECT * FROM {mymodule} WHERE fid IN (:fid)",
array(':fid' => array_keys($files)) )->fetchAllKeyed();
if ($datas) {
foreach($datas as $k => $data) {
$data['description'] = isset($data['description']) ? $data['description'] : array();
$files[$k]->data = isset($files[$k]->data) ? array_merge($files[$k]->data, $data) : $data;
}
}
?>so file_load() works (but is this Drupal 7 compliant ? not sure, but what's the Drupal7 way of doing such thing ?)
Then let's use this as hook_file_update($file) in order to test file_save($nid):
<?php
function mymodule_file_update($file) {
if (!empty($file->fid)) {
mymodule_file_delete($file);
mymodule_file_insert($file);
}
}
?>1) hook_file_delete($file) :
<?php
function mymodule_file_delete($file) {
db_query('DELETE FROM {mymodule} WHERE fid = :fid', array(':fid' => $file->fid) );
}
?>2) so we come to hook_file_insert($file) but it becomes hard to figure out how it should looks like :
<?php
function mymodule_file_insert($file) {
// seems clean but what additional hook is needed to make it work ?
//field_attach_insert('description', $file); ?
// below is what is used by the file_entity module, some others uses ctools_export(), ...
// but it is hardly understandable
//field_attach_insert('file', $file); ?
if (!empty($file->fid)) {
$record = array('description' => field_get_items("node", $file, "field_description"),
'fid' => $file->fid);
drupal_write_record('mymodule', $record);
}
}
?>but it does not work well because you'd have to use :
<?php
$a = file_load(<an existing fid>);
$a->field_description[0] = "new description";
file_save($a);
?><?php
$a = file_load(<an existing fid>);
$a->field_description[LANGUAGE_NONE][0] = "new description";
file_save($a);
?>(maybe a problem of using
field_get_items() ?)
Anyway we can still hope we may reach the end, but then comes the GUI.
hook names become more and more difficult to guess, effects to test; drush cc, F5, ... welcome in hell.
hours and hours after I'm coming here to say that either the documentation is missing this point, either feel free to switch this as a "support request".
hook_theme is (hopefully) fired :
<?php
function mymodule_theme() {
//var_dump(xdebug_get_function_stack()); // uninformative
return array(
'mymodule_description' => array(
'arguments' => array('description' => NULL)
)
}
?>but these are not, URL/file/$fid/edit is still unmodified :function theme_mymodule_description($description)function theme_mymodule_field_description($description) [ insert here the tons of function names I try to guess since I tried to use Drupal ]
why ?? where should I find why they are not fired ? how can I know which hooks are fired when this page is accessed ?
and of course I'll probably have 3 or more hooks to implements(think of [a-z_]*form_alter[a-z_]*, [a-z_]*form_submit[a-z_]*, ...).
3 hooks meaning dozens of function names to test hoping that one of them will be fired (after clearing cache each time)
Last year I already had this feeling that I was bruteforcing rather than coding with Drupal and losing dozens of hours because I can't find a good example (what is a real problem when the code is not clear).
[ the only module which, I think, may have helped in such a context is visualize_backtrace() which is no more supported and whose memory footprint (to analyse Drupal hook stack) easily exceed what is admissible ]
As I won't let my angriness affect this bug report much (more), let's just mention http://www.mediawiki.org/wiki/Manual:Hooks, and look at MW code... everything is said.
I'm posting here because I still hope that knowledgable people may help a lot by providing good examples about D7 fields attachment and handling (especially in the example module).
To be complete, I've a simple use case: adding a "filesystem old path" field to files migrated from a previous website to Drupal.
Comments
#1
I guess the best place for this might be the Examples module? If not, then probably the on-line documentation? Or where were you thinking this documentation should be put, assuming someone figures out the answer to your question and wants to write this documentation?
#2
for such things, the examples.module is probably the first place people look to (around the Field API chapter).
I don't know how important it is to reassign to another component/project (as its more about the knowledge than about the place code will be committed), but it's an important task.
#3
Reassigning to the Examples project.
#4
Just want to point to this documentation: http://drupal.org/node/443536
I think an example of how to attach a field to an arbitrary bundle in code is a good idea. We might even have it use example_field.
#5
I hadn't gone through https://drupal.org/node/474420 which is just fantastic !
There I even learnt things from simple comments like
// Fields created through Field UI are prefixed with 'field_'I understood (or rather "realized") that 'text', 'audio' where bundles of the file entity, rather than considering File from a 'node entity point of view' and all went fluently.
I saw the "description" text-input appearing in
URL/file/$fid/edit! then I got a big push of endorphin, proportioned to the liters of sweat my brain left on the keyboard these last days.At the same time I also realized the logic behind EntityFieldQuery.
So, I think that before closing as "RTFM" :
<?phpfield_info_instance("node", "mymodule_description", "file");
// versus
field_info_instance("file", "mymodule_description", "audio");
?>
<?php$field_name = "mymodule_description";
$query->entityCondition("entity_type", "file")->entityCondition("bundle", array("video", "text"), 'IN')->fieldCondition($field_name, "value", "description for file 42")->execute();
?>
<?php$a = file_load(42);
$a->mymodule_description[LANGUAGE_NONE][0] = "description for file 42";
// ? or $a->mymodule_description[LANGUAGE_NONE] = "description for file 42";
// ? or $a->mymodule_description = "description for file 42";
file_save($a);
?>
but I don't worry anymore and I'm confident I'll find the explanation soon, maybe hook_file_insert or field_attach_insert() are the way to go in D7 instead of manipulating object attributes manually ?
thx for having replied and pointed me to the right (and after all quite obvious) place, I still have a long road ahead.
#6
Quoth: "So, I think that before closing as "RTFM"...."
Nono... That's not how it works for the Examples project. :-) There's also a request for a FieldQuery example #916776: Add an EntityFieldQuery example.
Anyway, any code you'd like to contribute would be appreciated, and if not, those comments help a lot.
#7
I'm back here with some other confusing things about the Field API :
The introduction about the Field API [1] states that:
There one dramatically missing word : "Field Type" !
Taken from hook_field_info [2], we can see that it defines a Field Type, like Date creates a datetime field type.
That's confusing that hook_field_info create a field type which IHMO should be called hook_datatype
or so as it's related to the schema. (see below about inconsistency)
But looking at
field_example_field_info[3] I can't see it does clearly. It defines a widget, a formatter and a labelbut it does not state which "type" of data it stores. It leaves this to
field_example_field_schemain the .install file.So its still difficult to understanding what's a field. As it's a sensible subject, each page of documentation should
not forget to state how/what things are : in-code, in-database, abstract model or implementation (of something).
I still can't find an image illustrating all this, that's what make very hard to understand Drupal's big picture.
The terms primitive datatype, object, class, struct, ... are far less ambiguous and should be preferred.
As we are talking about datatype it would be helpful to learn for the semantics of typed language.
field type: primitive type (int, varchar) (those are declared within
hook_field_info())field: encapsulated primitive type like :
(lives in database only (not in-code) and exists after field_create_field() invocation)
struct fieldX {int i;
struct *metadata_about_i; // like formatters and widgets
}
field-instance: encapsulated type bound to a given entity and a given bundle like:
(live in database only (not in-code) and exists after field_create_instance() invocation)
struct FieldInstanceX {struct fieldX x; // fieldX === field_name
char * (of entity name) e;
char * (of bundle name) b;
}
bundle: fieldable entity like:
struct bundleB {struct FieldInstanceX a; // FieldInstanceX
struct FieldInstanceY b;
}
The most obvious sign of semantics inconsistencies is that field types are created with
hook_info_fieldbut listed withfield_info_field_types.While
field_info_fields()list fields which have no corresponding hook because they don't leave in-code but are created once in an install file usingfield_create_field.And the above applies to field's instances.
Taken from [1] in the $field description :
Search hook_field_info [2] for "type", "primitive", "data [type]", ... you won't find any of them.
On another side:
field_info_bundles()list the bundles (optionally: of a given entity), so why notentity_bundles()orentity_list_bundles()? [4]As there's nothing related to fields here.
Moreover why not write "entity name" (KISS) instead of "entity type" which is something I can't even think about.
Note: I really can't understand why this Field API does not give place for fields to leave (also) in-code, IHMO that's the biggest pitfall of this API. ($field aims to setup much more useful, and mostly static, settings into $field from [1] than in the field type definition). There is even a hook for pseudo-fields (*_extra_fields)!
Note: there are inconsistencies in bool handling, no_ui, locked, translatable use either boolean or integer while, hopefully, the core often uses empty($val).
There should really exist one full documentation page per Drupal data semantic like: /doc/bundle: what it is, which API to call to show, list, add, delete, ...
[1] http://api.drupal.org/api/drupal/modules--field--field.module/group/field/7
[2] http://api.drupal.org/api/drupal/modules--field--field.api.php/function/...
[3] http://api.drupal.org/api/examples/field_example--field_example.module/f...
[4] http://api.drupal.org/api/drupal/modules--field--field.info.inc/function...
PS: which shorttag to link to a 7 API function ?
#8
@drzraf:
Quote: ""Field types are defined by modules that implement hook_field_info()." Search hook_field_info [2] for "type", "primitive", "data [type]", ... you won't find any of them."
The keys of the info array are the machine names of the fields (or 'field types'). Drupal then uses these keys in invoking hooks like hook_field_schema().
Since it defines a few different types of fields, text_field_into() is a good example of this: http://api.drupal.org/api/drupal/modules--field--modules--text--text.mod... and subsequently text_field_schema(): http://api.drupal.org/api/drupal/modules--field--modules--text--text.ins...
The documentation for field_example.module lays out the bare minimum your module needs to implement a field: http://api.drupal.org/api/examples/field_example--field_example.module/g...
Also, the best way to understand how these concepts relate is to create some complex content types in Drupal, using the user interface to attach fields to nodes.
Drupal documentation is written by volunteers at all levels of expertise, so there are two options: 1) be forgiving, 2) submit a patch. :-)
#9
variable:
int A;int is the variable type, A is the variable name.entity: file entity: file is either the entity type or the entity name (machine name), but actually you'll
never write
file entity A;so its unnecessary complexity to use type which imply that a the given entity is abstractwhile in fact it is already a kind of implementation.
bundle:
video bundle A;same as above,video is an implementation, so its rather an bundle name rather than a bundle type.
It makes more sense to write:
fieldable file entity video; // video is the bundle machine-namethan
bundle video; // where video would be the bundle "type"field: description or term reference are fields (let's forget about instance for now),
so "description" and "term reference" are field machine names.
But
hook_field_info()implements the field type as intext_field description;So I still think that some of the current API semantics (and documentation) are inconsistent [3].
As a general documentation guideline, "machine name" should be used instead of "type" when possible.
But there is one sentence I disagree with you (and subsequently I disagree with [1]):
It would rather be defines a field type (what is highly bound to the storage backend).
So far in the whole example module there is one field implementation, it's there [2] because that's
the only data structure passed to the (only) call to
field_create_field().About the big picture understanding, a graph is still missing to me. Good documentation comes from good examples, API documentation and visualization (image) of relations when possible (especially when stuff becomes so complex).
About patch (or forgiveness) there exists
3) preliminary discuss about issues/features, especially since I'm new to Drupal I'm not reluctant to send patch but I obviously want to ensure they would solve an (existing) issue on which there's consensus.[1] http://api.drupal.org/api/examples/field_example--field_example.module/g...
[2] http://api.drupal.org/api/examples/node_example--node_example.install/fu...
[3] PS: finally found #1374116: Move bundle CRUD API out of Field API and
#1380720: Rename various entity terms to be more intuitive where some part of the above comments should go.