Drupal Planet

Subscribe to Drupal Planet feed
Drupal.org - aggregated feeds in category Planet Drupal
Updated: 2 min 11 sec ago

Amazee Labs: Extending GraphQL: Part 2 - Types and Interfaces

August 24, 2017 - 5:27am
Extending GraphQL: Part 2 - Types and Interfaces

After successfully creating a field with arguments and context, we are going to have a look at types and interfaces in GraphQL and how they help to build complex, yet self-documenting and type safe schemas.

Philipp Melab Thu, 08/24/2017 - 10:27

The last blog post in this series culminated in the epic achievement of adding a "page title" field to every URL object in our schema. Now we can request the page title for every internal URL. But menus and link fields can also store external addresses.

Wouldn't it be cool if we can request their page title's just the same way?

Overriding a field

Let's try it and ask questions later:

query { route(path: "http://www.drupal.org") { pageTitle } }

Unfortunately, this doesn't work out. The route field checks if the provided path is a Drupal route and if the user has access to it, and will return null if either of the two doesn't apply. So, the first thing we will do is extend the route field so it also can handle external URLs.

Note: At the time of writing there is a pending pull request that adds exactly this enhancement. If you are reading this in a couple of weeks from now (my now, not yours - unless you own a DeLorean), there's a chance that this already works for you. But since this is a nice example of overriding a field, we stick with it. If you don't just want to read but really play through this tutorial, make sure you work based on the 8.x-3.0-alpha3 version of the GraphQL module.

We create a new field called ExampleRoute in our graphql_example module. If you are not yet proud owner of one, please refer to the last blog post. This new field simply extends the existing Route field and even copies its annotation.

With one difference: We add a new property called weight which we set to "1". It's quite simple. When the schema builder assembles all field plugins for a given type and stumbles upon two with the same name, the higher weight takes precedence. That's how we tell GraphQL to use our custom implementation of a field.

The resolveValues method checks if the path is an external Url. In this case, it just constructs a Url object, else it will pass down to the parent implementation.

The result is still not satisfying. The route field now returns an Url object, but our page title field can only retrieve internal page titles.

So let's modify the PageTitle field. First, we check if the current value is a routed URL. In this case, we still leave it to the title resolver. Otherwise, we fire up Drupal's http_client (aka Guzzle), fetch the content behind the address, load it into an XML document, search for the title element and yield its contents. I am aware that this is not the most performant solution, but I'm trying to keep these examples short and concise.

It worked. Our query for an external page title yields the correct result.

{ "data": { "route": { "pageTitle": "Drupal - Open Source CMS | Drupal.org" } } }

The result is correct, but it doesn't feel right. Internal and external URLs are fundamentally different. The page title might make sense on both, but the similarities end there. External URLs won't route to an entity or provide any other information specific to Drupal. These fields won't break and will just return NULL instead, but that doesn't seem very elegant.

Diff: Page title of external URLs

Interfaces and Types

We have already met the Url type, and we know that it connects a certain value with a list of fields that can be executed on it. A GraphQL interface is in some ways similar to interfaces in an object oriented language. It gives a group of types with shared fields a common name.
Right now we've got the Url type provided by the GraphQL module, representing internal URLs (not 100% true, but for the sake of simplicity we leave it there). And we have our external URL which is emitted by the same route field, but operates differently. So what we need to do now:

  1. Create a GraphQL interface called GenericUrl
  2. Change the route field to return this interface instead.
  3. Attach our pageTitle field to this interface.
  4. Add a ExternalUrl GraphQL type that implements this interface.
Creating the interface

GraphQL interfaces live in their own plugin namespace Plugin\GraphQL\Interfaces where the schema builder will pick them up.

The plugin annotation for interfaces is quite simple. In most cases, it consists of the plugin id and a name to be used within the schema. The base class for interfaces contains an abstract method: resolveType. This method will receive a runtime value and has to select the appropriate GraphQL type for it. In our case, it checks if the URL is external or not and uses the schema manager service to return an instance of either Url or ExternalUrl.

Using the interface

This won't have any effect as long as we don't use this interface type somewhere. So we change the pageTitle field to attach it to the GenericUrl instead of Url and adapt our override of the route field to return a GenericUrl.

Creating the new type

The new type we need is rather simple. It's an empty class, extending TypePluginBase. The most important part is the annotation that defines a list of interfaces. Just the GenericUrl interface in our case.

GraphQL type source

Diff: Generic Url interfaces

Now our query still works. But there is a new problem. Internal URLs don't work anymore but emit an error message instead:

Type "Url" does not implement "GenericUrl"

We need to adapt the of the Url type, which is defined in another module. Sounds like a job for the hero we don't deserve, but we need right now. You can't say Drupal without screaming hook_alter from the top of your lungs!

Altering plugins

There's an alter hook for each plugin type in GraphQL. So, all we need is to implement hook_graphql_types_alter and add the GenericUrl interface to the Url types interface list.
Note that the types are indexed by their plugin-ID.

Diff: Altering existing plugins

Great! Now we are able to fetch page titles from both internal and external urls.

query { admin:route(path: "/admin") { pageTitle } drupal:route(path: "http://www.drupal.org") { pageTitle } }

Will return:

{ "data": { "admin": { "pageTitle": "Administration" }, "drupal": { "pageTitle": "Drupal - Open Source CMS | Drupal.org" } } }

But you will notice that we lost all the other fields attached to the Url type. Thats because they are not attached to the GenericUrl type, but to the Url type. And that makes sense, since you can't request for example an entity or the current user context for an external path.

Query composition and fragment selection

And this brings us to the most important and powerful aspect of interfaces and types. We are able to apply different query fragments and fetch different information based on the result type.

Assume the following scenario: Our Article type has a Links field that can contain links to either other articles or external URLs, as well as a Description field. Additionally, we extended our ExternalUrl type with an additional meta field that pulls meta tags out of the XML tree (Bonus objective: implement that yourself). Now we could do this:

query { route(path: "/node/1") { ... on Url { nodeContext { ... on NodeArticle { fieldLinks { url { pageTitle ...InternalLink ...ExternalLink } } } } } } } fragment InternalLink on Url { nodeContext { ... on NodeArticle { description:fieldDescription } } } fragment ExternalLink on ExternalUrl { description:meta(property: "og:description") }

The first part simply routes to the article with id 1 and fetches it's Links field, which will emit a list of URLs that might be internal or external. There we first pull the common page title and then include two fragments that apply on either type of URL and invoke different fields based on that information. So elegant!

The finish line

We've reached the (preliminary) end of our streak of practical GraphQL blog posts. Next up will be a peek into the future of the GraphQL module with planned features and possible use cases. But if you are interested in more advanced topics like performance optimisation, caching or deeper integration with Drupal subsystems (fields, views, contexts ...) ping me @pmelab and I'll see what I can do.

Categories: Blogs

Enzolutions: The Drupal Console Journey

August 23, 2017 - 9:00pm

Before the existence of the Drupal Console as a project, it all began with an idea to make Drupal 8 better. Every great invention/innovation begins with an idea, and the Drupal transition from 7 to 8 came with massive changes to the fundamental operating procedures of yesterday. Symfony components were making a splash into Drupal Core. Jesus and David, the initiators of Drupal Console project, came up with the idea of including the symfony console into the Drupal core. The same way that other symfony components were being included into the Drupal core.

Powering Though Frustration

As helpful as the Drupal Console project is nowadays, it wasn’t very widely accepted into the drupal community initially. In fact, it turned out to be a huge challenge to get anyone to listen to the idea. For Jesus and David, the primary objective to include the Symfony Console in Drupal was to have the option to have code generators, in the same way, the Symfony community does. Who wouldn’t want that? A way to automate the annoying redundancies that plague developers everywhere. So they decided to propose the idea to the Drupal core maintainers via the issue queue. That idea was however quickly dismissed.

After few attempts to request the inclusion and trying to collaborate into different drupal projects, it dawned on Jesus and David that inclusion and collaboration was not going to happen. They needed to regroup and find a better approach.

While at lunch at Drupalcamp Costa Rica, Jesus and David were casually discussing the frustrations they had encountered trying to bring innovation to Drupal and related projects, and Larry Garfield chimed in “someone needs to create a separate project that includes Symfony Console and code generation”. That sentence gave birth to the Drupal Console project as you know it today.

Building A Community

Jesus stacked up his calendar with almost every Drupal event in the U.S. The goal was to talk about the project in sessions at all Drupal community gatherings he could physically attend, or at minimum, present the idea at BOFs where sessions were not possible. The code sprints helped him interact with developers and users forming a critical source of feedback.

Along the way, he convinced me to join the project as a maintainer. I also embarked on his outreach campaign to help spread the word. Only, my campaign was global because it was important to reach non-english speakers because they often feel left out of major open source projects. Currently, the Drupal Console project is available, with some variations, in the 18 languages listed below.

  • English
  • Spanish
  • Catalán
  • French
  • Korean
  • Hindi
  • Hungarian
  • Indonesian
  • Japanese
  • Marathi
  • Punjabi
  • Brazilian Portuguese
  • Romanian
  • Russian
  • Tagalog
  • Vietnamese
  • Chinese Simplified
  • Chinese Traditional
One Million Downloads!

After four years of development in July 2017, we reached our first million downloads across different releases. This achievement is thanks to our more that 250 contributors across the globe.

This brought a great sense of validation for deciding to stick to our guns, do the right thing and most importantly... be globally inclusive.

Categories: Blogs

Larry Garfield: Best practices are contextual

August 23, 2017 - 7:51pm
Best practices are contextual

Articles, blog posts, and Twitter debates around "best practices" abound. You can't swing a dead cat without bumping into some article espousing the benefits of designing and building software a certain way. (Side note: What kind of sick person are you that you're swinging a dead cat around? Stop that! You're desecrating the dead!)

Larry 23 August 2017 - 6:51pm
Categories: Blogs

Glassdimly tech Blog: Drupal 8: How to Get Image Style Tokens for Metatags

August 23, 2017 - 7:15pm

It turns out that image style tokens are an easter egg of the regular Token module, ported in from the Imagecache Token module on this ticket.

Image style tokens don't show up in the media browser, and so you sort of have to guess at how to use them. I figured it out by studying the merged commit that added the functionality.

Categories: Blogs

Mediacurrent: Mediacurrent launches PwC CareerAdvisor on Drupal 8

August 23, 2017 - 4:56pm
PwC PArtners with Mediacurrent: A Mini Case Study 

In January 2015, PwC US announced the launch of its new CareerAdvisor tool. Aimed to help students discover their professional goals and set themselves up for career success, the CareerAdvisor site provides links to reference articles and videos and other resources with career advice. After visitors create an account, they can track their progress with an interactive checklist of action items for career success and add a list of personal ToDos.

Categories: Blogs

Lullabot: The Hidden Costs of Decoupling

August 23, 2017 - 4:00pm

Decoupled Drupal has been well understood at a technical level for many years now. While the implementation details vary, most Drupal teams can handle working on decoupled projects. However, we’ve heard the following from many of our clients:

  1. We want a decoupled site. Why is this web project so expensive compared to sites I worked on in the past?
  2. Why do our decoupled projects seem so unpredictable?
  3. If we decide to invest in decoupled technologies, what can we expect in return?

Let’s dive into these questions.

Why Can Decoupled Sites Cost More?

Before getting too much into the details of decoupled versus full-stack, I like to ask stakeholders:

“What does your website need to do today that it didn’t 5 years ago?”

Often, the answer is quite a lot! Live video, authenticated traffic, multiple mobile apps, and additional advertising deals all add to more requirements, more code, and more complexity. In many cases, the costs that are unique to decoupling are quite small compared to the costs imposed by the real business requirements.

However, I have worked on some projects where the shift to a decoupled architecture is fundamentally a technology shift to enable future improvements, but the initial build is very similar to the existing site. In those cases, there are some very specific costs of decoupled architectures.

Decoupling means forgoing Drupal functionality

Many contributed modules provide pre-built functionality we rely on for Drupal site builds. For example, the Quickedit module enables in-place editing of content. In a decoupled architecture, prepare to rewrite this functionality. Website preview (or even authenticated viewing of content) has to be built into every front end, instead of using the features we get for free with Drupal. Need UI localization? Content translation? Get ready for some custom code. Drupal has solved a lot of problems over the course of its evolution, so you don’t have to—unless you decouple.

Decoupling is shorthand for Service Oriented Architectures

For many organizations, a decoupled website is their first foray into Service Oriented Architectures. Most full-stack Drupal sites are a single application, with constrained integration points. In contrast, a decoupled Drupal site is best conceived of as a “content service,” accessed by many disparate consumers.

I’ve found that the “black-boxing” of a decoupled Drupal site is a common stumbling block for organizations and a driver behind the increased costs of decoupling. To properly abstract a system requires up-front systems design and development that doesn’t always fit within the time and budget constraints of a web project. Instead, internal details end up being encoded into the APIs Drupal exposes, or visual design is reflected in data structures, making future upgrades and redesigns much more expensive. Writing good APIs is hard! To do it well, you need a team who is capable of handling the responsibility—and those developers are harder to find and cost more.

Scalable systems and network effects

Once your team dives into decoupling Drupal, they are going to want to build more than just a single Drupal site and a single JavaScript application. For example, lullabot.com actually consists of five systems in production:

  1. Drupal for content management
  2. A CouchDB application to serve content over an API
  3. A second CouchDB application to support internal content preview
  4. A React app for the site front end
  5. Disqus for commenting

Compared to the sites our clients need, lullabot.com is a simple site. In other words, as you build, expect to be building a web of systems, and not just a “decoupled” website. It’s possible to have a consumer request Drupal content directly, especially in Drupal 8, but expect your tech teams to push for smaller “micro” services as they get used to decoupling.

Building and testing a network of systems requires a lot of focus and discipline. For example, I’ve worked with APIs that expose internal traces of exceptions instead of returning something usable to API consumers. Writing that error handling code on the service is important, but takes time! Is your team going to have the bandwidth to focus on building a robust API, or are they going to be focusing on the front-end features your stakeholders prioritize?

I’ve also seen decoupled systems end up requiring a ton of human intervention in day-to-day use. For example, I’ve worked with systems where not only is an API account created manually, but manual configuration is required on the API end to work properly. The API consumer is supposed to be abstracted from these details, but in the end, simple API calls are tightly coupled to the behind-the-scenes configuration. A manual set up might be OK for small numbers of clients, but try setting up 30 new clients at once, and a bottleneck forms around a few overworked developers.

Another common mistake is not to allow API consumers to test their integrations in “production.” Think about Amazon’s web services—even if your application is working from a QA instance, as far as Amazon is concerned there are only production API calls available. Forcing other teams to use your QA or sandbox instance means that they won’t be testing with production constraints, and they will have production-only bugs. It’s more difficult to think about clients creating test content in production—but if the API doesn’t have a good way to support that (such as with multiple accounts), then you’re missing a key set of functionality.

It’s also important to think about error conditions in a self-serve context. Any error returned by an API must make clear if the error is due to an error in the API, or the request made of the API. Server-side errors should be wired up to reporting and monitoring by the API team. I worked with one team where client-side errors triggered alerts and SMS notifications. This stopped the client-side QA team from doing any testing where users entered bad data beyond very specific cases. If the API had been built to validate inbound requests (instead of passing untrusted data through its whole application), this wouldn’t have been a problem.

There's a lot to think of when it comes to decoupled Drupal sites, but it’s the only way to build decoupled architectures that are scalable, and that lead to faster development. Otherwise, decoupling is going to be more expensive and slower, leaving your stakeholders unsatisfied.

Why are decoupled projects unpredictable?

When clients are struggling with decoupled projects, we’ve often found it’s not due to the technology at all. Instead, poor team structure and discipline lead to communication breakdowns that are compounded by decoupled architectures.

The team must be strong developers and testers

Building decoupled sites means teams have to be self-driving in terms of automated testing, documentation, and REST best practices. QA team members need to be familiar with testing outside of the browser if they are going to test APIs. If any of these components are missing, then sprints will start to become unpredictable. The riskiest scenario is where these best practices are known, but ignored due to stakeholders prioritizing “features.” Unlike one-off, full-stack architectures, there is little room to ignore these foundational techniques. If they’re ignored, expect the team to be more and more consumed by technical debt and hacking code instead of solving the actual difficult business problems of your project.

The organizational culture must prioritize reliable systems over human interactions

The real value in decoupled architectures comes not in the technology, but in the effects on how teams interact with each other. Ask yourself: when a new team wants to consume an API, where do they get their information? Is it primarily from project managers and lead developers, or documentation and code examples? Is your team focused on providing “exactly perfect” APIs for individual consumers, or a single reusable API? Are you beholden to a single knowledge holder?

This is often a struggle for teams, as it significantly redefines the role of project managers. Instead of knowing the who of different systems the organization provides, it refocuses on the what - documentation, SDKs, and examples. Contacting a person and scheduling a meeting becomes a last resort, not a first step. Remember, there’s no value in decoupling Drupal if you’ve just coupled yourself to a lead developer on another team.

Hosting complexity

One of the most common technological reasons driving a decoupled project is a desire to use nodejs, React, or other JavaScript technologies. Of course, this brings in an entire parallel stack of infrastructure that a team needs to support, including:

  • HTTP servers
  • Databases
  • Deployment scripts
  • Testing and automation tools
  • Caching and other performance tools
  • Monitoring
  • Local development for all of the above

On the Drupal side, we’ve seen many clients want to host with an application-specific host like Acquia or Pantheon, but neither of those support running JavaScript server-side. JavaScript-oriented hosts likewise don’t support PHP or Drupal well or at all. It can lead to some messy and fragile infrastructure setups.

All of this means that it’s very difficult for a team to estimate how long it will take to build out such an infrastructure, and maintenance after a launch can be unpredictable as well. Having strong DevOps expertise on hand (and not outsourced) is critical here.

Decoupled often means “use a bunch of new nodejs / JavaScript frameworks”

While server-side JavaScript seems to be settling down towards maturity nicely, the JavaScript ecosystem for building websites is reinventing itself every six months. React of today is not the same React of 18 months ago, especially when you start considering some of the tertiary libraries that fill in the gaps you need to make a real application. That’s fine, especially if your project is expected to take less than 6 months! However, if your timeline is closer to 12-18 months, it can be frustrating to stakeholders to see rework of components they thought were “done,” simply because some library is no longer supported.

What’s important here is to remember that this instability isn’t due to decoupling—it’s due to front-end architecture decisions. There’s nothing that stops a team from building a decoupled front-end in PHP with Twig, as another Drupal site, or anything else.

If we invest in Decoupled Drupal, what’s the payoff?

It’s not all doom and decoupled gloom. I’ve recommended and enjoyed working on decoupled projects in the past, and I continue to recommend them in discoveries with clients. Before you start decoupling, you need to know what your goals are.

A JavaScript front end?

If your only goal is to decouple Drupal so you can build a completely JavaScript-driven website front end, then simply doing the work will give you what you want. Infrastructure and JavaScript framework churn are most common stumbling blocks and not much else. If your team makes mistakes in the content API, it’s not like you have dozens of apps relying on it. Decouple and be happy!

Faster development?

To have faster site development in a decoupled context, a team needs to have enough developers so they can be experts in an area. Sure, the best JavaScript developers can work with PHP and Drupal but are they the most efficient at it? If your team is small and a set of “full-stack” developers, decoupling is going to add abstraction that slows everything down. I’ve found teams need to have at least 3 full-time developers to get efficiency improvements from decoupling. If your team is this size or larger, you can significantly reduce the time to launch new features, assuming everyone understands and follows best development practices.

Multichannel publishing?

Many teams I’ve worked with have approached decoupled Drupal not so much to use fancy JavaScript tools, but to “push” the website front end to be equal to all other apps consuming the same content. This is especially important when your CMS is driving not just a website and a single app, but multiple apps such as set-top TV boxes, game consoles, and even apps developed completely externally.

With full-stack Drupal, it’s easy to create and show content that is impossible to view on mobile or set-tops apps. By decoupling the Drupal front end, and using the same APIs as every other app, it forces CMS teams to develop with an API-first mentality. It puts all consumers on an equal playing field, simplifying the development effort in adding a new app or platform. That, on it’s own, might be a win for your organization.

Scaling large teams?

Most large Drupal sites, even enterprise sites, have somewhere between 5-10 active developers at a time. What if your team has the budget to grow to 30 or 50 developers?

In that case, decoupled Drupal is almost the only solution to keep individuals working smoothly. However, decoupled Drupal isn’t enough. Your team will need to completely adopt an SOA approach to building software. Otherwise, you’ll end up paying developers to build a feature that takes them months instead of days.

Decoupling with your eyes open

The most successful decoupled projects are those where everyone is on board—developers, QA, editorial, and stakeholders. It’s the attitude towards decoupling that can really push teams to the next level of capability. Decoupling is a technical architecture that doesn’t work well when the business isn’t buying in as well. It’s worth thinking about your competitors too—because if they are tech companies, odds are they are already investing in their teams and systems to fully embrace decoupling.

Categories: Blogs

Drupalize.Me: New Free Series: Coding Standards

August 23, 2017 - 9:49am
Categories: Blogs

InternetDevels: Spam protection with Drupal 8 modules

August 23, 2017 - 9:14am

Spam causes huge inconvenience to Internet users and headaches for site owners. Spam is one of reasons why you don’t need a comment section on your web resource. However, allowing your site visitors to post comments and any other content means communication and feedback. Allowing them to express their opinions and share their ideas on your site has its good sides as well. So it would be not right to get rid of this option all together. Luckily, Drupal can offer you a solution — and not just one.

Read more
Categories: Blogs

Abhishek Lal | GSoC Blog: Examples for Developer #12 Week of Coding

August 23, 2017 - 6:17am
Examples for Developer #12 Week of Coding Abhishek Lal B Wed, 08/23/2017 - 14:47
Categories: Blogs

Amazee Labs: Tour de DrupAlps - Cycling the Alps to DrupalCon Vienna

August 23, 2017 - 4:14am
Tour de DrupAlps - Cycling the Alps to DrupalCon Vienna

As per the extreme version of Tour de Drupal, my plan is to cycle from Switzerland to DrupalCon Vienna by crossing the Alps 6 times. The tour will take me over some of the most challenging Alp passes using my road cycle.

Josef Dabernig Wed, 08/23/2017 - 09:14

I’ll visit 5 different countries, including: Switzerland, Italy, Germany, Slovenia and Austria. Overcoming the Alps with the bicycle has been one of my long term dreams... Inspired by the sport achievements of my uncle, I am taking this challenge to see if I can make it from Switzerland to DrupalCon Vienna by taking some scenic and challenging detours over the Alps.

The final destination is DrupalCon Vienna which serves as the perfect goal because it’s my favorite conference and my hometown at the same time.

#DrupAlps in numbers

  • Days: 31
  • Distance: 2361 km
  • Total elevation gain: 57864 meters


Here is how I planned out the route so far

Depending on weather and physical conditions, I will adapt the route along the way while riding.


Over the last months, I have been riding various passes to get ready for the tour. Longer rides included St. Moritz - Splügenpass - Chur, Erstfeld - Oberalppass - Chur, and  Zurich - Vierwaldstättersee - Lucerne. In 2015, we cycled the Pyrenees as part of #TourDeDrupal Barcelona(1, 2, 3), early 2016 we cycled Sierra Nevada(1, 2) and later summer 2016 I cycled the biggest mountain of Austria on the Großglocknerstrasse.

Until now, I have no experience in cycling more than 3 consecutive days,  so I am really looking forward to seeing how it will go while being in the saddle for so many days in a row.

For DrupalCon Barcelona we cycled along the Pyrenees.

Amazee Extreme Challenge

“After three years of fulltime employment, our employees get one month off to free their mind and do something really extreme.”

I would like to thank Amazee for giving me this unique chance. Find out about what my colleagues are planning or have already accomplished at the Amazee Labs Extreme page.

DrupalCon Vienna

DrupalCon is the biggest Drupal conference, organized by the Drupal Association and will take place in Vienna from 26-29 September this year. I am especially looking forward to this one in my home town!

On Monday, there will be sprints, training sessions & summits organized by Drupal Austria.

Tuesday to Friday is packed with the official conference program and I am particularly excited about the site-building track, which I am a track chairing this year. Check out my blog post about what to expect from this week at DrupalCon.

Join Tour de Drupal

If you’d like to join my for the last few days of cycling, the last days are planned to be flat along the Danube river. For example, we plan to cycle from Linz to Krems on Saturday, 22nd of September. On Sunday, 23rd of September we go from Krems to Tulln where we will have a lunch break at the webshapers office. After that, we’ll cycle towards Vienna to arrive on Sunday afternoon.

Sign-up here and see my blog post for further details.

Follow me

Along the route, I will post regular updates about the ups and downs of the Tour de DrupAlps. To stay tuned, you can follow me here:

Categories: Blogs

Ben's SEO Blog: A Drupal SEO Expert and a Drupal Developer Hit the Mat

August 23, 2017 - 4:01am

[Photo credits: Ben Finklea, Jan 2017. Photos captured from iPhone video shot at Texas Grappling Challenge Brazilian Jiu-Jitsu tournament in Cedar Park, Texas.]

My Drupal developer friends and I (like any friendship) don’t always see eye-to-eye. While we have much in common—we both want stylish, high-performing websites—in some areas our goals are different. This can create the appearance of conflict. However, after working through the issues (or hitting the mats, as they say in Brazilian Jiu-Jitsu), we can find common ground and mutual respect.

In Drupal 8 SEO, I lay out a list of modules that should be installed on your Drupal website to enhance SEO. I wrote this book for marketers and provide the step-by-step details you need to increase Google ranking, website traffic... Read the full article: A Drupal SEO Expert and a Drupal Developer Hit the Mat

Categories: Blogs