Sammi Sinno

Development - Off The Beaten Path

A development blog that explores everything and puts an emphasis on lesser known community driven projects.

Home

Content Management Systems - Coming Full Circle

Wednesday, July 17, 2019

It's been a while since I've worked with a true monolithic CMS system like Drupal, but here I am again plugging away and trying to find the best way to work with it (or work around it). I took an absence from using coupled CMS systems for a lot of reasons, first and foremost was the hard dependencies on things like templating, databases and routing. What if I want to choose my database or if server side routing doesn't fulfill my expectations entirely? What if I don't want a denormalized database? As an architect these are all of the things I would love to be able to choose myself, or do I?

Lando - "Yeah, I'm responsible these days. ..."

The first questions I had was how are we going to orchestrate local development along with scaling and deployment to production. Docker is what came to mind first, after doing a little research we realized that there was a tool called Lando that is built on top of docker. You could imagine my surprise when I realized that Lando is basically docker-compose on steriods:

So what did we have to do to get up and running with Lando? We included the Drupal recipe, ran lando start and as the Lando console would say BOOMSHAKALAKA!! Our site was up and running with several containers (database, mailhog, nginx/php and solr). Some of this required a little bit more configuration but was extremely simple. After the site is running, Lando also makes it easy to access certain tools from within the containiers. By simply using commands like lando npm, lando drush, lando blt ect.. we could access those processes seamlessly.

Drush

Drush is a command line interface provided with tools specifically for Drupal. There is a myriad of things you can do with Drush out of the box, but if it doesn't come with the functionality you need it is actually extremely easy add on commands in a custom Drupal module.

Drush can also be a very useful tool when it comes to setting up your continuous integration process. It comes with a number of commands that you can use to generate not only content items, but users as well. With just a few simple commands we were able to do a Drupal site install, generate content and users as well as spin up a site for testing:

drush si --existing-config --yes --account-name=admin --account-pass=admin
drush en devel_generate
drush genc 4
drush genu
drush runserver http://127.0.0.1:8080/ & npm run test

Notice that I concatenated the runserver and npm run test command since runserver does not exit or run in the background.

Configuration Before Code

Coming back full circle to the actual development aspect of CMS systems, I wasn't expecting to be surprised by anything, just the same old approach to problems we've figured out how to solve in other ways. Low and behold, a configuration management system based on YAML? What is this and why do I need it? Configuration management with CMS systems in the past have always been cumbersome and extremely manual. Not to mention they have always used XML as a data format to share changes across environments. Drupal has made it extremely easy to make changes to content definition without worrying about applying those same changes everywhere else. When we are ready to commit our changes we simply do a drush cex to export all of the configuration or content definition changes we have made through Drupal. Whenever you are pulling changes from your main repo you can do drush cim or drush cim --partial --source=only-few-configs/ if you only want to import certain configs.

Theming

One of the core features that Drupal offers is a layout management system built on blocks and views. Blocks can be anything you want them to be and are necessary for plugging html into the page while views are easily configurable lists of any content type in the system. One challenge we initially faced was how do we override the default template that Drupal provides for the view or block? Well it ended up being quite simple, once you've created a block and placed it on the page you can then click on inspect element on that block and you will see comments that list out the possible TWIG template names that you can use:

If you poke around any theme you'll see a templates folder with a lot of different template types under it, what I just showed you is an example for blocks, but it may be a good idea to look up other Drupal node types that you can create templates for (for instance fields, forms, menu items ect..). TWIG templates are great because it forces developers to keep logic out of the templates and ultimately place them in custom modules.

Custom Development

While one of the arguments against using CMS systems is that they impose too many standards on you and force you to do things a certain way, I would argue that in a lot of cases that is a good thing. Whether it is comments, naming conventions or project structure a CMS system like Drupal helps immensely when enforcing standards. One of the first things I noticed when first diving into the solution is that Drupal has custom PHP code sniffer rules that are automatically picked up and used during linting. Some of the rules may not comply with how you want to do things, but it is actually very simple to exclude them from the linting process (using the phpcs.xml.dist file in the root of the project):

  <!-- Include existing standards. -->
  <rule ref="Drupal"/>
  <rule ref="DrupalPractice">
    <!-- Ignore specific sniffs. -->
    <exclude name="DrupalPractice.InfoFiles.NamespacedDependency"/>
    <exclude name="Drupal.Commenting.FunctionComment"/>
    <exclude name="Drupal.Commenting.ClassComment"/>
    <exclude name="Drupal.Commenting.VariableComment"/>
  </rule>

Just like that we were able to customize our PHP linting rules (and its also very easy to add new rules using composer) and then we were off and running!

One of our requirements was to have some SPA functionality with an API backing it. While Drupal comes with a lot out of the box, we wanted to spin up API endpoints to deliver data that was customized to what we needed. With Drupal (and Symphony) this was extremely simple:


namespace Drupal\pem_content\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;

class TestController extends ControllerBase {    
    public function content() {
        $response["data"] = "My custom test data!"
    	return new JsonResponse($response);        
    }
}

In order for you controller to be discoverable by Drupal you need to create src/Controller folder in your module folder. From there we then needed to wire up the route using a {module_name}.routing.yml file in the module:

test_module.content:
  path: '/test-content'
  defaults:
    _controller: '\Drupal\test_module\Controller\TestController::content'
    _title: 'Test Route'
  requirements:
    _permission: 'access content'
  methods: [GET]

The routing YAML file allows us to set the path of the route as well as control access to the route. We can also specify which HTTP methods are allowed, in this case we are only allowing GET requests.

One of the more frustrating parts of development was a lot of changes that we did required us to clear the cache before we could see them reflected on the site. It is extremely simple to do this by running drush cr which will rebuild the site and clear all of its caches. We also added this line to our settings.php file:

$settings['cache']['bins']['render'] = 'cache.backend.null';

Conclusion

I haven't nearly begun to scratch the service with what Drupal can do, but I hope this was a good enough introduction to how to build modern websites using Drupal and can help newbies like myself get up and running quickly.

While on my journey I compiled a list of extremely helpful links and wanted to share them here as well:

  • https://opensenselabs.com/blog/tech/how-build-single-page-application-drupal-8-and-vue-js
  • https://ffwagency.com/learning/blog/managing-css-and-javascript-files-drupal-8-libraries
  • https://www.drupal.org/docs/8/api/routing-system/structure-of-routes
  • https://www.drupal.org/project/content_export_yaml
  • https://www.drupal.org/docs/8/creating-custom-modules/adding-a-basic-controller
  • https://www.drupal.org/docs/8/configuration-management/managing-your-sites-configuration
  • https://api.drupal.org/api/drupal/includes%21module.inc/group/hooks/5.x
  • https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code