Drupal entities

May 3, 2013

So, for the last two days I have been getting in a little deeper than usual with Drupals custom entities. I have already looked into creating entity-types and bundles before, but only so much as the step-by-step tutorials to assess its viability and how long it takes. This time, I am doing it properly, and with a goal (other than the usual one of kicking some codeass). There are loads of useful tools to help with learning and creating entities, but one thing you really must install is the Entity APImodule. Whilst entities are in core, this gives a lot of handy helper functions/methods to make this process a lot easier, more reliable, and fun. Without it, you would be constantly writing CRUD functionality for every new entity aswell as custom admin UIs.

To get this learning going, I started by watched the current series of screencasts on Drupalize.me called Working with Entities in Drupal 7(sounds like a safe bet huh?), these have been very well presented, the first few screencasts are free to view, and I would recommend you start there, as it begins by reminding youwhat entities are, and how they fit into the world of nodes/users.

Entity Example (articles):

Most importantly, I needed to get the terms for entities tied down in my head, else all the tutorials in the world will just confuse the hell out of me. So below, are some useful little pieces

Entity Type:(eg. Nodes) - this is the core declaration, it may have some global properties set which are used by all entities of this type

Bundle:(eg. Article) - this is really just a grouping of entities, the main plus of this grouping as it allows us to make the entities fieldable. So if you want to be able to add fields, just create a single bundle for yuor entity-type (although you could add many if you like).

Entity:(eg. man on moon) - A single piece of content created using the entity-type and bundle. For example, whilst article is a bundle, the single article 'man lands on moon' is an entity.

So, before I start this I just want to let it be known that I don't want to rehash what was gone over in the screencasts, but to go over my steps afterwards. I am going to plan out and go through a new requirement below, and see how I solve it using what has been learnt.

Requirement:

The Quicktabs module can pull items in from views, which sounds perfect, but that content should not have a URL which google could potentially find (I know robots could block it, heard that 101 times before, and that is an aweful solution tbh. If you want to hide something from google don't obfuscate it, either secure it, or don't make it public in the first place). Using an entity will also help keepthe code lean,reducing DB requests (which is always a good thing when it comes to CMS'), this is due to removing the overhead for things such as URI and comments.

So it must have certain config related properties, be fieldable, and be accessible by views/quicktabs.

Entity architecture:

  • Module Name: Tabs Entity (tabsentity)
  • Entity Name: Tabbed Content (tabs)
  • Schema Name:tabsentity_tabs
  • Properties: ID, Title, parent reference, created date, updated date
  • Fields: Image, Body

Starting Blocks:

Okay, first place to start once we have the initialarchitecture planned, is to get an example which we can use as a test base. This is a great way methodfor me when learning, as you can use this to debug after writing your code, also, if you manage to get a good example, then you learn good practice (although of course, don't get a bad axample, else the world just may end). In the talk, there was a link to Lullabots GIT accountwhich looks very handy. But, even better than that once you drill in, all the code they presented in the talk can bedownloaded in their Entity API Demosrepo, so go grab it and take a look first.

As an alternative, there is also a module called examples, as the name suggests, it is a handy reference, so I have it there for future reference (and other projects), but for an initial learning piece, I will stick with this lullabot repo for now.

Step One: Configure the DB and base entity

  • Created a new custom module called tabsentity
  • Created the .info file - (with a dependency on the entity module)
  • Created the .install file - utilizing hook_schema to allow me to declare the entity, it's properties, and it's primary key. Using the repo example, this is fairly easy to go through, just make sure you have chosen the correct DB fieldtypes before continuing
  • Created the .module file - at this stage it just needs to describe the entity (referencing the schema we just setup in the .install file)
    • Copy HOOK_entity_info() from videoentity example (this function will do most of the config in declaring classes to extend, and include files to use
    • Copy 'Menu autoloader' from videoentity example and modify (do NOT forget this, I did, and have written up the issue below)
    • Change id to be 'tabs'
    • Change basetable to be 'tabsentity_tabs' (as per schema)
    • Replace all occurances of VideoEntity with TabsEntity
    • Replace all occurances of videoentity with tabsentity
    • Comment out 'EntityDefaultViewsController' (we will add this back in later)
    • Quickly read through and tidy it all up (to check all rewrites have been picked up on, and that you understand the code)
    • Create empty stubs for: TabsEntityController, TabsEntityUIController, TabsEntity

Now this has been done, I can enable the module. You will noticed though that there are several callbacks and suchlike referenced in the info file there which we haven't used yet. That time will come, at the moment we just want the foundations of our entity in place.

So long as now there are no errors, and that the table has been created in the DB, then I think I am probably good. If there are errors, I can comment out some of the lines in the .module file until they are needed later.

Step Two: Basic entity creation and UI

Whilst we have built the base entity, it is fairly useless so far, so what we are now going to do is build an admin page to list the entities, and run and admin like actions against them all.

  • Create the tabsentity.admin.inc file inside an includes directory
  • Insert a stub for 'tabs_form()' (note that the HOOK part is actually the entity-type NOT the module name)
  • Populate the stub with the editable properties
  • create a hook_submit function using the videoentity example (this just ensures that all the content is submitted using the methods in the entityAPI module)
  • add validation functionality if required
  • Insert the field_attach_form() to the HOOK_form function so any custom fields (from the bundles)are pulled into this admin page

BIG ISSUE ONE: For some reason, even though I can create content I cannot edit it, I also cannot view my fields added via bundles (the videoentity module works fine when enabled)

SOLUTION: Ensure you have the 'menu autoloader' part included in your .module file (steps above ammended to fit with this). Don't go thinking this is just hook_menu in a new costume, else you could waste ages debugging to track down the issue.

Step Three: Views integration

in .module ensure the line below has been copied across and uncommented:

'views controller class' => 'EntityDefaultViewsController',

create a new view but make sure that on the create page you click the 'show' dropdown to change it from content to your entity (this annoyed me for a while)

Step Four: Theming

A bit of extra credit in that I don't want to use the default entity theme for my item (for a start, the title shouldn't be displayed). And so, all I had to do was duplicated entity.tpl.php into my modules folder and call it tabs.tpl.php.Then I added the below code to my module to reference it:

function tabsentity_theme(){  $theme = array();  $theme['tabs'] =   array(    'render element' => 'elements',    'template' => 'tabs'  );  return $theme;}

And boom, a prototype for my entity is in existence. The rest is all quicktabs and views with arguments, so wont go into that, as want this post focussed upon entities. But basically, you create your view to use arguments, then in quicktabs call your view and pass in the arguments (eg. id).

Further work

It doesn't look too crazy to extend this to cater for multiple bundles in the future, so my next step in this piece will probably be to look into that.