FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607
Staying Sane with Drupal NEPHP
1. Staying Sane with Drupal
A Developer's Survival Guide
Oscar Merida, @omerida
Northeast PHP, Boston
August 2015
2. 80% of building sites with
Drupal is configuration (easy)
Credit: https://www.flickr.com/photos/jeepersmedia/12953571545/
3. The hard part is the final
20% of customization
• Functionality that is
new to Drupal
• or different than
assumptions
• Integration with
external systems
Credit: https://www.flickr.com/photos/jurvetson/15393495039
4. Drupal is procedural
• Not Object Oriented
• Many structures like nodes, forms, users, etc use StdClass
objects or arrays
• Not MVC like other PHP frameworks
• but there is separation of concerns between menu callbacks
& theme layers
• Hooks afford a way to alter existing functionality or implement
new features.
• Leverage hooks
5. Play Well with Others
• Don’t short circuit how
things work.
• If you're stuck, ask for help.
• "Always code as if the
person who ends up
maintaining your code is a
violent psychopath who
knows where you live."
• http://c2.com/cgi/wiki?
CodeForTheMaintainer
6. Use Drupal APIs
• EntityFieldQuery
• Form ArrayPI
• node_load(), node_presave(), node_* hooks
• Node Access System
7. Dig into the Documentation
• api.drupal.org has function definitions and
examples.
• Security -
https://www.drupal.org/writing-secure-code
• Check Project Issue Queues
9. Rules of the Road
• Use Version Control (git, svn, etc.)
• Use branches to manage workflow
• Replicate production environment in a VM
• Rule 0.1 - never edit files on live
10. Follow Drupal's coding standard.
• Everyone on the team must follow it.
• https://www.drupal.org/coding-standards
• Don't waste cycles arguing about which
standard to follow.
• Can use your IDE to follow a standard.
• BONUS: Use PHP CodeSniffer to enforce it.
11. Use Features
• Drupal saves a lot of configuration to the database,
• makes code sharing & deployments difficult.
• Use Features + Strongarm modules to export settings to
Features modules.
• Create one Feature module for each Content Type or Function
• Include related fields, Views, Display Suite settings, Panels &
Pages, Contexts, Path aliases, Rules,etc.
• Have a Base module with shared fields and global settings
like input formats.
12. Share Features
• Track your modules in VCS
• When you update codebase, check Features UI for
Overrides and resolve them.
13. Automate Deployments
• As simple as having a simple script to sync files
to your environments
• Or automagically update dev, stage, production
by pushing to the correct VCS branch
• https://www.freelock.com/node/1108
• BUT remember to keep sensitive information out
of VCS (like database credentials).
14. Don't Fear the Command Line
• Drush is faster and easier to automate than
going through the UI.
• Clearing caches - drush cc all
• DB Snapshot - drush sql-dump > ../mydb.sql
• Creating a user - drush user-create
• Reindexing search - drush search-index
16. Focus on building new solutions.
• Avoid “Not-invented-here” syndrome
• There's a module for that. In fact, there's
probably several …
http://dilbert.com/strip/2014-08-11
17. But with great power…
• Inherit technical debt
of a module.
• Keep up-to-date with
new releases,
especially security
releases.
• Sometimes you don't
need a module to
change things.
18. Evaluating a module
• Does the maintainer have other modules?
• Does it have a stable release?
• Does it have recent commits?
• Is the issue queue active?
• Does it have good documentation?
19. Practically core…
• Views (is in D8)
• Views Bulk Operations
• Features & Strongarm
• Pathauto & Token
• Webform
• Rules
• Email
• Link
• Smart Trim
• Redirect
• Entity Reference
• Entity API
• Entity Cache
20. Nice to Have
• Bean (better Blocks)
• Administration Views
• Display Suite
• Backup & Migrate
• Content Locking
• Menu Block
• Revisioning
• Add Another
22. Views
• GUI based Query Builder
• Has its own terminology
• Excels at building
display output.
• Not limited to HTML.
https://commons.wikimedia.org/wiki/File:Tower_Optical_Binoculars.jpg
23. Fields & Filters
• Fields - the fields to return from the query
• SELECT id, name, birthdate
• Filters - conditions to select items
• WHERE name=“smith”
24. Contextual Filters
• Apply filters based on external arguments.
• Filter values usually come from the URL path.
• Node ID
• User ID
• Term ID
25. Relationships
• Add a related table to the base query
• JOIN groups ON (groups.id=users.group_id)
• Can then pull in fields from other entites into
Fields, Filters, and Contexts.
26. Use an Existing View Programatically.
• views_embed_view(): Useful for building custom blocks
with logic while keeping them configurable in the Views UI.
function mymodule_view() {
$tid = (int) arg(2);
$preferred = views_embed_view('dept_resources', 'block_1', $tid);
// test if the view is empty by looking for view-empty class
if (false !== strpos($preferred, 'class="view-empty"')) {
$all = views_embed_view('dept_resources', 'block_2', $tid);
$block['content'] = $all;
} else {
$block['content'] = $preferred;
}
return $block;
}
27. Views Hooks
• Views API provides a number of hooks for
changing a view.
• hook_views_pre_view()
• hook_views_pre_ender()
• hook_views_query_alter()
• and more!
https://api.drupal.org/api/views/views.api.php/group/views_hooks/7
28. hook_views_pre_view()
• Runs at start of processing. Useful for changing
or cleaning up arguments.
function foo_views_pre_view(&$view, &$display_id, &$args) {
if ('map_legend' == $view->name) {
// get location short name from aliased view pat
// ex. "campus/fairfax-va"
$shortname = parse_path_shortname();
// get the campus node with the matching shortname
$node = foo_campus_lookup_shortname($shortname);
$args[0] = $node->nid;
}
}
29. hook_views_pre_render()
• Runs at start of rendering.
• Useful for changing titles, adding headers & footers,
replacing placeholders.
function foo_views_pre_render(&$view) {
if ('campus_map' == $view->name) {
if (isset($view->result[0]->nid)) {
$node = $view->result[0]->_field_data['nid']['entity'];
$view->set_title('Campuses for ' . $node->title);
}
return;
}
30. hook_views_query_alter()
• Alter a query before it runs.
• Helps deal with input edge cases.
function foo_views_query_alter(&$view, &$query) {
if ('campus_contacts' == $view->name) {
if ('mid-state' == arg(2)) {
$query->where[0]['conditions'][0]['value'] = 'Mid-State';
}
}
}
31. but sometimes…
• You need to query for a collection of Entities
• In that case, use EntityFieldQuery
• https://www.drupal.org/node/1343708
• Unless you're reading & writing your own custom
tables, don't use db_query().
32. function foo_lookup_shortname($short)
{
// now look up the node for this short to make sure it exists
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'campus')
->propertyCondition('status', 1)
->fieldCondition('field_short_name', 'value', $short, '=')
->range(0, 1);
$result = $query->execute();
if (!$result || empty($result)) {
return false;
}
$ids = array_keys($result['node']);
$nodes = node_load_multiple($ids);
$node = array_pop($nodes); // return first
return $node;
}
34. Custom modules for:
• Implementing hooks
• Integration with 3rd party systems
• Custom entities
35. Use Permissions
• hook_perm defines permissions
• Check for custom permission in routes, views, etc.
• Avoid checking for one or more roles.
/**
* Implements hook_permission().
*/
function foo_email_digest_permission() {
return array(
'administer foo digest' => array(
'title' => t('Administer FOO Digest'),
'description' => t('Manage Digest settings.'),
),
);
}
36. Use Node Grants
• hook_node_grants defines
what "realms" a user works
with.
• A user can belong to one or
more groups in a realm.
• https://www.phase2technology.com/
drupal-7-node-access-grants-locks-
and-keys/
function foo_node_grants($account, $op) {
$grants = array();
// If a user is an admin then grant access to all
if (user_has_role('Site Admin', $account)) {
$admin_nids = foo_get_workspace_nids();
$grants['workspace_member'] = $admin_nids;
} else {
$ws_nids = foo_get_users_ws_nids($account);
// add the workspace nodes that the user
// is an owner of too.
$owner_nids = foo_get_owner_ws_nids($account);
$grants['workspace_member']
= array_merge($ws_nids, $owner_nids);
}
return $grants;
}
37. Use Node Grants, pt 2
• hook_node_access_records
control access to nodes.
• Control user access for
viewing, updating, deleting at
the node-level.
• Integrates with views and
search results.
function foo_node_access_records($n)
{
// don't clutter grants
if ('resource' !== $n->type) {
return;
}
$ws_nid = $n->field_workspace['und'][0]['target_id'];
if (empty($ws_nid)) {
return;
}
$grants = array();
$grants[] = array (
'realm' => 'workspace_member',
'gid' => $ws_nid,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
return $grants;
}
40. Expose tasks via Drush
• Make functionality
available via drush
• http://www.drush.org/en/
master/commands/
• Can then execute
them in Terminal
• easier to schedule
via cron too
/**
* Implements hook_drush_command().
*/
function foo_email_digest_drush_command() {
$items['foo-digest-send'] = array(
'description' => 'Send email digest.',
'aliases' => array('foodig'),
);
return $items;
}
function drush_foo_email_digest_foo_digest_send() {
foo_digest_send();
}
41. Best Practices
• Create an admin settings form
• Keep editable settings outside of code
• Consider having switches to toggle functionality
on/off
• Provide theme functions
• Themes and other modules can override
rendered display
43. Basics
• t() - Output a translatable string with replacements
• check_plain() - Output sanitized text. Important if
you’re taking user input and displaying it.
• filter_xss() - Output sanitized text, allow some HTML
tags. Important if you’re taking user input and
displaying it.
• See also filter_admin_xss()
• arg() - Get an argument from the URL path.
Remember to filter input!
44. get_current_user()
• Retrieve the user object for the current user.
• Better than global $user;
<?php
function foo() {
$user = get_current_user();
if (0 == $user->uid) {
drupal_goto('user/login');
}
}
45. current_path()
• Returns the current unaliased path. Useful if a
hook should only run on one or more specific
paths.
if ('node/15' == current_path()) {
// customize this node
}
if ('publications' == current_path()) {
// customize this node
}
46. drupal_get_path_alias()
• Returns the aliased path for an internal path.
// check alias of current page
$alias = drupal_get_path_alias();
if ('about' == $alias) {
// customize the 'About Us' page
}
47. menu_get_item()
• Returns the currents menu item path. Similar to
current_path(), easier to test if something
should only run on a set of pages.
$menu = menu_get_item();
// make sure we're on any user profile page
if ('user/%' !== $menu['path']) {
return
}
48. drupal_goto()
• Redirect client to another URL. Allows other
modules to rewrite paths.
if (0 == $user->uid) {
drupal_goto('user/login');
}
50. drupal_set_message()
• Display success, warning, or error messages to
client (web or Drush).
<?php
function foo() {
$node = node_load('node/' . $nid);
// ...do some stuff to $node
// something went wrong
drupal_set_message(
'Could not use node',
'warning', // status|warning|error
);
}
51. Thank You. Questions?
• Follow me @omerida
• Slides:
• http://phpa.me/drupal-sane-nephp
• Feedback:
• https://joind.in/talk/view/14727
• Editor-in-chief of php[architect]
• Check out http://world.phparch.com