Tags:

Drupal 8 Metatags inheritance for Images

<p><span class="drop-cap">M</span>etatag is a great <a href="https://www.drupal.org/project/metatag">module</a>. It gives content managers tools to control how the content is portrayed when it’s shared on social media and how it turns up on search engines. </p> <p>We realized some of our clients were not investing their time in setting the meta tags for their contents up, and we thought: “hey, Drupal can be cool with that, we can set default tags based on the content, and the path for them to follow if they want to be more specific anytime”.</p> <p><em>Meaning</em>: we figure with them the default tags we need to set up, and give them the ability to override those for any particular content they want. This way, if they don’t have the time, when content is shared it still looks beautiful.</p> <p>Metatag practically covers all of this up, <em>but</em> the image contents (there is <a href="https://www.drupal.org/project/metatag/issues/2044621">this issue</a> and <a href="https://www.drupal.org/project/metatag/issues/2755149">this other issue</a> about this matter).</p> <p>We set up a default image to be used when sharing content, and a field in each content type for the content manager to upload a different image for that particular content. The expected behavior would be to use the default image every time the particular image is not set. Sort of like <em>inheriting the image global image if the particular one is not set.</em> Piece of cake.</p> <p>As I said earlier, this doesn’t work out of the box. It might some day, but in the meantime, we are using using <em>tokens</em> and <em>image styles.</em></p> <h2>Our workaround:</h2> <p>First of all, we enabled Metatag and used tokens to set up our default image.</p> <p>We created a custom token in our custom module with our default image for sharing purposes. We storaged it in our theme and referenced it like this:</p> <pre> <code class="language-php">use Drupal\media\Entity\Media; use Drupal\image\Entity\ImageStyle; use Drupal\Core\Url; // Replace {custom} and {module_name} with your own! /**  * Implements hook_token_info().  */ function {module_name}_token_info() {   $info = [];   $info['types']['{custom}'] = ['name' =&gt; t('{custom}'), 'description' =&gt; t('{custom}')];   $info['tokens']['{custom}']['share_image'] = [     'Default share image.',     'name' =&gt; 'Share image',     'description' =&gt; 'Default image used unless overwritten'   ];   return $info; } /**  * Implements hook_tokens().  */ function  {module_name}_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {   $replacements = [];   if ($type == '{custom}') {     foreach ($tokens as $name =&gt; $original) {       // Find the desired token by name.       switch ($name) {         case 'share_image':           $replacements[$original] = Url::fromUri('internal:/themes/custom/.../share.jpg', [ 'absolute' =&gt; TRUE ])-&gt;toString();           break;       }     }   }   return $replacements; }</code></pre> <p>After doing this, on our Metatag config page, we set our token [{custom}:share_image] as the image URLs:</p> <img alt="Global configuration of the Metatag module" data-entity-type="file" data-entity-uuid="c47420b7-02ac-403f-a0c2-0591f3b32afa" src="/sites/default/files/inline-images/metatags%201.png" class="align-center" width="1244" height="575" loading="lazy" /> <p>So far, so good, all pages, unless told otherwise, will show that image when shared.</p> <p>Next, we created a custom field for the sole purpose of overriding the previous default image: field_share_image and added it to our content types. Then we set up another token for the overridden share image:</p> <p> </p> <p>We set up the configuration of the Content on the Metatag page, that now looks like this:</p> <img alt="Content configuration of the Metatag module" data-entity-type="file" data-entity-uuid="aa958505-16bc-418b-9f43-1a68998cf78c" src="/sites/default/files/inline-images/metatags%202.png" class="align-center" width="1257" height="632" loading="lazy" /> <p> </p> <p>This way, if our content has the field_share_image and our user loaded content there, we’ll use it for our metatags. Else, if they skip that field, then we’ll display our default image. </p> <pre> <code class="language-php">use Drupal\media\Entity\Media; use Drupal\image\Entity\ImageStyle; use Drupal\Core\Url; /**  * Implements hook_token_info().  */ function {module_name}_token_info() {   $info = [];   $info['types']['{custom}'] = ['name' =&gt; t('{custom}'), 'description' =&gt; t('{custom}')];   $info['tokens']['{custom}']['share_image'] = [     'Default share image.',     'name' =&gt; 'Share image',     'description' =&gt; 'Default image used unless overwritten'   ];   $info['tokens']['{custom}']['share_image__overridden'] = [     'Share image (overridden).',     'name' =&gt; 'Custom share image',     'description' =&gt; 'Overrides default share image'   ];   return $info; } /**  * Implements hook_tokens().  */ function  {module_name}_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {   $replacements = [];   if ($type == '{custom}') {     foreach ($tokens as $name =&gt; $original) {       switch ($name) {         case 'share_image':           $replacements[$original] = Url::fromUri('internal:/themes/custom/.../share.jpg', [ 'absolute' =&gt; TRUE ])-&gt;toString();           break;         case 'share_image__overridden':           $fallback = Url::fromUri('internal:/themes/custom/.../share.jpg', [ 'absolute' =&gt; TRUE ])-&gt;toString();           if ($data['node'] &amp;&amp; $data['node']-&gt;hasField('field_share_image') &amp;&amp; $data['node']-&gt;get("field_share_image")) {             $image_field = $data['node']-&gt;get("field_share_image")-&gt;getValue();           }           if(!empty($image_field)){             $media_entity = Media::load($image_field[0]['target_id']);             $fid = $media_entity-&gt;get('field_media_image')-&gt;getValue()[0]['target_id'];             $file = \Drupal\file\Entity\File::load($fid);             $file_uri = $file-&gt;getFileUri();             $url_with_style = ImageStyle::load('share_image')-&gt;buildUrl($file_uri);             $replacements[$original] = $url_with_style;           }           else {             $replacements[$original] = $fallback;           }           break;       }     }   }   return $replacements; }</code></pre> <p>That’s it. It’s simple and it works.</p> <p> </p> <p>Hope this tip is useful to anybody! See you soon!</p> <p> </p>

Related blog posts