Feb 01. 2019 · by Helge Sverre

How to integrate Hubspot with CraftCMS

Hubspot is a marketing automation suite of tool that allows marketers to create landing pages, blog posts, hosted lead forms, and also comes with a pretty neat (and free) CRM module.

When a company/client is already using Hubspot to power their content marketing, but are looking to refresh their website and get moved on to Craft CMS, they might want to integrate the existing content from their HubSpot portal into their new website.

We at Guilty encountered this with one of our latest projects, and thought we might as well build a plugin for this that can be used by our self and others in the future to make this task a bit easier.

So we built the Craft CMS plugin called Hubspot Connector

Installing the plugin

Installing Hubspot Connector with composer
bash
 composer require guilty/hubspot-connector

Then go to the Control Panel, click on to Settings → Plugins and click the “Install” button for HubSpot Connector.

It is now installed!

Configuring HubSpot Connector

  1. Go the the Control Panel
  2. Go to the Settings page
  3. Click on "HubSpot Connector" under the "Plugins" section
  4. Enter your Hubspot API key and click Save.
  5. Done

Now that we've got the plugin installed and configured with our Hubspot API key, we might want to pull down blog posts from the client's hubspot blog and show it within the new website design.

Pulling published blog posts from Hubspot and showing it in Twig

templates/blog/index.twig (just an example)
TWIG
{# 
    Only return blog posts whos state is "PUBLISHED",
    valid states are: DRAFT, PUBLISHED, or SCHEDULED.
    
    A Hubspot "portal" can include several blogs, 
    pass the Blog ID as the first parameter to 
    only fetch content from that specific blog.
#}

{% set publishedBlogPosts = craft.hubspot.blogPosts("123123123", {
    state: "PUBLISHED",
}) %}

{% for post in publishedBlogPosts %}
    <article>
        <h5>
            <a href="{{ post.published_url }}">
                {{ post.html_title }}
            </a>
        </h5>
        {{ post.post_summary | striptags }}
    </article>
{% endfor %}

And just like that, we've now pulled content from a Hubspot blog into our Craft CMS website template, now you can integrate that into the new website design.

Showing a single Hubspot blog post in our Craft CMS site

Just listing out the existing blog posts are not enough for some projects though, sometimes you need to integrate the Hubspot content as if it was a native Craft Section, this can be done by the help of a custom route and some Twig code:

config/routes.php
PHP
<?php

return [
    // Easy: Use the blog post ID
    'blog/<blogPostId>' => ['template' => 'blog/_entry'],

    // OR
    
    // Intermediate: Use the hubspot blog post slug
    'blog/<slug>' => ['template' => 'blog/_entry'],

];

Now that we got our custom route set up, here is two examples of implementing a blog entry page with content pulled from a Hubspot Blog.

templates/blog/_entry.twig (with blogPostId)
TWIG
{# Note:  I am assuming that you're extending a layout with a block called "content" #}
{% extends "_layout" %}

{# See: https://github.com/guilty-as/hubspot-connector/blob/master/src/variables/HubspotConnectorVariable.php#L33 #}
{# the variable blogPostId is injected into the template because it is a named route parameter #}
{% set post = craft.hubspot.blogPost(blogPostId) %}
{% set siteUrl = craft.app.request.absoluteUrl %}

{% block content %}

    {# (optional) Integration with SEOMatic #}
    {% do seomatic.meta.setAttributes({
        seoTitle: post.html_title,
        seoDescription: post.post_summary | striptags | default('') | raw,
        seoKeywords: post.keywords,
        seoImage: post.featured_image,
        canonicalUrl: siteUrl,
        twitter: {
            card: "summary_large_image",
            site: siteUrl,
            creator: post.blog_post_author,
            title: post.html_title,
            description: post.post_summary | striptags | default('') | raw,
            image: post.featured_image,
        },
        og: {
            type: "article",
            locale: "en_GB",
            url: siteUrl,
            title: post.html_title,
            description: post.post_summary | striptags | default('') | raw,
            image: post.featured_image,
        }
    }) %}


    <h1>{{ post.html_title }}</h1>

    <img src="{{ post.featured_image }}" class="post-featured-image">

    <div class="post-body">
        {# 
            Hot tip:
            You can use the plugin "Retcon" to manipulate the HTML 
            retrieved from Hubspot, adding or removing classes, cleaning the HTML etc
        #}
        {{ post.post_body | default('') | raw }}
    </div>

{% endblock %}

The second example is a little messy due to the fact that Hubspot does not allow us to search or fetch a blog post from the API by querying for the slug by itself, so we have to loop through all the posts until we find a matching one, it's not optimal, but it is a solution.

ProTip: use the cache tag to only have to do this once.

templates/blog/_entry.twig (with slug)
TWIG
{% extends "_layout" %}
{% set slug = craft.app.request.segments | last  %}

{# 
    Here we have to include the blogId, as we are now fetching a 
    list of blog posts from a specific blog in the hubspot portal 
#}
{% set blogId = "123123123" %}
{% set siteUrl = craft.app.request.absoluteUrl %}

{% set posts = craft.hubspot.blogPosts(blogId, {
     state: "PUBLISHED"
  }) %}


{% block content %}


    {# Loop thought all posts until we find the matching one. #}
    {% for post in posts if post.slug == 'blog/' ~ slug %}

        {# (optional) Integration with SEOMatic #}
        {% do seomatic.meta.setAttributes({
            seoTitle: post.html_title,
            seoDescription: post.post_summary | striptags | default('') | raw,
            seoKeywords: post.keywords,
            seoImage: post.featured_image,
            canonicalUrl: siteUrl,
            twitter: {
                card: "summary_large_image",
                site: siteUrl,
                creator: post.blog_post_author,
                title: post.html_title,
                description: post.post_summary | striptags | default('') | raw,
                image: post.featured_image,
            },
            og: {
                type: "article",
                locale: "en_GB",
                url: siteUrl,
                title: post.html_title,
                description: post.post_summary | striptags | default('') | raw,
                image: post.featured_image,
            }
        }) %}


        <h1>{{ post.html_title }}</h1>

        <img src="{{ post.featured_image }}" class="post-featured-image">

        <div class="post-body">
        {# 
            Hot tip:
            You can use the plugin "Retcon" to manipulate the HTML 
            retrieved from Hubspot, adding or removing classes, cleaning the HTML etc
        #}
        {{ post.post_body | default('') | raw }}
        </div>

    {% endfor %}

{% endblock %}

Integrating Hubspot SEO Meta with SEOMatic

SEOMatic is a plugin we use for almost all of our Craft projects, it's the defacto standard SEO plugin, that allows clients to change meta titles, descriptions, that are shown when sharing your website on social media platforms like Facebook, Twitter etc.

To integrate our new external Hubspot content into SEOMatic, we can use a cool feature included by the SEOMatic plugin that lets us swap out the meta information from anywhere in your template (even within partials) by calling the setAttributes method, here is a simple example:

Integration with SEOMatic
TWIG
{% do seomatic.meta.setAttributes({
            seoTitle: post.html_title,
            seoDescription: post.post_summary | default('') | raw,
            seoKeywords: post.keywords,
            seoImage: post.featured_image,
            canonicalUrl: craft.app.request.absoluteUrl,
            twitter: {
                card: "summary_large_image",
                site: craft.app.request.absoluteUrl,
                title: post.html_title,
                description: post.post_summary | default('') | raw,
                image: post.featured_image
            },
            og: {
                type: "article",
                locale: "en_GB",
                url: craft.app.request.absoluteUrl,
                title: post.html_title,
                description: post.post_summary | default('') | raw,
                image: post.featured_image,
            }
        }) %}

Note
You might have to clear the craft cache to see changes, as SEOMatic caches its meta tags.https://github.com/nystudio107/craft-seomatic/issues/298

You can find a full reference of available fields on the blog post api documentation.

For more templating examples including how to do pagination, check out the templating wiki on GitHub

If you encounter a bug or an issue with the plugin hit me up on the CraftCMS slack channel, send a private message to "helgesverre" and I'll try to help the best I can.