Development for Drupal 7 with the new Entity Concept

The concept of entities (Entity), which will be considered in this article, is one of the novelties presented in Drupal 7. In order to realize the whole novelty of the proposed approach, you should take a short digression into history and recall how everything was in Drupal 6.

What is what to eat with?


All modules written under Drupal can be conditionally divided into two categories. The first is modules that do not actually declare new data types and work with data already somewhere defined and stored. For example, the lightbox2 module allows you to change the presentation of images on the site, and the devel module provides various utilities useful to the developer. And although the devel module stores some information in the database (runtime of sql queries, for example), in fact this cannot be called a full-fledged data model.
The second category is the modules that allow you to create new types of objects, define new data models. Such modules, for example, include the webform module (allows you to create survey forms), as well as the user module included in the kernel (create (register) new users, perform various operations with them).

When you, as a developer of a module, need to declare a new data type, you have two options. The first option is to define your data type as a new material type (node ​​type). The second option is to create everything from scratch. Both approaches have their advantages and disadvantages. It is clear that the second option is more flexible and does not impose practically any restrictions on your implementation. The first option is more convenient and faster in execution due to the ready-made tools provided by the Node API.

However, not only because of the speed and convenience of development, most serious modules created under Drupal 6 and implementing some new data model declare it as a node type. An important feature is that all materials, regardless of type, have a certain general structure and a general scheme of external interaction. This approach allows you to write one module to expand the functionality of all types of material existing in the system. In fact, you may not even be aware of the existence of a certain type of material and at the same time create a module that will influence it and be able to operate on it. Vivid examples are the CCK and Views modules.

It would seem that everything is fine, but one problem remains. As mentioned earlier, an implementation through a node type has some limitations, which, in some cases, make this approach inconvenient or impossible. In particular, it is hard to imagine that users or comments in Drupal 6 are implemented as content types. Of course, there is an arbitrarily long debate here regarding the possibility or impossibility of implementing users in the form of types of materials, but this approach cannot be called elegant and correct in any case.

What has changed with the advent of Drupal 7? In fact, a new level of abstraction has been created. And his name is Entity. In fact, Entity is a lower level of abstraction than node in Drupal 6. I’ll immediately make a reservation that in Drupal 7 the nodes have not disappeared, however, now they are an add-on for Entity. The best part is that users, comments, and more in Drupal 7 are also Entity. You can already feel the difference “out of the box”. For example, earlier, using the CCK module, you could add new fields only to material types. In Drupal 7, you can do the same with users, comments, and even taxonomy tags. This is because tags, content, comments, and users are all subclasses of Entity.

An autopsy will show


Before you start a discussion on the internal structure of an entity system, you should recall such a wonderful resource as api.drupal.org , where you can always familiarize yourself with all the interfaces, classes and methods discussed later in the Shakespeare language.

So, it's time to see how it all works. Despite the fact that (as will be seen later) in Drupal 7, Object-Oriented Programming has become much more actively used, in order to inform the system that our module implements a new entity, we will have to implement a hook. The hook we are interested in is hook_entity_info (). From it we must return an associative array, the keys of which will be the names of the entities that we will implement. In turn, each entity will correspond to an array of its parameters. Below is a list of parameters that we can specify:
  • label: The so-called “human-readable” type name of our entity. Must be wrapped in t () if you want the label to be translatable.
  • controller class: The name of the counterOller class that will be responsible for loading the data. The class must implement the DrupalEntityControllerInterface interface. If you do not specify this parameter, the default controller, DrupalDefaultEntityController, will be used as the controller (Entity controllers will be discussed later in this article).
  • base table: The base table in the database for our entity. Formally, the parameter is optional, because Used by and inherited from the DrupalDefaultEntityController class. Those. You can implement the DrupalEntityControllerInterface interface in your own way and receive data not from the database table, but at least from the Martian satellite, but if you decided to use the DrupalDefaultEntityController or its derivative as a controller, please specify this parameter. Looking ahead, I’ll say that in most cases the controller makes sense to inherit from the DrupalDefaultEntityController or from an even higher-level class, such as EntityAPIController (to be discussed later).
  • revision table: Similar to the previous, but in this case revision table. An analogy with nodes is rightly traced here. If your entity does not assume various revisions, the parameter can be omitted.
  • static cache: Another parameter used by DrupalDefaultEntityController. Enables static entity caching during page request. Those. in this case, it’s not about caching between requests (when the data is cached for a certain period of time and returned from the cache with each new request until the cache expires), but about caching for one request. Thus, if it is not planned to change the essence of the query and then reload it, it makes sense to set it to TRUE (or simply omit the parameter, since by default it is TRUE).
  • field cache: Here we are talking about a persistent cache in relation to fields (Field API), which, as will be shown later, can be attached to entities. The official documentation says that setting this parameter to FALSE makes sense only if the whole entity is subject to constant caching. In other words, if you do not know exactly why you need it, it is better to omit this parameter and remember that by default it is set to TRUE.
  • load hook: Another parameter used in the DrupalDefaultEntityController. Called by the DrupalDefaultEntityController: attachLoad function and allows other modules to attach their information to the entity. In any case, the attach_load function will call hook_entity_load for all entities, while the specified load hook will allow you to call a separate hook for your entity (such as hook_node_load in the node module).
  • uri callback: The entity itself is taken as an argument. Returns the uri of the entity components (such as path and options) from which the address of the entity can later be obtained using the uri () function. There are different approaches to the definition of this function. In the Entity API module, in particular, there is an entity_class_uri function that simply calls the $ entity-> uri () function of the entity passed to it, thus giving the entity the ability to determine its address.
  • label callback: An optional parameter used to get the name of the entity. As arguments, it takes the essence itself and the type of entity. If the name of the entity is equivalent to some parameters of this entity (such as the name of the node is equivalent to the title field) it is better to use the 'label' - the element 'entity keys' (will be described later). If the name is formed somehow in a special way (for example, as a combination of some fields), you can set it using the 'label callback'.
  • fieldable: As the saying goes "and this is the magick". By setting to TRUE and assuming that your controller implements this functionality (or is inherited from the DrupalDefaultEntityController), you make your entity extensible using the Drupal Field API (Integrated into the kernel for the Drupal 6 CCK in Drupal 6). Those. as with users, types of nodes, tags and much more, various fields can be attached to your entity.
  • translation: Without going into details - this field makes it possible to make your entity and the fields attached to it translatable. If not empty, an associative array is indicated as the value, where the keys are the names of the modules that provide the ability to translate, and the values ​​are information structures of any kind necessary for translation.
  • entity keys: Parameter used by the Field API. An associative array that may include the following elements:
  • - id: An entity field containing the unique primary identifier of the entity. Required if fieldable is set to TRUE. The value must be a positive integer.
  • - revision: The name of the parameter containing the revision ID. The Field API assumes that all Entities within a type have a unique revision ID. If your entity does not implement the version (revision) model, then the parameter can be omitted.
  • - bundle: The parameter that indicates which bundle (subtype) the entity belongs to. Each bundle has its own set of fields. Here, a good example would be the types of nodes, where for each type you can specify your own fields (despite the fact that in the end, all types are one entity). If you are not going to implement similar capabilities in your entity type (do not plan on having different bundles and different sets of fields for them), you can omit this parameter. Then, the name of the entity type will be automatically used as a parameter.
  • - label: A parameter that indicates the name of the entity. If the title should be formed dynamically, use the label callback parameter described earlier.
  • bundles: Actually defines a set of bundles (subtypes) of the type of our entity. An associative array of keys of which are the machine names of the bundles (in the form in which they are stored in the 'bundle' field defined in the 'entity keys' parameter). The names of the bundles, in turn, are associated with associative arrays with the following keys:
  • - label: “human-readable” is the name of the bundle. As usual, we wrap in t () if we want the name to be translatable.
  • - uri callback: Same as 'uri callback' for entity type, only for bundle. If both of these are specified, then callback will be used for the bundle.
  • - admin: An array with information that will allow the Field API to embed its pages in entity administration pages.
  • - path: The path to the bundle administration page. If the path includes a placeholder, you will also need to specify values ​​for the 'bundle argument' and 'real path' (discussed below).
  • - - bundle argument: The position of the stub in the path. The principle is the same as when working with menu paths.
  • - - real path: The path to the bundle administration page without a stub. Will be used to generate links.
  • - - access callback: Same as in hook_menu (). If left blank, user_access () will be used.
  • - - access arguments: Same as in hook_menu ().
  • view modes: Various ways (types) of displaying the entity. A good example is full and teaser in node. An associative array in which machine view names are used as keys. By default, each entity has a default view. All other views can inherit default values ​​from it if TRUE is set to 'custom settings' of view parameters. Each view is an array with the following keys:
  • - label: “human-readable” view name. As usual, we wrap in t () if we want the name to be translatable.
  • - custom settings: Strictly speaking, this option applies primarily to fields. If set to FALSE (the default value), then initially the view will inherit the settings for displaying fields from the default view. Then the settings can be changed through the Field UI.

That's actually all the parameters that you can specify for the entity type. It should be noted that add-ons for DrupalDefaultEntityController, such as, for example, EntityAPIController may require \ use additional parameters. An EntityAPIController for example uses the 'entity class' parameter. This possibility is achieved thanks to the function entity_get_info ($ entity_type = NULL), which allows you to get data about the desired type of entity.

Entity controller

Now let's dwell on the entity controller, which is defined in the 'controller class' parameter. In essence, a controller is a way to manage the data of your entity. What I like about OOP is that it not only makes the code more efficient, but also makes it easier to understand the processes inside it. In order to understand how the controller works, we start the review from the bottom. By definition, the parameter implementing the DrupalEntityControllerInterface interface must be specified in the 'controller class' parameter . And despite the apparent complexity of the concept of entities, this interface is surprisingly minimalistic. It assumes that there are only three methods in our class implementing:
DrupalEntityControllerInterface :: load - loads one or more entities. It takes an array of entity IDs to load as the first argument. As a second argument, it takes a certain array of conditions in the form 'field' => 'value'.
DrupalEntityControllerInterface :: resetCache - clears (flushes) the static cache for all entities (if no argument is specified) or for the selected set of entities (if the corresponding id array is passed as an argument)
DrupalEntityControllerInterface :: __ construct - Constructor. As a single argument, accepts the type of entity for which to create an instance of the controller. Thus, knowing the type of entity for which the controller is being created, one can obtain parameters of this type using entity_get_info ($ entity_type) and customize the created instance of the controller.

That's actually all the methods that must be implemented in your class so that it has the full right to be called an entity controller. However, in most cases it makes sense not to implement the interface from scratch, but to inherit your controller class from the DrupalDefaultEntityController class , which we will now examine.
What does the DrupalDefaultEntityController give us?
  1. The ability to load any entities regardless of the type from the database tables specified in the parameters of the 'base table' and 'revision table' of the entity type.
  2. Attaches fields to our entity using the Field API if the fieldable parameter is set to TRUE for the entity type.
  3. The ability to load entities efficiently. The class supports static caching. (Although, in essence, the resetCache method of the DrupalEntityControllerInterface interface hints that static caching should be implemented in any entity controller).

Thus, the DrupalDefaultEntityController does not set as its goal the implementation of functionality for the end user as such, it only provides some starting point for the developer. Moreover, in cases where your entity must somehow implement the CRUD concept (Create Read Update Delete), it makes sense to inherit your controller from an even higher level class - EntityAPIController, which is presented in the Entity API module. However, consideration of the principles of working with the Entity API is beyond the scope of this article and will be dedicated another time.

Also popular now: