For this project, I have the front-end running as swift on the server (using the vapor framework). For the backend data store, I’m using a Drupal 8 site. One issue I ran into very early on was getting inline images stored in the body of a Drupal node to render correctly via vapor’s leaf templates.
The issue was that when you use the ckeditor wysiwyg to add an image in the body of a Drupal node, Drupal only stores the relative path to that image. ie: sites/default/files/styles/my_style/public/2018-07/image.jpg instead of https://www.mydomain.com/sites/default/files/styles/my_style/public/2018-07/image.jpg. Of course, when leaf (or any front-end working with a decoupled Drupal back-end) tries to render this image on the server side, it can’t find sites/default/files/styles/my_style/public/2018-07/image.jpg.
How to alter the view in order to add the domain name and force each image to be an absolute url? I found a couple of ways. The first way (which I didn’t end up using) is the REST Absolute URLs module. This module is not covered by the security advisory policy and at the time of this writing only has 44 sites reported as using it. I usually like to see usage numbers a bit higher than that in order to feel good about using this module in a production setting. I am however excited about taking a look at this module and spending some time experimenting with it for development purposes. Maybe it can eventually replace the custom solution that I settled on below.
The second way (which I did end up using) is to create a custom views style plugin for serialized output. That sentence makes this sound much more complicated than it is.
First, create a custom module in your Drupal 8 install. If you don’t know how to do that, I’d suggest checking out the docs for drupal console. Specifically the generate:module command: https://docs.drupalconsole.com/en/commands/generate-module.html.
With your new custom module in place, you’ll need to add your custom plugin class. To do so, navigate to the root directory for your custom module. In this example our custom module is called my_module and it’s located in web/modules/custom/my_module. Our site is in /var/www/my_site, so we issue the following command in the terminal:
Now there should be a directory there called src. If there’s not, then feel free to create it:
Now you’ll need to create a directory hierarchy underneath src in order to create your custom plugin:
cd ./src mkdir -p Plugin/views/style cd ./Plugin/views/style
Drupal 8 uses the PSR-4 standard for class autoloading. The above commands set up the directory hierarchy you need in order for Drupal to autoload your new plugin class. For more information on Drupal 8 autoloading and PSR-4 see https://www.drupal.org/docs/develop/coding-standards/psr-4-namespaces-and-autoloading-in-drupal-8 and https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md.
Now we’re ready to create our new plugin class. The new class will inherit from the base Serializer class and override that class’ render() method. I’ve called the new class ContentSerializer, and so the name of the file is ContentSerializer.php. You can name the class anything you like, but it’s important to remember that in order for Drupal 8 to recognize and autoload your class, your filename needs to match your class name. For example if you call your class MyClass, then the file should be called MyClass.php
Let’s walk through this code. In the first two lines, we declare a namespace for this class and issue a ‘use’ statement for the base Serializer class. The namespace is important as it’s used in autoloading. The namespace is ‘Drupal’ then your custom module name, then the path from your src directory to your views style plugin directory:
The ‘use’ statement allows us to access the Serializer class without having to use the fully qualified namespace each time we need to refer to it. ie - We can just use ‘Serializer’ instead of \Drupal\rest\Plugin\views\style\Serializer in the body of our class.
Now take a look at the comment block that precedes the class. The @ingroup and @ViewsStyle annotations are important. Without the @ViewsStyle annotation information, this plugin won’t be detected and the code will not work. For more information on annotation-based plugin discovery, take a look at https://www.drupal.org/docs/8/api/plugin-api/annotation-based-plugins-in-views.
Now we come to the class itself. As mentioned before, the new plugin class extends the Serializer base class. We want to override the Serializer::render() method. Within our overridden method, we of course first call the parent render method and then decode the result of calling that method. Now we have the response data in array form and we can look to alter it in any way we choose. In our case, we’ll be changing the relative image paths to URIs so that our front end can render these images properly.
As such, we cycle through each element of the $data array (each returned row of our view) and pull out the body field. We then run a preg_match_all to match any ‘’ tag’s ‘src’ attribute. Next we cycle through the list of matches and execute a string replace that changes the original src attribute by prepending our protocol and domain name. Lastly, encode the altered $data array and return it as the response.
Great! That was easy, right? The last thing to do in order to make this work is to clear Drupal’s cache and then edit the view that you want to alter the response for. Under the ‘Format’ section of the view, click the link that reads ‘Serializer’. In the resulting dialog, choose the new plugin you’ve just created. In our example, ‘Content Serializer’. Click ‘Apply’ and choose which formats you wish to allow in your responses. Now save the view and presto! You should start to see the changes your plugin makes show up in the view response.