PHP for programming Drupal.

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

I am floundering a bit with Drupal and I'd like to test some of the assumptions I have made thus far.

1. The best place to put PHP code.
As far as I can see we have the custom Theme, the Custom Module and the block. The block can have functions defined and would appear to be the solution of choice for small pieces of code.
Q. What are the namespaces of a block? Can I call a function from another block?
As far as I can see, large functions should be coded as modules.
I'm a bit wary of placing PHP code in Themes because, as I see it, the Themes can be easily swapped which would remove access to the code.

2. Functions and Hooks.
It sees to me that if the Drupal API has a function called hook_somefunction() then if a website technician writes a function called myownname_somefuntion(), then wherever Drupal makes a call to that somefunction(), it will call the myownname_somefuntion() instead. So, provided the parameters match in number and type, and the returned value is of the type expected by Drupal, the new function should work in it's stead.

3. Accessible variables.
I understand that there are a number of variables that Drupal is using and which should be accessible to my functions.
Q. Do these have a particular namespace? To a block located in the left sidebar, for example?
Q. If I have a Taxonomy object which has user defined tags, is there a way to access those tags programatically? And can I generate menus on the fly based on those tags? (I'm sure I can, but how?)

4. Database tables and queries.
Q. Can I access the MySQL database? For example, I have defined a Profiles module which captures user data in addition to that initially required for registering but I see no way for the registered user to access and update that profile.

Q. If I call a function that returns, say, an array containing a view, can I pass that array so that it is rendered on the 'contents' section of the page if the block I'm calling it from is on, say, the left sidebar?

Can anyone comment on these.

Are my assumptions largely correct?

Can anyone comment on my Questions?

Are there any reasonably clear tutorials? Not video, though. We are absolutely stung by the local Telecom company here for what they perceive as bandwidth abuse. Sad

Many thanks,

~Dave

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

Yeah, I am struggling a bit with Drupal.

I'm a little brassed-off with the community because there doesn't seem to be much willingness to help out newbs. I guess they must be a bit thinly spread.

I'm slowly getting there and once I've got more of a clue I will come back here and post the answers to my questions to help any other poor saps struggling down this road...

Megan's picture

She has: 11,421 posts

Joined: Jun 1999

Sorry, previous post removed (that was spam).

I'll try to get Jeevesbond, our resident Drupal expert, to come by and help you out here.

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

Q. What are the namespaces of a block? Can I call a function from another block?

Oh, please don't do that, God kills a kitten every time you put functions in blocks. Why do you hate kittens? Smiling

But seriously, PHP in blocks is for silly stuff like this:

<?php
$taglines
[1] = 'Web design help for everyone';
$taglines[] = 'What-ever';
$taglines[] = 'Probably not fit for human consumption';
$taglines[] = 'Keep out those blasted Kiwi&#0039;s!';
$taglines[] = 'Egotistical Design Syndrome';
$taglines[] = 'Zeldman for Dummies';
$taglines[] = 'Who took my cheese?';
$taglines[] = 'We’re not trying to sell you anything';
$taglines[] = 'For when it all gets too much...';
$taglines[] = 'Internet Explorer&trade;&reg; made me do it!';
$taglines[] = 'Web 3.1 and a &#189;';
$taglines[] = '&apos;least I aint chicken!';
$taglines[] = 'You&#0039;ve liquified me you scchhhlags!';
$taglines[] = 'We&#0039;re not from London you know.';
$taglines[] = 'I&#0039;m going to do the steamroller on you! Hoser!';

$rand_selection = rand(1, count($taglines));
floor($rand_selection);
echo
'<div id="tagline">' . $taglines[$rand_selection] . '</div>';
?>

And even that should probably be in a template_preprocess_page() function.

As far as I can see, large functions should be coded as modules.

Absolutely, and make sure you have a rough idea of what the Drupal API is capable of before embarking on module writing, it's powerful stuff, but probably not what you're used to. For reference, see: http://api.drupal.org for learning, see: http://drupal.org/node/206753

Important note: you're frustrated because not enough people like you (or even I) thought of going back to the documentation and fixing it once we'd worked something out. Even just typing a comment on the offending page when you don't understand something is a really big help. The Drupal community is literally crying out for help with documentation, the problem we have is that we already understand all this stuff so it's really difficult to put ourselves in a newbie's shoes. Smiling

I'm a bit wary of placing PHP code in Themes because, as I see it, the Themes can be easily swapped which would remove access to the code.

True. Any code you get a feeling like that about probably belongs in a module. If you have functions that you may put in other themes, but are very theme-specific (hacks for breadcrumbs, the menus and such) put them in template.php. Smiling

It sees to me that if the Drupal API has a function called hook_somefunction() then if a website technician writes a function called myownname_somefuntion(), then wherever Drupal makes a call to that somefunction(), it will call the myownname_somefuntion() instead.

Pretty-much. I'm unsure whether you're fully understanding what hooks are for though. Smiling

Note: while the below is a fun and valid example, if you really need to work with images, use theme_image()!

It's a bit like 'implements' in OOP (bear in mind Drupal implemented namespaces and OOP in PHP years before it possessed these things Smiling ). So you're programming a module called davesbeaver that irritatingly inserts a picture of a beaver (get a proper Canadian beaver) into every node, whether the author wants it or not. Inside sites/all/modules/davesbeaver/davesbeaver.module you create a function:

<?php
/**
* Implementation of hook_nodeapi().
*/
function davesbeaver_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
 
// Insert our beaver image whenever a user views a node.
  // Uncomment the following if you want to see the structure of a node:
  // drupal_set_message('<pre>' . var_export($node, TRUE) . '</pre>');
 
if ($op == 'view') {
   
$node->body .= '<img src="'. drupal_get_path('module', 'davesbeaver') .'/images/beaver.jpg" alt="A beaver!" />';
  }
}
?>

hook_nodeapi function: http://api.drupal.org/api/function/hook_nodeapi
Full list of hooks: http://api.drupal.org/api/group/hooks/6

As long as you do the other things required to make a module, that should magically add beaver.jpg to all your nodes. Smiling

So that's version 1.0, but you show your new module off to some of your mates and they angrily tell you that there shouldn't be any HTML in a hook! Whoops, all HTML (or as much of it as is reasonably possible) belongs in the theme layer. Let's fix that:

<?php
/**
* Implementation of hook_nodeapi().
*/
function davesbeaver_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
 
// Insert our beaver image whenever a user views a node.
  // Uncomment the following if you want to see the structure of a node:
  // drupal_set_message('<pre>' . var_export($node, TRUE) . '</pre>');
 
if ($op == 'view') {
   
$node->body .= theme('beaver_picture', drupal_get_path('module', 'davesbeaver') .'/images/beaver.jpg');
  }
}

/**
* Implementation of hook_theme().
*
* @ingroup themeable
*/
function davesbeaver_theme() {
  return array(
'beaver_picture' => array('template'  => 'beaver-picture', 'arguments' => array('beaver_file' => NULL)));
}
?>

Now we add a new file to sites/all/modules/davesbeaver, called beaver-picture.tpl.php It will contain the HTML for theming the beaver image:

<?php
// $Id$

/**
* @file
* Default theme implementation to format a beaver image.
*
* Variables available:
* - $beaver_file: Path to the default image (if the themer wishes to use it).
*/
?>

<div class="beaver"><img src="<?php print $beaver_file; ?>" alt="A beaver!" /></div>

Now theme authors can simply put a beaver-picture.tpl.php into their theme's directory and they'll be able to override your module's HTML!

There's also a more in-depth handbook page on theme authoring.

Sorry I didn't get to this sooner, I'm in demand! Wink

*** EDIT ***
I haven't tested any of the above, so don't blame me if it pan-fries your cat. Wink

a Padded Cell our articles site!

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

Oh, to answer some more of your questions:

Q. Do these have a particular namespace? To a block located in the left sidebar, for example?

If you're using blocks to access things like $node, then You'reDoingItWrong(tm). Having said that, you can load the current node (assuming you're on a node page), using:

<?php
$node
= menu_get_object();
?>

You'll probably see arg() used around the place, avoid the use of the function if possible, it may well be going away in Drupal 7 (just speculation at this point).

Q. If I have a Taxonomy object which has user defined tags, is there a way to access those tags programatically? And can I generate menus on the fly based on those tags? (I'm sure I can, but how?)

User defined tags: Of course! That question could take a very long time to answer though, it's a bit nebulous. Smiling

To start, look at:
http://api.drupal.org/api/file/modules/taxonomy/taxonomy.module/6
http://api.drupal.org/api/function/taxonomy_get_tree/6
http://api.drupal.org/api/function/taxonomy_vocabulary_load/6

Also, have a look at forum.module, it does some interesting stuff with Vocabulary:
http://api.drupal.org/api/file/modules/forum/forum.module
http://api.drupal.org/api/function/forum_get_topics/6

Menus: heh, you would have thought that would be easy wouldn't you? Smiling

We've had trouble with this stuff too. I believe the Taxonomy menu is the answer to your woes.

Q. Can I access the MySQL database?

You can access whatever database system you like (well, MySQL and Postgres mostly). Drupal has a database API, with some really nice bonus security features -- make sure you use them! The database system is getting an overhaul in Drupal 7, to use PHP PDO It's going to rock. Smiling

Q. If I call a function that returns, say, an array containing a view, can I pass that array so that it is rendered on the 'contents' section of the page if the block I'm calling it from is on, say, the left sidebar?

Short answer: no. Smiling

This is another case of You'reDoingItWrong(tm), but then I'm sure you've realised that by now! Laughing out loud

You could put the View into a block, then position that block beneath the content.

a Padded Cell our articles site!

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

One more thing:

I have defined a Profiles module which captures user data in addition to that initially required for registering but I see no way for the registered user to access and update that profile.

Is there a reason you're not using the built-on profile module? Or, if you need something flashier, I managed to do make some pretty nice profiles for a client using Advanced Profile Kit /shameless_plug

a Padded Cell our articles site!

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

JeevesBond,

Thanks for replying to this and for emailing a very nice note to inform me that you had.

Yes the bulk of the questions I was asking have been researched and either implemented or abandoned in favour of something else.

I will horde (hoard?) your example module for a later date as who knows when I may next require to place a jpeg of a Canadian beaver into someone's unsuspecting node?

The views problem has largely been resolved by the use of PHP and SQL queries, which I am certain are pretty ungainly and inefficient hacks which should be consigned to the hack-bucket the moment the site becomes under any sort of strain and I have a better idea of what to replace them with.

However, the profile issue has suddenly become pressing. I had managed to get away with a solid column of checkboxes and textfields stretching for several pages, and although this was somewhat ungainly, it functioned reasonably well.

Now, however, there is a requirement to make each checkbox (there are 11 in all) have 4 sub-categories with their own checkboxes, which will make 55 checkboxes all lined down one side of the web page.

This will undoubtedly be tedious both for the user and for the poor sod who has to click his way through creating each individual checkbox, entering it's title, name and description and (hopefully) not forget to check the box which will render it on the page before clicking 'save' and moving onto the next one.

So your shamless plug re the APK could well be my salvation.

I have had the 'Alphabetical List of Drupal Modules' open for some days now as I have investigated several likely candidates for solving this. In fact I was looking at the Advanced Profile Kit this evening shortly before receiving your email.

I had been fast coming to the conclusion that the best option entailed the use of CiviCRM, but on your recommendation I shall revisit the Advanced Profile Kit.

Essentially, what needs to be done is to have the 11 checkboxes displayed without their 44 underlings. Then, when a checkbox is checked, the appropriate squad of underling checkboxes would be exposed for the user's consideration.

It would be nice if there was a way to group the checkboxes rather than leaving them in their column, but this is really not that consequential.

Oh, and one further point; I shall definitely refrain from calling procedures from other blocks as I really don't want kittens on my conscience.

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

Essentially, what needs to be done is to have the 11 checkboxes displayed without their 44 underlings. Then, when a checkbox is checked, the appropriate squad of underling checkboxes would be exposed for the user's consideration.

Sounds like APK is what you need, that and CCK for adding the custom fields to profiles (one of the things APK helps with is turning user profiles into nodes, which CCK can then add fields to).

You may need some custom jquery to make the extra checkboxes appear/disappear though. CCK integrates nicely with Views by the way.

a Padded Cell our articles site!

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

Hmm.

Very frustrating.

Can't see a way to use it.

Gone to /admin/settings/advanced-profile where there is a checkbox called "Create user profile node type" but when I enable this and save configuration it just disables itself.

I've installed and enabled just about everything I can think of including Author Pane, Panels 3, CTools, Views, CCK, Content Profile and Link.

It says "try manually importing the contents of uprofile.inc with content copy's UI", but I can't see anything that might resemble a UI for copy.

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

Ah yes, I forgot to mention that APK is hard. This whole area is undergoing a lot of development at the moment. The code is usable/pretty stable, but it takes a bit of graft to setup.

Gone to /admin/settings/advanced-profile where there is a checkbox called "Create user profile node type" but when I enable this and save configuration it just disables itself.

Believe it or not, that's what it's supposed to do! Smiling

If the box is checked, APK tries to create a node type to store profile information in, it then unchecks the box. If I remember correctly, I could never get it to create the blasted content type anyway. So don't worry that it doesn't work.

I believe the part you're getting stuck on is:

2. Profile type auto creation: Check this box and save configuration. This option will be disabled once the node type exists and you will need to delete your node type if you want to re-create it. If you get errors with this step, it is due to issues with CCK's content copy module. Try importing manually, by copying and pasting the contents of uprofile.inc directly to ?q=admin/content/types/import

Bear in mind these instructions are for Drupal 5, where the profiles as nodes modules are different to the ones for Drupal 6 (there were two competing in five, which were merged for six -- generally regarded as a good thing).

Does http://www.example.com/?q=admin/content/types/import exist on your site? If not, it's because Content Profile doesn't work the same as its Drupal 5 equivalents.

If it does exist, look in the advanced_profile/includes directory for a file named uprofile.inc (I've updated the documentation to make a little more sense), then copy and paste the contents to http://www.example.com/?q=admin/content/types/import

If that URL does not exist, don't worry about it, just skip that step. It's mostly just creating a bunch of default stuff for you, that's easy to do by hand.

Then, this instruction will be erroneous for you:

8. Configure Bio:

You're using Content Profile, if you're on Drupal 6.

The rest should be reasonably straight-forward. What you need to know: all user profiles will become nodes, just like Story, Page, Blog post or Forum post. APK tries to create the node type of 'uprofile' for user profiles (that's what the importing earlier is all about). However, if this doesn't work out, you can either create your own blasted node type or -- if it creates one, I'm not sure -- use the one provided by Content Profile.

Does that get you any further? Smiling

*** EDIT ***
Just one more thing: going to ?q=admin/content/types on your site is where you can edit content types and the CCK fields that appear on them.

a Padded Cell our articles site!

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

Hi JeevesBond,

JeevesBond wrote:
Ah yes, I forgot to mention that APK is hard. This whole area is undergoing a lot of development at the moment. The code is usable/pretty stable, but it takes a bit of graft to setup.

It's not really intuitive, is it? Sad
JeevesBond wrote:

Gone to /admin/settings/advanced-profile where there is a checkbox called "Create user profile node type" but when I enable this and save configuration it just disables itself.

Believe it or not, that's what it's supposed to do! Smiling

If the box is checked, APK tries to create a node type to store profile information in, it then unchecks the box. If I remember correctly, I could never get it to create the blasted content type anyway. So don't worry that it doesn't work.


Yes, it makes sense. I just couldn't see anywhere to edit my profile and assumed that the checkbox enabled more configuration settings.

JeevesBond wrote:

I believe the part you're getting stuck on is:

2. Profile type auto creation: Check this box and save configuration. This option will be disabled once the node type exists and you will need to delete your node type if you want to re-create it. If you get errors with this step, it is due to issues with CCK's content copy module. Try importing manually, by copying and pasting the contents of uprofile.inc directly to ?q=admin/content/types/import

Bear in mind these instructions are for Drupal 5, where the profiles as nodes modules are different to the ones for Drupal 6 (there were two competing in five, which were merged for six -- generally regarded as a good thing).


Yes, when the checkbox didn't stay enabled, I assumed that there was something wrong with my install.
JeevesBond wrote:

Does http://www.example.com/?q=admin/content/types/import exist on your site? If not, it's because Content Profile doesn't work the same as its Drupal 5 equivalents.

If it does exist, look in the advanced_profile/includes directory for a file named uprofile.inc (I've updated the documentation to make a little more sense), then copy and paste the contents to http://www.example.com/?q=admin/content/types/import


Yes it's there.
JeevesBond wrote:

If that URL does not exist, don't worry about it, just skip that step. It's mostly just creating a bunch of default stuff for you, that's easy to do by hand.

I imported the uprofile.inc as you suggested, but got the following error: "The content type uprofile already exists in this database.
Exiting. No import performed."
JeevesBond wrote:

Then, this instruction will be erroneous for you:

8. Configure Bio:

You're using Content Profile, if you're on Drupal 6.


Yes, I realised that.
JeevesBond wrote:

The rest should be reasonably straight-forward. What you need to know: all user profiles will become nodes, just like Story, Page, Blog post or Forum post. APK tries to create the node type of 'uprofile' for user profiles (that's what the importing earlier is all about). However, if this doesn't work out, you can either create your own blasted node type or -- if it creates one, I'm not sure -- use the one provided by Content Profile.
Okay, I'll give it a go.
JeevesBond wrote:

Does that get you any further? Smiling
JeevesBond wrote:

*** EDIT ***
Just one more thing: going to ?q=admin/content/types on your site is where you can edit content types and the CCK fields that appear on them.

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

JeevesBond,

This is completely impenetratable.

I have been through it and created a mess of Panels, mini panels, field types, nodes, pages and a mass of different content, but I have got no clue on where it all fits together.

I would envision that the panels (or the mini panels) would allow the checkboxes to be grouped together, but I cannot see any way of placing the checkboxes on the form nor of displaying them (or hiding them). And why do I need to specify a teaser when I create a panel? Where does the teaser appear, and what function does it serve? And why can't I just leave it out?

And what is the APK's role in all of this? It doesn't appear to do anything at all.

Sorry if I sound a bit grumpy, but I'm chasing my tail here.

I'm starting to feel that what I'm hoping to do is not possible.

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

If I think (deluded fool that I am) that I have managed to configure a panel to provide the 5 checkboxes, how do I get these to show up on the profile?

If I could get that far, then I could possibly have a look at implementing the CSS and Javascript which would control the visibility of the 4 sub checkboxes. Not that I can see any way of adding that code. But still, getting to first base would be nice. I cannot begin to describe how frustrating it is to be at no further forward after three days of googling the same content (largely outdated) repeatedly.

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

I would envision that the panels (or the mini panels) would allow the checkboxes to be grouped together,

I don't have much experience with mini-panels, so could be wrong, but I don't think they can do that, unfortunately.

The Fieldgroup module is more likely to do what you want. At the very least it'll be able to group your fields so -- with a little JQuery magic -- you'll be able to make the groups behave in the required way. In fact, under Configure, it's possible to set the groups to 'collapsible' or 'collapsed', which seems to be exactly what you're looking for.

Panels should be used for pages only, in fact (and sorry I didn't think to mention this before) but for the form Panels shouldn't be required at all.

APK's role is mostly just to give you the theme and some instructions. There's also some default Panels and Views; if you're using the APK theme for your user profiles the module has to be enabled. It's also worth leaving enabled to see updates to the module/theme.

If I think (deluded fool that I am) that I have managed to configure a panel to provide the 5 checkboxes, how do I get these to show up on the profile?

Am a little unsure where you are or how you got there. :-/ However, to start ignore Panels and APK. Concentrate on CCK and Content Profile. Make sure Content Profile is installed, go to Administer -> Content management -> Content types then on the Profile row, see the manage fields link. On that page is where the registration/user edit form can be setup.

Note: might be a good idea to switch off the default 'Profile' module, supplied by Drupal.

I don't know why this isn't enabled by default, but it's also important to go to: Administer -> Content management -> Profile -> Edit, then hidden at the bottom under Content Profile is a checkbox. Make sure it's checked and Save.

You'll notice that whatever you configure in manage fields will appear on the user registration and edit forms. Also, note that these are nodes, and when logged-in as admin there'll be a lot of crap about menus, promoting to front page, title etc. Firstly: the problem is that nodes were not strictly designed to be used as user profiles (although conceptually they're perfect for the job), so there's some extra cruft. Secondly: it's easy to hide the cruft from your users, in fact they should see very little of it by default. Thirdly: Nodes are being made more versatile in the up-coming Drupal 7, so this usage of nodes is known -- and approved of -- by the Drupal core developers (those that care anyway).

When you've set the checkbox under Content Profile a tab, of the same name, will appear next to Edit. Note that Hide form fields: only applies to the user registration page, not the user edit page. For that you'll need the Auto Node Title module.

This is all pretty new, innovative stuff for Drupal, so a lot of the UI is still being worked out. As I said earlier: it all works pretty well, but is very hard for a newbie to put together, 'unintuitive' is a good way of describing it, yes. Smiling

If this is a professional project and you're really stuck, it would take me about two hours to get a user profile creation/edit form that has grouped checkboxes on it. Hopefully this will be enough information to get you going though.

a Padded Cell our articles site!

davecoventry's picture

He has: 112 posts

Joined: Jun 2009

Hi JB,

Sorry about getting a little terse.

Although I wouldn't describe this project as 'professional', I am hoping to make money from it and I do have a partner in the enterprise, so in that context it's not a hobby which I can abandon if the going starts getting tough. Also, my partner (my cousin) behaves like a 'boss' sometimes, so it very definitely feels like a professional project, however, neither of us has the funds, resources and backing which might be considered the prerequisite of a one.

What I'm also planning to do is programmatically add checkboxes to the user's profile. When this started we had 11 checkboxes. This has expanded to over 80 (against my protestations) and each checkbox topic needs it's own 'news' category and it's own forum and these will have to be raised at the same time as the checkboxes are added. Additionally I have blocks which must be made aware of their existence.

At the moment some six or seven pieces of code must be updated with the addition of each new checkbox, so the scope for error through typo or omission is significant and would be eliminated through such a one-click implementation.

I can easily write an DHTML form with the checkboxes hidden intially, but expanded as the user selects a category. I wonder if an option wouldn't be to write those checkboxes directly to the profile_fields and profile_values tables?

JeevesBond's picture

He has: 3,956 posts

Joined: Jun 2002

When this started we had 11 checkboxes. This has expanded to over 80 (against my protestations) and each checkbox topic needs it's own 'news' category and it's own forum and these will have to be raised at the same time as the checkboxes are added.

The way I would tackle this is create a module, call it 'category_profile_fields' (or something), which synchronises Forum taxonomy, synonym/related terms in the News taxonomy and check boxes on the user profile edit page. This could happen when a new term or profile option is created, or on a hook_cron.

I wonder if an option wouldn't be to write those checkboxes directly to the profile_fields and profile_values tables?

You'd have to give up using CCK and Content Profile, as those are the database tables used by Drupal core's profile module, which doesn't use CCK. If you've given up on Content Profile/CCK already then this is probably the way to go.

Either way, you're going to have to get quite heavily into Drupal internals, read a lot of code and work out how stuff functions. Smiling One good way to find out how to do stuff is by looking at the code for core or other contributed modules.

Also, as said before, you can group CCK fields (not sure if the same is possible for profile.module) in a collapsible fieldset (see the modules page for an example). Personally, I'd use these, then set 'collapsed' => FALSE in a category where the user has checked a box. Not sure if that makes sense for what you're trying to do though. Smiling

The disadvantage of using CCK over profile.module is that I can't seem to find any CCK API docs anywhere. This will improve when CCK goes into Drupal core, but until then you'll be stuck looking at how other modules integrate with CCK

a Padded Cell our articles site!

Want to join the discussion? Create an account or log in if you already have one. Joining is fast, free and painless! We’ll even whisk you back here when you’ve finished.