drupal7entitiessfdug-130311184749-phpapp01

advertisement
Entities in Drupal 7
& the Entity API
#sfdug
March 11, 2013
JD Leonard (@drupal_jd)
ModernBizConsulting.com
About Me

Computer science background

Working with Drupal since 2006

Developer, architect, project manager


Freelance through my business:
Modern Biz Consulting
Focus on complex web application development
Agenda

Ways we represent data in D6 vs D7

Entities, entity types, & bundles

Fields (formerly CCK)

The Entity API contrib module

How (and why) to define a custom entity

Using EntityFieldQuery

Defining entity property info

Leveraging the Entity Metadata Wrapper
Poll


Who here is...
–
Brand new to Drupal?
–
A site builder?
–
A module developer?
–
On the business side of things?
Who here has...
–
Created a custom D7 entity?
–
Not created a custom D7 entity?
D6 Data

(Custom database table)

User

Comment

File

Taxonomy vocabulary

Taxonomy term

Node
–
Page, Blog post, (custom content type)
D7 Data

(Custom database table)

Entity
–
User
–
Comment
–
File
–
Taxonomy vocabulary
–
Taxonomy term
–
Node
•

Page, Blog post, (custom content type)
(Custom entity type)
Entity Types

Elemental building block for most important data in
Drupal 7

Represents a concept or noun

Different types store different data
–
Generally in a single DB table
–
Eg: Node
•
–
Eg: User
•

Title, body, created timestamp, etc.
Username, created timestamp, last visit timestamp, etc.
Defined using hook_entity_info()
Entity

Instance of an entity type

Examples of entities from core:

–
The user jdleonard with uid 80902
–
The page “Drupal Rocks” with nid 44
Any entity can be loaded with entity_load()
–
Eg: entity_load(‘node’, array(44, 65))
•
–
Returns an array with two nodes with nids 44 and 65
Core provides some convenience wrappers
•
Eg: node_load() and user_load()
Entity Bundles

Subtype of an entity

Eg: Page and Blog post
–

Two content types (bundles) of node entity type
Not all entity types have more than one bundle
–
Eg: User
•
No subtypes; concept stands on its own
Why We Need Entity Bundles

Each entity type stores some data in a table
–
We call these pieces of data “properties”
–
Eg: node
•
•
Title, body, author (uid), created timestamp
But not all nodes are created equal
–
–
–
Page may have data beyond that captured by node
Blog posts get tagged
We need fields!
CCK: Content Construction Kit (D6)

Contrib module

Store additional data per content (node) content type

Eg: Fivestar
–
Contrib module leveraging contrib CCK API
–
Allows nodes to be rated (think 1-5 stars)
–
Eg: let users rate pages or blog posts
Fields (D7)

Core concept

Store additional data per entity type

Applies power of CCK to all entity types
–

Not just nodes!
Eg: Fivestar
–
Contrib module leveraging core Field API
–
Allows entities to be rated
–
Eg: let users rate pages, blog posts, users, comments,
custom entities
Entities in Core vs Contrib

Entities are a Drupal 7 core concept

Core provides the “Entity API”
–


Functions and hooks to define and interact with
entities, entity types, and entity bundles
Everything we've discussed so far is part of Drupal 7
core (no contrib modules necessary)
Not everything “made it into” core
–
Thankfully there's the “Entity API” contrib module
•
(confusingly named)
Entity API Contrib Module

Makes dealing with entities easier

Provides full CRUD functionality
–
CReate, Update, and Delete

Allows definition of metadata about entities

Optionally makes entity data
–
Exportable (for configuration)
–
Revisionable (eg: node revisions)

Optional administrative UI for managing entities

Object-oriented representation of entity types
A Note on Documentation


Documentation for core's Entity API is separate from
that for the Entity API contrib module
In practice, you'll probably always use the Entity
API contrib module
–

It provides so much awesome functionality!
Don't get confused
–
Always install the Entity API contrib module when
defining an entity
–
Make sure to read the module's documentation
Contrib Modules Defining Entities

Commerce
–

Organic Groups
–

Rules Configuration
Message (think activity stream)
–

OG Membership, OG Membership Type
Rules
–

Commerce Product, Commerce Payment Transaction
Message, Message Type, Message Type Category
… and many more!
Example Custom Entity Type


TextbookMadness.com
–
Classified listings for textbooks at schools
–
Online price comparison shopping (prices from
Amazon, Textbooks.com, etc.)
Online prices are
–
Fetched from a third-party API and stored by Drupal
–
Hereafter known as Offers
How Shall we Define an Offer?


We could use the UI to define an offer node with a
bunch of fields to store pricing information
But there's a lot of overhead!
–
Don't need/want a page (path) per offer
–
Don't need/want revisions
–
Don't need/want node base table information
•

Language (and translation info), Title, Author,
Published status, Created date, Commenting,
Promoting, etc.
We want an entity!
Implement hook_schema()
$schema['tbm_offer'] = array(
// SIMPLIFIED FOR SLIDE
'fields' => array(
'offer_id' => array('type' => 'serial'),
'isbn' => array('type' => 'int'),
'retailer_id' => array('type' => 'int'),
'price' => array('type' => 'int'),
'last_updated' => array('type' => 'int'),
),
'primary key' => array('offer_id')
);
Implement hook_entity_info()
$entities['tbm_offer'] = array(
'label' => t('Offer'),
'plural label' => t('Offers'),
'entity class' => 'Entity',
'controller class' => 'EntityAPIController',
'module' => 'tbm',
'base table' => 'tbm_offer',
'fieldable' => FALSE,
'entity keys' => array(
'id' => 'offer_id',
));
Make an Entity Type Exportable
$entities['tbm_offer'] = array(
'label' => t('Offer'),
'plural label' => t('Offers'),
'entity class' => 'Entity',
'controller class' => 'EntityAPIControllerExportable',
'module' => 'tbm',
'base table' => 'tbm_offer',
'fieldable' => FALSE, 'exportable' => TRUE,
'entity keys' => array(
'id' => 'offer_id', 'name' => 'my_machine_name'
));
Make an Entity Type Revisionable
$entities['tbm_offer'] = array(
'label' => t('Offer'),
'plural label' => t('Offers'),
'entity class' => 'Entity',
'controller class' => 'EntityAPIController',
'module' => 'tbm',
'base table' => 'tbm_offer', 'revision_table' => 'tbm_o_revision',
'fieldable' => FALSE,
'entity keys' => array(
'id' => 'offer_id', 'revision' => 'revision_id',
));
Other hook_entity_info() configuration
$entities['tbm_offer'] = array( …
'entity class' => 'TbmOfferEntity', // Extends Entity class
'controller class' => 'TbmOfferEntityController',
'access callback' => 'tbm_offer_access',
'admin ui' => array(
'path' => 'admin/structure/offers',
'file' => 'tbm.admin.inc',
),
'label callback' => 'entity_class_label',
'uri callback' => 'entity_class_uri',
); // TbmOfferEntity->defaultLabel(), defaultUri()
Entity Property Info



hook_entity_property_info()
–
Defines entity metadata
–
Provided for core entities
Automatically generated from hook_schema()
–
Doesn't understand foreign key relationships
(references)
–
Doesn't know when an integer is actually a date (eg:
unix timestamps)
Fill in the gaps with
hook_entity_property_info_alter()
hook_entity_property_info_alter()
$offer = &$info['tbm_offer']['properties'];
$offer['url']['type'] = 'uri'; // was a varchar in hook_schema
$offer['url']['label'] = 'URL';
$offer['last_updated']['type'] = 'date'; // integer in hook_schema
$offer['last_updated']['label'] = t('Last updated');
// Other types: text, token (machine name), integer, decimal,
duration, boolean, entity, struct, list<TYPE>
hook_entity_property_info_alter()
// Define a new property to reference an offer's retailer (eg:
Amazon.com)
$offer['retailer'] = array(
'label' => t('Retailer'),
'type' => 'tbm_retailer', // The tbm_retailer entity type
'description' => t('The retailer making the offer.'),
'required' => TRUE,
'schema field' => 'retailer_id', // as defined in hook_schema
);
Entity API Integrations

Views
–
Eg: in a view of offers, we can add a retailer
relationship and include details about each offer's
retailer

Rules

Search API

Features

I18n

Token system - Eg: [tbm_offer:retailer:name]
EntityFieldQuery

Tool for querying entities

Can query entity properties and field data

Can query field data across entity types
–

Eg: “return all pages and users tagged with
taxonomy term 456”
Returns entity IDs
–
Usually you’ll then load with entity_load()
EntityFieldQuery Example
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'page')
->propertyCondition('status', 1)
->fieldCondition('field_awesome_factor', 'value', 3.2, '<')
->fieldOrderBy('field_awesome_factor', 'value', 'DESC')
->range(0, 10);
// Top 10 Published page nodes with an awesome factor < 3.2
EntityFieldQuery Example
$result = $query->execute(); // Keyed by entity type
if (isset($result['node'])) {
$nids = array_keys($result['node']);
$pages = node_load_multiple($nids);
$same_as_pages = entity_load('node', $nids);
}
Entity Metadata Wrapper Examples
// Get node author's email address (given $nid)
// BEFORE
$node = node_load($nid);
$author = user_load($node->uid);
$email = check_plain($author->mail);
// AFTER
$wrapper = entity_metadata_wrapper('node', $nid);
$email = $wrapper->author->mail->value();
Entity Metadata Wrapper Examples
// Set node author's email address
$wrapper->author->mail = 'me@example.com';
$wrapper->save();
// Iterate over a list of node's taxonomy terms
foreach ($wrapper->field_taxonomy_terms->getIterator() as
$term_wrapper) {
$label = $term_wrapper->label->value();
}
More Modules!


Entity Construction Kit (ECK)
–
CCK for entities
–
Create entity types in a UI
Entity Cache
–

Entity Reference
–

Integrates entities with Drupal's Cache API
Like node reference field, but for any type of entity
Automatic Entity Label
–
Generic successor to Automatic Node Titles
More information

Entity API documentation
–
Lots of information in many subpages

EntityFieldQuery documentation

Entity API contrib module page

Deck is on Slideshare

Email me: jd at ModernBizConsulting.com

Follow me: @drupal_jd
Download