This draft patch introduces a common set of methods for saving, loading, and deleting Drupal objects (nodes, users, taxonomy terms, etc.) and also a new hook, hook_drupal()
, to be used with all object types, supplementing (but not replacing) the existing set of hook_user()
, hook_nodeapi()
, etc.
Background
On the dev list recently we've discussed a longstanding desire: to bring consistency to the various Drupal object types (node, term, user, etc.) and at the same time make it possible to attach behaviours to all object types at once (rather than having to do each as a separate hook).
Several creative approaches have been suggested. They all have at their core the creation of a common set of methods that all object types can extend. From there several different proposal emerge. Some want to introduce a new global object table, so all objects have a single unique identifier. Some ideas involve relatively minor changes, while others propose radical restructuring of our existing code.
Approach
We could and should continue discussing possible approaches. But it seems most practical to start with implementing the minimum we've agreed on so far: a common, consistent set of methods for all object types. With that implemented, we'll be in a better position to see what further restructuring we need. It will also put some powerful immediate benefits of a common API into fairly easy reach, e.g., converting node_access to be a general object access system.
Specifics:
- Introduce a set of object handling functions in common.inc:
drupal_load()
, calls the appropriate $type's method, e.g.,node_load()
.drupal_save()
.drupal_delete()
.drupal_invoke()
, used to invoke a type-specific method, e.g.,node_load()
. This is called only by thedrupal_load()
,drupal_save()
, anddrupal_delete()
methods. In other words, we retain our existing _load, _save, and _delete functions (for nodes, users, etc.), but they're now called not directly but via the general drupal_ calls.drupal_invoke_drupal()
. This calls the general hook_drupal(), which can be implemented for all object types. Our old friendshook_user()
still exist, and can still be implemented separately. In fact, doing so is more efficient if the desired action applies to only one object type, e.g., node. They are not, however, invoked directly. Rather, callingdrupal_invoke_drupal()
invokeshook_drupal()
and the type-specific hook, e.g.,hook_user()
.
- Details:
hook_nodeapi()
is renamed tohook_node()
for consistency.- Order of arguments is reordered for consistency and for passing by reference. Specifically, the $op argument comes before the $object being passed in
hook_drupal()
and therefore inhook_node()
etc. - Existing object functions are renamed for consistency, e.g.,
_comment_load()
becomescomment_load()
. - The existing _save and _delete return values
SAVE_NEW
,SAVE_UPDATED
, andSAVE_DELETED
are returned for all object types (e.g., they were used previously for terms but not for nodes). - Taxonomy vocabulary and term object handling is pulled into two include files, vocabulary.inc and term.inc, which have function names without the 'taxonomy', e.g.,
vocabulary_save()
. hook_taxonomy()
is dropped.hook_vocabulary()
andhook_term()
implementations are possible, but in most cases it will be simpler to changehook_taxonomy()
implementations intohook_drupal()
implementations.- The existing function
drupal_load()
is renamed todrupal_load_file()
, since we introduce a newdrupal_load()
.
Remaining issues
Many, no doubt!
This is a roughed-in and untested patch. I haven't yet upgraded my test environent to MySQL 4.1 so I haven't been able to test at all yet. I'll do so soon and post revisions. It's certainly broken in places.
One I'm aware of is passing by reference. In this draft I've used only a single argument passed by reference in drupal_invoke_drupal()
, where hook_drupal()
and the other object hooks (hook_user()
, etc.) are invoked. However, at least one existing hook, hook_user()
, has two arguments passed by reference in at least some hook implementations. I'll need to look more closely at these to see if it's possible to get this down to a single argument passed by reference. If not, it'd be messy, since arguments passed by reference can't have defaults. We'd need to pass in arguments for all calls to drupal_invoke_drupal()
, even though in most cases they're not needed.
Also, a number of inconsistencies are not addressed here. For example, though
I'd appreciate feedback on the general approach and the specific implementation. Is this where we want to be going as a first step to the aims outlined above?
Comment | File | Size | Author |
---|---|---|---|
#51 | dataapi.patch | 71.53 KB | RobLoach |
#27 | drupal-dataapi_2.patch | 83.54 KB | nedjo |
#25 | drupal-dataapi_1.patch | 84.44 KB | nedjo |
#21 | drupal-dataapi_0.patch | 80.72 KB | nedjo |
#12 | drupal-dataapi.patch | 79 KB | nedjo |
Comments
Comment #1
forngren CreditAttribution: forngren commentedSubscribe.
Comment #2
pwolanin CreditAttribution: pwolanin commentedsee: http://drupal.org/node/79684#comment-184791
I think that hook_drupal() is really non-obvious (-1). hook_objectapi_X, hook_objects_X, hook_entity_X, something!
If we're going to do this, hook_nodeapi, hook_user, etc. should be abolished- a make the API consistent, and as simple as possible.
For this API hook, I'd also rather see a sparate function for each op (per the link above), which mean that the only switch statement in each function is a switch on the object class (need to standardize terminology here).
Comment #3
nedjoAgreed, it's probably not critical at this point to agree on final terminology but 'entity' might be better until we do.
Thanks for linking back to the CRUD issue, I hadn't seen the promising contributions there in the last few days.
I've updated that issue with a reference to this one, and a possible strategy of completing this patch as a step towards a larger API reworking.
Comment #4
webchickI think this type of approach will definitely work as an interim step to a totally abstracted hook_everything. Though if we can do it in one step... :)
-1 to arguments like $a3 and $a4. I was stunned and shocked that that got into hook_nodeapi. These are totally non-intuitive, and worse, aren't PHPDoced in the actual functions. Why do we need $a3 and $a4 when we have $args as an array of N depth? Is this for compatibility with nodeapi? If so, nodeapi should be changed to take the more generic $args parameter, imo.
$param is never used in the function body, so this isn't adding any flexibility... it's also PHPdoced as $args, not $param. One of these is a typo...
General question: would it reduce the logic required if we passed $arrays rather than $objects around? Everything comes in and out of forms as arrays.
Sorry for the brevity; will try and review more thoroughly later...
Comment #5
nedjoHere's a refreshed patch with at least some of the more glaring errors fixed.
Changes in this patch version:
drupal_invoke_drupal()
changed to the more intuitivedrupal_invoke_entity()
.drupal_load()
.drupal_entity_include
. This allows custom object handling methods, comparable to the approach withdrupal_mail()
which allows the registration of a custom mail handler.Since this patch changes loading, saving, updating, and deleting, as well as all object API hooks (
hook_user()
,hook_nodeapi()
,hook_taxonomy()
, etc.), there's a lot of room for problems.I've installed this on a new site and most things seem to work--create, update, and delete nodes, terms, users. I added a node with a path and that was correctly saved, indicating that the new
hook_entity()
is working as a wrapper for existing hook implementations (in this case, path.module'spath_nodeapi()
, renamed topath_node()
).One error I found was that the taxonomy term select was broken on node edit--no terms were displayed as options. There will be more such problems.
Well, sure, we could use
func_get_args()
to read in these arguments, thenarray_shift()
off the first ones as needed (when we need to pass some arguments by reference, or when there are indeed consistent arguments used across all object types).The main issue here is that in this patch we're not - yet - making the larger changes that would be needed to get all object types to have the same set of arguments.
The biggest problem looks to be user, which passes around that extra argument--the array of user properties that's used in addition to the user object.
But, again, in this patch so far I'm not wading into reworking everything. Yes, we could and should make user handling more consistent with node and comment. Yes, we could and should allow term loading to feed in an array of parameters to match like we do with users and nodes. Yes, we could and should be consistent in our choice of array vs. object data type.
I'm hoping we can tackle those one by one in follow up work after making the minimal changes to achieve the primary aim here: a single set of object handling methods and a single object hook.
Any testing, comments, or reviews would be greatly appreaciated.
Comment #6
webchickNo patch, nedjo. :(
Comment #7
nedjoThanks, with attachment this time :)
Comment #8
pwolanin CreditAttribution: pwolanin commentedI still feel that we'd be btter served by breaking out the object hook into a set of hooks: hook_entity_X, where X = load, insert, update, prepare, submit, view, etc- essentially the full list from hook_nodeapi().
this can also help to avoid the $a3, 4a4 problem, since you can more clearly define what the parameters means for each X!
Comment #9
nedjoI can see the point, but I don't see the strong need that would justify introducing a plethora of new hooks (most of them very similar to each other). I think our best way forward is to make these changes in steps, doing the minimum at a time. Keep in mind that there are many $op arguments used at present for the object hooks, much more than just the basic load, update, insert, delete, so we might end up with 8 or 10 or more new hooks. It would be better to incrementally reduce the need for all these $op arguments, and then consider whether we can/should separate out to separate hooks.
In general this patch would need a lot of community review and energy. I'm not confident enough in the details of the approach to press on without some informed feedback and critical review, so I'm going to leave it where it is until if and when it gets the quality of review that would allow us to move forward--or abandon this approach for a better one.
I've opened a new issue, http://drupal.org/node/118345, focusing on user methods, that could be tackled separately to make this job much easier.
Comment #10
pwolanin CreditAttribution: pwolanin commentedI turned my sandbox code (mentioned above) into a working module: http://drupal.org/project/object_driver
Some of the features implicit in the original (non-working) code have been cut, but perhaps this will at least be useful and/or provoke more discussion.
Comment #11
ray007 CreditAttribution: ray007 commentedsubscribing
Comment #12
nedjoHere's a refreshed patch. It needs testing and work, but it's ready for review.
With a schema API nearly ready, http://drupal.org/node/144765, we're opening the door to a much improved Data API.
Most of that improvement, likely, will happen after Drupal 6. This patch is intended only to take some preliminary steps.
Notably:
* Introduce a single set of methods for saving/updating, deleting, and loading data objects.
* Introduce a hook_dataapi that can be used to respond to these operations for all data objects.
* Enable the substitution of a custom set of object handlers via an override of the dataapi.inc file. This is designed to enable the development of contributed solutions.
Changes from the previous patch:
* changed 'entity' to 'dataapi', more descriptive.
* removed user handling. we'd need to write http://drupal.org/node/118345 before being able to integrate users.
(I'm having diff challenges, please report if the patch doesn't apply for you.)
Comment #13
kika CreditAttribution: kika commentedJust reading the patch:
1. _selete looks like typo
2. why term.inc and vocabulary.inc are created? It is a good thing, yes, but why part of this patch - can not it be handled independently, making this particular patch slimmer?
Comment #14
moshe weitzman CreditAttribution: moshe weitzman commentedsubscribe
Comment #15
Wim LeersSubscribing.
Comment #16
bonobo CreditAttribution: bonobo commentedsubscribe
Comment #17
nedjo@kika, thanks for the typo note, I'll roll a new patch soon.
The term and vocabulary methods are needed for consistency--all object methods are in the form e.g.
objectname_load()
. They could be included directly in taxonomy.module, they just seemed to me to make more sense as includes.These changes are half-steps; they increase consistency, but leave most of the variability of the current methods unaddressed. In http://drupal.org/node/145684 I've tried to sketch out some next steps. It only makes sense to take the first steps now if have an idea of where we want to go next.
Comment #18
webchicknedjo, while you're re-rolling, the vocabulary.inc hunk fails for me:
patch: **** malformed patch at line 2063: --- modules/taxonomy/vocabulary.inc
(and subscribing :))
Comment #19
bjaspan CreditAttribution: bjaspan commentedsubscribe
Comment #20
criznach CreditAttribution: criznach commentedThis is killer stuff guys and gals. I'll be watching this API for sure.
Comment #21
nedjoRerolling the patch. Includes a few small error fixes.
Comment #22
drewish CreditAttribution: drewish commentedin common.inc:
it seems a little wonky to be calling variable_get('drupal_dataapi_include', '') three times. why not put the value into a variable? trying to keep it out of the global scope?
otherwise i'm pretty excited by what this will allow, and it'll take care of one of my pet peeves: the lack of a hook_delete_revision
Comment #23
webchickFunctionality test:
1) I can create vocabularies, but I cannot edit them, add terms to them, or list their terms... I always get redirected to the overview page. Probably something isn't quite wired correctly after the vocabulary.inc/term.inc split, because adding/editing forums and forum containers works fine.
2) LOTS of errors/warnings when previewing a comment. The trouble seems to start with:
Sorry, gotta run to supper for now. :(
Comment #24
nedjoThanks, I'll get working on these issues, and more testing.
Comment #25
nedjoHere's an update with a few fixes.
The vocabulary and term issue has to do with our new menu system. We feed in menu items with e.g. %taxonomy_vocabulary as an argument, which is interpreted as a load function,
taxonomy_vocabulary_load()
. I've replaced the occurrences of %taxonomy_vocabulary with %vocabulary in the vague hope that that might triggervocabulary_load()
, but no go in my testing. Hints anyone?Some of the errors in comment preview came from a usage of $status that I missed in
comment_save()
, now fixed. But there are some remaining, suggesting that certain form values are not available in validation. I haven't determined why.This patch certainly needs plenty of further work and even basic testing. Leaving the status at 'needs review' therefore seems inappropriate. But in some ways the remaining issues are details--important ones, yes, but solvable. What's equally important at this point is, is this a general path we want to be taking? So I'm leaving it as needs review because that question remains open.
Comment #26
pwolanin CreditAttribution: pwolanin commented@nejo - try activating or de-activating a module to force a menu_rebuild()
Comment #27
nedjoRefreshed patch with some fixes and improvements.
The vocabulary paths are now working (vocabulary editing, term adding).
I went through the various dataapi.inc methods and eliminated the hated $a3, $a4, $a5 arguments. Turned out most of them were unneeded. The only change I had to make to this was that, for node validation,
hook_nodeapi()
used to pass the $form array. This wasn't used anywhere in core. I've dropped it, enabling us to have clean, unambiguous arguments inhook_dataapi()
.I'm still getting some undefined index errors on testing. I'll try to test on a clean install and compare.
Comment #28
nedjoNot for Drupal 6.
The approaches taken in this patch were interim and designed for Drupal 6. A fresh start after code freeze, based on schema information, might learn something from this patch but should start at a much more basic rewrite.
Comment #29
KarenS CreditAttribution: KarenS commentedThis is a very interesting concept. I think maybe some of this could be a part of our next CCK in core effort -- standardizing the data object itself might be a nice starting point for creating a standardized method to add fields to any kind of data.
Anyway, just subscribing so I don't lose track of the idea.
Comment #30
wuf31 CreditAttribution: wuf31 commentedsubscribing
Comment #31
Frando CreditAttribution: Frando commentedComment #32
the greenman CreditAttribution: the greenman commentedsubscribing
Comment #33
beginner CreditAttribution: beginner commentedsubscribe
Comment #34
Panchosubscribe
Comment #35
yojoe CreditAttribution: yojoe commentedsubscribe
Comment #36
traxer CreditAttribution: traxer commentedsubscribe
Comment #37
hswong3i CreditAttribution: hswong3i commentedsubscribe
Comment #38
dropcube CreditAttribution: dropcube commentedSubscribing.
Comment #39
RobLoachPlanning on using parts of this in Services for Drupal 7, so subscribing.
Comment #40
alex_b CreditAttribution: alex_b commentedsubscribing.
Comment #41
RobLoachWill there be a Block Data API?
drupal_load('block')
Comment #42
Owen Barton CreditAttribution: Owen Barton commentedSubscribe
Comment #43
amitaibusubscribe
Comment #44
cfennell CreditAttribution: cfennell commentedsubscribing
Comment #45
BioALIEN CreditAttribution: BioALIEN commentedSubscribing. What's the update on this? Is some of this moving to CCK as per KarenS's post in #29?
Comment #46
RobLoachWhat happened to this? #225450: Database Layer: The Next Generation is now in, which will likely make the Data API much easier to implement....
Comment #47
litwol CreditAttribution: litwol commentedWith 2 major changes in core (dbtng and testing), i think this needs to be moved to HEAD. i see little hope of this getting into core before drupal 8.
p.s. 'VERSION' select box does not offer 'HEAD' as a value. it would have been nice to have something to symbolize a version past the current development.
Comment #49
webchickI don't see why we couldn't get this in 7.x. Code freeze is an indefinite period of time away. Of course, the sooner people start the better the chances of this are. :)
Comment #50
RobLoachWhat I don't like about this patch is how $revisions is a parameter in
drupal_load
, as it's only unique to loading nodes. What if we used:Then we could still pass the revisions argument, while keeping it abstracted for any other data type.
Comment #51
RobLoachHad a big hack at it from scratch using nedjo's earlier patch as a model. I started to slow down as I got into the taxonomy. Comments and nodes work..... Help appreciated. Lots of tests failing ;-) .
Comment #52
BioALIEN CreditAttribution: BioALIEN commentedComment #53
RobLoachThis needs a lot of work...
Comment #54
recidive CreditAttribution: recidive commentedNow that hook_nodeapi() and hook_user() are gone, we are a step forward in enabling this.
One possible battle plan for making this happen:
Focus this first patch on node objects and make a consistent data api implementing the drupal_load(), drupal_delete(), etc wrappers.
File separated issues for:
Renaming drupal_load() to drupal_load_file().
Renaming hook_nodeapi_* to hook_node_* for consistency.
A patch for each other objects (user, vocabularies, etc) refactoring their APIs integrating them with the new generic one.
I've filed an issue for renaming hook_nodeapi_* to hook_node_* and to make node APIs more consistent.
Comment #55
RobLoachThis is pretty pwniful for standard loading of multiple data content types: #460320: Standarized, pluggable entity loading (nodes, users, taxonomy, files, comments).
Comment #56
RobLoachLet's move this discussion over here: #460320: Standarized, pluggable entity loading (nodes, users, taxonomy, files, comments).