Connecting with the Community at Drupal Camp Asheville 2017

Last weekend I had the pleasure of attending Drupal Camp Asheville 2017 ('twas my fourth year in a row : ). I absolutely love this event and encourage you to consider putting it on your list of Drupal events to hit next year. The Asheville area is a beautiful (and delicious) place to spend the weekend, but the bigger draw for me is the people involved:

Drupal Camp Asheville 2017 group photo
(Check out all the pictures in the album on Flickr.)

Drupal Camp Asheville is always well organized (props to April Sides et al; seriously, it's in the best of the best small conferences for venue, amenities, and content) and attended by a solid blend of seasoned Drupal users / contributors and newcomers. I live only an hour away, so I get to interact with my Drupal friends from Blue Oak Interactive, New Valley Media, and Kanopi on occasion, but then on Camp weekend I also get to see a regular mix of folks from Mediacurrent, Code Journeymen, Lullabot, Palantir, FFW, CivicActions, end users like NOAA, and more.

This year we got to hear from Adam Bergstein as the keynote speaker. Unfortunately, that "we" didn't include me at first, as I managed to roll up right after Adam spoke ... but his keynote is on YouTube already thanks to Kevin Thull! I encourage you to give it a listen to hear how Adam's experience learning to work against his own "winning strategy" as a developer (that of a honey badger ; ) helped him gain empathy for his fellow team members and find purpose in collaborative problem solving to make the world a better place.

I gave a presentation of Drupal Commerce 2.x focusing on how we've improved the out of the box experience since Commerce 1.x. This was fun to deliver, because we really have added quite a bit more functionality along with a better customer experience in the core of Commerce 2.x itself. These improvements continued all the way up to our first release candidate tagged earlier this month, which included new promotions, coupons, and payment capabilities.

Many folks were surprised by how far along Commerce 2.x is, but now that Bojan has decompressed from the RC1 sprint, I expect we'll start to share more about the new goodies on the Drupal Commerce blog. (If you're so inclined, you can subscribe to our newsletter to get bi-weekly news / updates as well.)

Lastly, I loved just hanging out and catching up with friends at the venue and at the afterparty. I played several rounds of a very fun competitive card game in development by Ken Rickard (follow him to find out when his Kickstarter launches!). I also enjoyed several rounds of pool with other Drupallers in the evening and closed out the night with cocktails at Imperial Life, one of my favorite cocktail bars in Asheville. I treasure these kinds of social interactions with people I otherwise only see as usernames and Twitter handles online.

Can't wait to do it again next year!

Publishing Content on Relative Dates Using Rules in Drupal 7

A friend of mine is a DUI lawyer who uses a Drupal site for content marketing and lead generation, managed by my friends at EverConvert. They quickly identified the weekend as the best time for the website to directly appeal to visitors who found the site after a Friday or Saturday night arrest. In addition to live chat, they decided to alter the site's appearance by publishing content only during these times that contains large, direct calls to action.

Fortunately, Drupal provides the tools to build this with nary a line of code.

Introducing the Rules Scheduler

The primary module you'll use to create something similar is Rules with its Rules Scheduler sub-module. Most purpose built content scheduling modules that I've seen allow you to set absolute dates for pieces of content to be published or unpublished. However, you wouldn't want to have to enter in every single weekend date to get that content published at the right times. Fortunately, Rules Scheduler allows you to schedule arbitrary actions using relative date strings (in addition to any other format supported by strtotime()).

Before we dive into the configuration I proposed to them, you should understand that the Rules module basically adds a GUI based scripting language to the back end of Drupal. In addition to configuring actions to be performed after certain events when a set of conditions are met, you can create Rules components that are essentially subroutines that can be directly invoked by Rules (or code) without being triggered by events.

To setup a Rules Scheduler based content publishing schedule, you have to create two Rules components: one will publish the piece of content and schedule it to be unpublished on a relative date (i.e. "next Monday"), while the other will unpublish the piece of content and schedule it to be published on a relative date (i.e. "next Friday"). Another Rule will need to react to content being created or updated to initiate the publishing schedule.

Using Fields to Avoid "Magic" Behavior

One of the keys to building a maintainable Drupal site (or module) is to ensure that every "automated" action is explicitly enabled. In the early days of Drupal Commerce development, I adopted an approach to some module behaviors where a feature just automatically worked if certain conditions were met (e.g. the representation of attribute fields on Add to Cart forms). "Neat!" I thought, until I realized that it was difficult to document and even more difficult to ensure users knew to look for said documentation. Much better to include explicit user interface components to enable functionality.

In the case of a scheduling system, you wouldn't want to build the site to just automatically enter every piece of content, or even every piece of content of a certain type, into the publishing pattern. Your client, a.k.a. the end user, really expects (and needs) to see an indicator on a form that submitting it will lead to a desired outcome.

Checkbox enabling the publishing pattern for calls to action.

For my friend's site, my recommendation was simply to add a Boolean field using a checkbox widget to the relevant content type reading, "Schedule this content to be published on Fridays and unpublished on Mondays." If the site required more publishing patterns than just the weekend pattern, I would've used a List (text) field with radio buttons or a select list identifying the different available publishing patterns.

Building the Scheduling System in Rules

Working with Rules is fairly simple if you can write out long hand what it is you're trying to accomplish. Considering all of our constraints here, we need a set of rules that accomplishes the following:

  1. When a call to action is created or saved with the scheduling checkbox checked, delete any scheduled tasks related to the content. (This is possible because Rules Scheduler lets you assign an identifier to scheduled rules, so identifying our scheduled tasks with the node ID will allow us to delete the tasks when needed in the future.)
  2. If the call to action that was just saved isn't published, schedule it to be published next Friday.
  3. If the call to action that was just saved is published, schedule it to be unpublished next Monday.
  4. When a call to action is automatically published on Friday, schedule it to be unpublished next Monday.
  5. When a call to action is automatically unpublished on Monday, schedule it to be published next Friday.
Actions that queue up a publishing schedule.

The first three items will be accomplished through a single rule reacting to two events, "After saving new content of type Call to action" and "After updating existing content of type Call to action." The rule will first delete any scheduled task for the piece of content and then it will invoke two rules components that will schedule the appropriate task based on whether or not the call to action was published.

Components that manage the publishing schedule.

The final two items will be accomplished through rules components that perform the necessary action and then schedule the appropriate task. As mentioned above, we'll use relative time strings ("next monday" and "next friday") and choose task identifiers that include the call to action node IDs ("unpublish-call-to-action-[node:nid]" and [publish-call-to-action-[node:nid]" respectively).

Give it a whirl!

It only took me about 10 minutes to create and test the rules based on the specification above, but if you aren't familiar with the Rules UI, it could take much longer. I believe Rules is worth learning (we built Drupal Commerce around it, after all), but there's something to be said for ready made examples.

I've attached to this post a Features export of the content type and related rules configurations for you to try on your own site. Give Scheduled Calls to Action a whirl and let me know how it works for you in the comments!

(Note: to see the rules configurations and scheduled tasks, enable the Rules UI, Views, and Views UI modules if they aren't already enabled on your site.)

Hello {you} with Slim Framework 3.x

Last week I wrote my Slim 3.x "Hello World" tutorial after hearing Josh Lockhart introduce the framework at our UpstatePHP meet-up. I was eager to learn the basics of using Slim as an HTTP router, and since then I've continued to learn new things (yay!), like using named routes / arguments and its dependency injection container (based on SensioLabs's oddly named Pimple).

Let's break it down here...

Named Routes

Defining a route is pretty straightforward. Slim's App class gives us methods to add routes named after their correlating HTTP methods (e.g. get(), post(), etc.). To give a route a name, you simply append a call to setName() to its definition:

<?php
$app
->get('/hello', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
  return
$response->write('Hello, world!');
})->
setName('hello-world');
?>

Why would you want to do this? There may be times in your application where you want to redirect from one route to another or create a link to another route. Instead of hardcoding route paths into your code, use Router::pathFor() to let Slim generate the path for you:

<?php
$app
->get('/some/other/route', function($request, $response, $args) {
 
$path = $this->getContainer()->get('router')->pathFor('hello-world');
 
// ...
});
?>

In Slim 2.x and prior, this was named urlFor(). Yours truly played semantic stickler to propose renaming it in a pull request that was recently merged by Rob Allen, a.k.a @akrabat, who offers a great intro to Slim 3.x in his Slim 3 Primer and numerous blog posts.

Dynamic Routes with Named Arguments

In addition to static routes like the ones defined above, Slim depends on FastRoute to support dynamic routes that use named arguments in their path patterns. Curly braces identify named arguments in a route's pattern, and regular expressions further determine what request paths match a route:

<?php
// Add a route for a personal greeting using an argument.
$app->get('/hello/{name:[A-Za-z]+}', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
  return
$response->write('Hello, ' . $args['name'] . '!');
})->
setName('hello-name');
?>

To create a path for this route, I would use the same function from above but pass it an array including the named argument:

<?php
// From inside a route closure...
$path = $this->getContainer()->get('router')->pathFor('hello-name', ['name' => 'Shady']);
?>

The regular expression in the pattern will only permit a match to this route if the request path includes a second argument containing only upper or lower case letters. Throw a number, a space, or punctuation in there, and you'll get a 404. Granted, if I were actually writing an application, I would use a template engine like Twig to automatically escape variables when generating output.

Slim's Dependency Injection Container

Ahh, what a fine segue that was!

To use Twig in a Slim application, you might make use of Slim's dependency injection container (or DIC). I'm still relatively new to the concept, so I won't try to explain the design pattern in depth.

At a high level, the DIC is an object that your application uses to find other objects that encapsulate discrete bits of functionality. Pimple refers to these objects as services, and they may be responsible for communicating with the database, sending mail, or rendering output through a template engine.

When you create a new Slim App object, you pass in the container object like so:

<?php
// Prepare the Pimple dependency injection container.
$container = new \Slim\Container();

// Add a Twig service to the container.
$container['twig'] = function($container) {
 
$loader = new Twig_Loader_Filesystem('templates');
  return new
Twig_Environment($loader, array('cache'));
};

// Create the Slim application using our container.
$app = new \Slim\App($container);
?>

Now any route can make use of the Twig service to build output via a template:

<?php
$app
->get('/hello', function(Slim\Http\Request $request, Slim\Http\Response $response) {
 
// Load the template through the Twig service in the DIC.
 
$template = $this->getContainer()->get('twig')->loadTemplate('index.html');
 
// Render the template using a simple content variable.
 
return $response->write($template->render(['content' => 'Hello, world!']));
})->
setName('hello-world');
?>

The Slim DIC also contains an array of settings that you can append values to when you construct it by passing in an associative array of settings:

<?php
// Prepare the Pimple dependency injection container.
$container = new \Slim\Container([
 
'site_name' => 'Slim Shady',
]);
?>

These settings are appended to a default settings array and may be used elsewhere in the code. Slim binds route closures (the function we defined in above examples as the second argument to the $app->get() calls) to the $app object, so a quick reference to the site_name setting would look like:

<?php
$app
->get('/hello/{name:[A-Za-z]+}', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
 
// Load the template through the Twig service in the DIC.
 
$template = $this->getContainer()->get('twig')->loadTemplate('index.html');
 
// Render the template using content and site name variables.
 
return $response->write($template->render([
   
'content' => 'Hello, ' . $args['name'] . '!',
   
'site_name' => $this->settings['site_name'],
  ]));
})->
setName('hello-name');
?>

You can learn more about the DIC in the Slim 3.x dependency injection documentation or Rob Allen's comprehensive blog post on accessing services in Slim 3.x.

A final word...

I'm used to using the Devel module for debugging when writing Drupal modules. I was a bit lost writing PHP outside of Drupal at first, but then I remembered that Devel for Drupal 8 uses the Kint library to provide fantastic variable / backtrace debugging support.

To use it in your Slim application, add it to your composer.json file and the autoloader will allow you to make automatic use of it. The composer.json for my little learning application is currently:

{
    "require": {
        "slim/slim": "dev-develop",
        "raveren/kint": "^1.0",
        "twig/twig": "~1.0"
    }
}

Adding a simple line like the following to a route closure will produce some easily navigable debug output to the page:

<?php
d
($this->settings);
?>

Seriously... it's magic. There's never been a better time to lose yourself in the PHP Renaissance.

Hello World with Slim Framework 3.x

We were fortunate to have Josh Lockhart (a.k.a. @codeguy) join us at tonight's UpstatePHP meet-up. He is the creator of Slim Framework and author of Modern PHP (should be in your library) / PHP the Right Way (should be in your bookmarks), which basically makes him a leader of the PHP renaissance.

I loved PHP the Right Way but hadn't heard of Slim yet, so it was great to hear him present the upcoming 3.x version this evening.

Slim currently describes itself as "a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs." Slim provides you with an intelligent router to handle incoming HTTP requests. It implements HTTP request and response objects according to PSR-7 and manages a simple first-in-first-out stack for each route (made up of "middleware" callbacks and the route's own callback) that it invokes in turn to generate a response.

I'm easily distracted by new things, so I couldn't wait to get home and put together a quick "Hello, world!" application using Slim 3.x.

To get started, I created a quick local project directory (yep, slimjim), changed into it, and grabbed the framework and its dependencies using Composer:

composer require slim/slim:3.x-dev

I then created a small index.php file that includes Composer's autoload file, instantiates a new Slim application object, and registered a simple GET request route. Routes require two parameters, a URI pattern (that may include argument tokens) and a PHP callable that accepts request, response, and argument parameters.

Here I've used a simple closure that echoes the greeting:

<?php
require 'vendor/autoload.php';

$app = new \Slim\App();

$app->get('/', function($request, $response, $args) {
    echo
'Hello, world!';
});

$app->run();
?>

To test it out, I fired up PHP's built in web server:

php -S localhost:8000

And then navigated to http://localhost:8000 to see my handsome message.

I made a backwards compatibility inspired gaffe on purpose in my first test by echoing the output directly. In Slim 3.x, your callback is really supposed to write output through the response object and return it instead. It still supports the echo method from previous versions through some ob_start() trickery, but you really should stop doing that and write better code like so:

<?php
require 'vendor/autoload.php';

$app = new \Slim\App();

$app->get('/', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
   
$response->write('Hello, world!');
    return
$response;
});

$app->run();
?>

Note that I'm also a recent phpStorm adoptee, so type hinting my closure's parameters makes for a much nicer development experience. Time to step up!

I look forward to playing with Slim some more.

Pages