The concept of entities (Entity), which will be considered in this article is one of the new products presented in Drupal 7. In order to realize the novelty of the proposed approach, you should take a brief history and recall how everything was in Drupal 6.
What is, what to eat?
All modules written under Drupal can be 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 that are useful to the developer. And although the devel module stores some information in the database (execution time of sql queries, for example), in fact it 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 (create (register) new users entering the kernel, perform various operations with them).
When you, as a developer, have the 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 on you virtually any restrictions in implementation. The first option is more convenient and faster in execution due to the ready tools provided by the Node API.
However, not only because of the speed and convenience of development, most of the serious modules created for 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 a single module to extend 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 with it. The best examples are the CCK and Views modules.
')
It would seem that everything is fine, but one problem remains. As mentioned earlier, the implementation through the node type has some limitations that, in some cases, make it inconvenient or impossible to use this approach. In particular, it’s hard to imagine that users or comments in Drupal 6 are implemented as content types. Of course, here, for as long as you like, you can make controversies about the possibility or impossibility of realizing users in the form of types of materials, but this approach cannot be called elegant or correct.
What has changed with the arrival of Drupal 7? In fact, a new level of abstraction was created. And his name is Entity. In fact, Entity is a lower level of abstraction than a node in Drupal 6. Immediately, I’ll make a reservation that in Drupal 7 the nodes have not disappeared anywhere, however, now they are a superstructure above Entity. The most beautiful thing is that users, comments and more in Drupal 7 are also Entity. You can already make a 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. That's because tags, content, comments, and users are all subclasses of Entity.
Autopsy will show
Before you start a discussion on the internal structure of the entity system, you should be reminded of such a wonderful resource as
api.drupal.org , where you can always familiarize yourself with all the interfaces, classes and methods discussed further in the language of Shakespeare.
So, it's time to see how it all works. Despite the fact that (as will be seen later), Object Oriented Programming has become much more active in using Drupal 7, in order to inform the system that our module implements a new entity, we will need 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" name of the type of our entity. Must be wrapped in t () if you want the label to be translated.
- controller class: The name of the control class that will be responsible for loading data. The class must implement the DrupalEntityControllerInterface interface. If you do not specify this parameter, the default controller will be used as the controller - DrupalDefaultEntityController (Entity controllers will be discussed further in the course of the article).
- base table: The base table in the database for our entity. Formally, the parameter is optional, since used by the DrupalDefaultEntityController class and inherited from it. Those. You can implement the DrupalEntityControllerInterface interface in your own way and receive data not from the database table, but at least from a Martian satellite, but if you decide to use DrupalDefaultEntityController or a derivative of it as a controller, please indicate this parameter. Looking ahead, I will say that in most cases, the controller makes sense to inherit from DrupalDefaultEntityController or from an even higher-level class, such as EntityAPIController (to be considered later).
- revision table: Same as above, but in this case revision table. Here the analogy with the notes is fairly traced. If your entity does not assume different revisions, the parameter can be omitted.
- static cache: Another parameter used by DrupalDefaultEntityController. Enables static caching of entities during page request. Those. in this case, we are not talking about caching between requests (when data is cached for a certain period of time and sent from the cache with each new request up to the cache expiration time), but about caching during one request. Thus, if the request does not plan to change the entity, and then reload it, it makes sense to set it to TRUE (or just omit the parameter, since by default it is TRUE).
- field cache: Here we are talking about a permanent cache for fields (Field API), which, as will be shown later, can be attached to entities. Official documentation suggests that setting this parameter to FALSE only makes sense if the entire 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 DrupalDefaultEntityController. Called by the DrupalDefaultEntityController function: attachLoad and allows other modules to attach their information to the entity. The attach_load function in any case 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: It takes an entity as an argument. Returns the uri components of the entity (such as path and options) from which the entity address 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 allowing the entity to determine its address.
- label callback: Optional parameter used to get the name of the entity. It takes the essence and the type of the entity as arguments. If the entity name is equivalent to some parameters of this entity (as for example, the node name is equivalent to the title field), it is better to use 'label' - the 'entity keys' element (which 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 'label callback'.
- fieldable: As they say "and this is the magick". By setting it to TRUE and provided that your controller implements this functionality (or inherits from DrupalDefaultEntityController) you make your entity extensible using the Drupal Field API (Analog CCK for Drupal 6 in Drupal 7 integrated into the core). Those. as well as to users, node types, tags and much more, various fields can be attached to your entity.
- translation: Without going into details - this field allows you to make your entity and the fields attached to it translatable. If not empty, the value is specified as an associative array, where the keys are the names of the modules providing the possibility of translation, and the values are information structures of any kind necessary for the translation.
- entity keys: Parameter used by Field API. An associative array that can include the following elements:
- - id: Entity field containing the unique primary identifier of the entity. Required parameter 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 a version (revision) model, then the parameter can be omitted.
- - bundle: Parameter that indicates to which bundle (subtype) the entity belongs. Each bundle has its own set of fields. Here is a good example of node types, 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 type of entity (you 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: Parameter in which the name of the entity is indicated. If the header is to be dynamically configured, 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 which are machine names of bundles (as 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 it in t () if we want the name to be translated.
- - uri callback: Same as 'uri callback' for an entity type, only for a bundle. If both are specified, the callback for the bundle will be used.
- - admin: An array of information that enables Field API to embed its pages into entity administration pages.
- - path: The path to the bundle administration page. If the path includes a stub (placeholder), you will also need to specify values for the 'bundle argument' and 'real path' (which will be discussed below).
- - - bundle argument: The position of the stub on the path. The principle is the same as when working with menu paths.
- - - real path: The path to the administration page of the bundle without a stub. Will be used to generate links.
- - - access callback: Same as hook_menu (). If left empty, user_access () will be used.
- - - access arguments: Same as hook_menu ().
- view modes: Different ways (views) of an entity. A good example is full and teaser in node. An associative array in which machine names of species are used as keys. By default, each entity has a default view. All other types can inherit default values from it if the value of the custom settings of the view parameters is set to TRUE. Each view is an array with the following keys:
- - label: “human-readable” view name. As usual, we wrap it in t () if we want the name to be translated.
- - custom settings: Strictly speaking, this parameter refers mainly to the fields. If set to FALSE (default value), then initially the view will inherit the field display settings from the default view. Then the settings can be changed via Field UI.
That's all the parameters that can be specified for an entity type. It should be noted that add-ins over DrupalDefaultEntityController, such as, for example, EntityAPIController may require \ use additional parameters. EntityAPIController for example uses the 'entity class' parameter. This possibility is achieved through 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 take a closer look at 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 allows not only to make the code itself more efficient, but also makes it easier to understand the processes inside it. In order to understand how the controller works, we will start the review from the very bottom. By definition, in the 'controller class' parameter a class should be specified that implements the
DrupalEntityControllerInterface interface. And despite the apparent complexity of the concept of entities, this interface is surprisingly minimalist. It assumes that only three methods are implemented in our class:
DrupalEntityControllerInterface :: load - loads one or more entities. As the first argument takes an array of ID entities that you want to load. As a second argument, it takes a certain array of conditions in the form of a 'field' => 'value'.
DrupalEntityControllerInterface :: resetCache - clears (resets) the static cache for all entities (if the argument is not specified) or for the selected set of entities (if the corresponding id array is passed as an argument)
DrupalEntityControllerInterface :: __ construct - Constructor. As the only argument takes the type of entity for which you want to create an instance of the controller. Thus, knowing the type of entity for which the controller is created, you can get the parameters of this type using entity_get_info ($ entity_type) and customize the controller instance that is created.
That's all the methods that should be implemented in your class, so that it has every 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 consider.
What gives us DrupalDefaultEntityController?
- Ability to load any entity, regardless of type, from database tables specified in 'base table' and 'revision table' parameters of entity type.
- Attaches fields to our entity using the Field API if the fieldable parameter is set to TRUE.
- Ability to load entities efficiently. The class supports static caching. (Although in essence, the resetCache method of the DrupalEntityControllerInterface hints that static caching should be implemented in any entity controller).
Thus, DrupalDefaultEntityController does not set as the purpose implementation of such functionality for the end user, it only gives some point of reference for the developer. Moreover, in those cases where your entity must somehow implement the CRUD (Create Read Update Delete) concept, it makes sense to inherit your controller from an even higher-level class - EntityAPIController, presented in the Entity API module. However, a review of how to work with the Entity API is beyond the scope of this article and will be consecrated another time.