lukaswhite/meta-tags

A PHP class for managing meta tags

0.0.2 2020-07-08 06:52 UTC

This package is auto-updated.

Last update: 2024-02-29 03:43:40 UTC


README

Run Tests codecov

This library helps you manage meta tags (including Open Graph) for your website or web application.

Chances are, many of your meta tags are generated programmatically, for example from a database. Most notably the page title, but perhaps also the description and keywords.

Perhaps you make these available as data to your views, then build the tags there. What this library does is allow you to build them inside of your application (in a controller, for example), then output them in your views really easily.

So, instead of something like this (I'm using Blade syntax here):

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<title>{{ $title }}</title>
		<meta name="og:title" content="{{ $title }}" />
		<meta name="description" content="{{ $description }}" />
		<meta name="og:description" content="{{ $description }}" />
		<meta name="keywords" content="{{ implode( ', ', $keywords ) }}" />
		<meta name="robots" content="index,nofollow" />
		<link rel="canonical" content="{{ $url }}" />
	</head>

This library allows you to do this:

$meta = new MetaTags( );
$meta
	->charSet( 'utf-8' )
	->title( $title )
	->description( $description )
	->keywords( $keywords )
	->robotsShouldIndexButNotFollow( )
	->canonical( $url );

In fact, if you're using UTF-8 then it's the default anyway, so that ->charSet( ) call is redundant.

Then in your view:

<!DOCTYPE html>
<html lang="en">
	<head>
		{!! $meta !!}
	</head>

If you're using Laravel, you could do something like this:

{!! app( )->make( MetaTags::class )->render( ) !!}

In the example above, we've registered the MetaTags class as singleton with the service container. More on that later.

So what are the benefits?

  • A maximum of one variable to pass to your views
  • No hand-coding the tags themselves
  • No confusion as to when to use name vs property attributes
  • It makes it harder to forget things like setting the character set
  • Set the title and description, and the corresponding Open Graph meta tags are set for you
  • It (hopefully!) helps as a reminder for important, but often overlooked things like canonicalization
  • It exposes some of the lesser-known meta tags, like the one aimed directly at Googlebots
  • It keeps your layouts nice and tidy

Installation

composer require lukaswhite/meta-tags

First Steps

Create an instance as follows:

use Lukaswhite\MetaTags\MetaTags;

$meta = new MetaTags( );

I'd recommend creating it as a singleton, then popping it in a service container or similar.

For example, if you're using Laravel:

$this->app->singleton( MetaTags::class, function( $app ) {
	return new MetaTags( );
} );

Then, whenever you want to set your meta tags, for example in a controller, then you can get the instance like this:

$meta = app( )->make( MetaTags::class );

Standard Metadata

Setting the Title

$meta->title( 'My Website' );

By default, this will add the following:

<title>My Website</title>
<meta property="og:title" content="My Website" />

You'll notice that it's added an Open Graph tag with the title by default; if you don't want that behavior:

$meta->includeOpenGraph( false )
	->title( 'My Website' );

The result:

<title>My Website</title>

Setting the Description

$meta->description( 'A description of the website' );

By default, this will add the following:

<meta name="description" content="A description of the website" />
<meta property="og:description" content="A description of the website" />

You'll notice that it's added an Open Graph tag with the description by default; if you don't want that behavior:

$meta->includeOpenGraph( false )
	->description( 'A description of the website' );

The result:

<meta name="description" content="A description of the website" />

Setting the Keywords

$meta->keywords( 'PHP', 'meta tags', 'SEO' );

The result:

<meta name="keywords" content="PHP, meta tags, SEO" />

Setting the URL

Set the url like this:

$meta->url( 'http://example.com' );

This will output the following:

<meta name="url" content="http://example.com" />

Setting the Character Set

The library will automatically add the character set to the meta tags, defaulting to UTF-8. So this is added automatically:

<meta charset="utf-8" />

To set the character set to something different:

$meta->charSet( 'ISO-8859-1' );

The result:

<meta charset="ISO-8859-1" />

Should, for any reason, you not wish this to be added then you can simply set it to null:

$meta->charSet( null );

Canonical URLs

To set the canonical link meta tag:

$meta->canonical( 'http://example.com' );

This results in the following:

<link rel="canonical" content="http://example.com" />

Pagination

If you're rendering a page that lists something and it uses pagination, then it can be helpful to add links to the next and / or previous pages as links. That's to say, a link tag with rel=prev or rel=next.

Suppose we're currently on page four:

$meta->previousPage( 'http://example.com/articles/page/3' )
	->nextPage( 'http://example.com/articles/page/5' );

You can also set the first and last pages:

$meta
	->firstPage( 'http://example.com/articles/page/1' )
	->lastPage( 'http://example.com/articles/page/10' );

Feeds

RSS

If you have an RSS feed associated with a website or category, it's a good idea to create a link to it.

Without a title:

$meta->addRssFeed( 'http://example.com/feed.rss' );

Or with:

$meta->addRssFeed( 'http://example.com/feed.rss', 'My Website RSS Feed' );

Atom

If you have an Atom feed associated with a website or category, it's a good idea to create a link to it.

Without a title:

$meta->addAtomFeed( 'http://example.com/feed.xml' );

Or with:

$meta->addAtomFeed( 'http://example.com/feed.xml', 'My Website Atom Feed' );

Robots

By default robots will index and follow; if you want to tell them not to do one or both you can use one of the following methods:

$meta->robotsShouldIndexButNotFollow( );
// or
$meta->robotsShouldFollowButNotIndex( );
// or
$meta->robotsShouldNotIndexNorFollow( );

Googlebot

There are a couple of additional requests you can make from the Googlebot, in addition to the noindex or nofollow clauses.

To prevent a text snippet or video preview from being shown in the search results:

$meta->googleShouldNotIncludeSnippets( );

To prevent Google from showing the Cached link for a page:

$meta->googleShouldNotIncludeSnippets( );

To specify that you do not want your page to appear as the referring page for an image that appears in Google search results:

$meta->googleShouldNotShowPageAsReferringPageForImageSearchResults( );

Apologies for the ridiculously long method name, better suggestions welcome!

To request that Google stop crawling a page after a specified date:

$meta->googleShouldStopCrawlingAfter( new \DateTime( '2019-10-23' ) );

Performance-related Meta Tags

There are a number of meta tags that are performance-related; specifically things like pre-fetching and pre-rendering.

Pre-fetching

You can add a link to the meta tags to tell the browser to pre-fetch a resource:

$meta->addPrefetchLink( '/scripts.js' );

You can also add a hint as to the type of resource:

$meta->addPrefetchLink( '/scripts.js', 'script' );

Preloading

You can add a link to the meta tags to tell the browser to preload a resource:

$meta->addPreloadLink( '/scripts.js' );

You can also add a hint as to the type of resource:

$meta->addPreloadLink( '/scripts.js', 'script' );

DNS Pre-fetching

DNS pre-fetching is the process of initiating the dns resolution of each domain where we have hosted resources, before the browser makes a request for them, with the goal to save the DNS resolution time when the resource is requested. (source)

To do so:

$meta->addPreconnect( '//cdn.example.com' );

Pre-renderng

To ask the browser to pre-render content:

$meta->addPrerender( '//example.com/landing-page.html' );

http-equiv Meta Tags

There's a catgeory of meta tags that look like this:

<meta http-equiv="Pragma" content="no-cache">

They're essentially the HTML equivalent of HTTP headers.

To add one:

$meta->httpEquiv( 'Pragma', 'no-cache' );

To tell browsers that you don't want a page to be cached, you can either set the relevant tags manually, or you can do this:

$meta->tellBrowsersNotToCache( );

As per the advice on this page, since not all all client browsers and caching devices (e.g. proxy servers) are known to successfully implement all no-caching options, this method includes multiple no-caching options; specfically it adds the following meta tags:

<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="cache-control" content="no-store" />

Custom Meta Tags

Custom meta tags can be useful for injecting data into a page that you can later access via JavaScript. Laravel does this for CSRF tokens, for example, or you may wish to use this approach for injecting an application ID or public key.

Here are some examples:

$meta->custom( 'google-maps', 'your-google-maps-key' );
$meta->custom( 'mixpanel', getenv( 'MIXPANEL_APP_ID' ) );
$meta->custom( 'csrf-token', csrf_token( ), 'name' );

Note that last one; Laravel outputs the CSRF meta tag like this:

<meta name="csrf-token" content="the-token" />

Whereas the Google Maps example above will use the more common property attribute, i.e.:

<meta property="google-maps" content="your-google-maps-key" />

Open Graph Tags

The library makes adding Open Graph meta tags easy.

Enabling or Disabling

Open Graph tags are enabled by default; that includes some which get added automatically. If you wish to disable this:

$meta->includeOpenGraph( false );

Basic Tags

There are some basic — and very important — Open Graph tags that have "standard" equivalents. Notably the title, description and URL. These get automatically added as Open Graph tags in addition to "standard" tags when you specify them with the corresponding methods.

In other words, if you do this:

$meta
	->title( 'My Website' )
	->description( 'A description of the website' )
	->url( 'http://example.com' );

You'll get the following:

<title>My Website</title>
<meta property="og:title" content="My Website" />
<meta name="description" content="A description of the website" />
<meta property="og:description" content="A description of the website" />
<meta name="url" content="http://example.com" />

Defining the Type

By default the required og:type meta tag is added, which by default is a website. I.e.:

<meta property="og:type" content="website" />

You can override this with the type( ) method, for example:

$meta->type( 'article' );

You can also use one of the provided constants:

$meta->type( MetaTags::OG_TYPE_ARTICLE );

You can also set any attributes at the same time, for example:

$meta->type(
	MetaTags::OG_TYPE_BOOK,
	[
		'isbn'  =>  'ISBN-123-1234',
	]
);

// or
$meta->type(
	MetaTags::OG_TYPE_PROFILE,
	[
		'first_name'  =>  'Joe',
		'last_name' => 'Bloggs',
		'username' => 'joebloggs',
	]
);

Passing an array of attributes in this way isn't the preferred way to do this; we'll look at other ways later.

Or, call one of the following methods:

$meta->isArticle( );
$meta->isBook( );
$meta->isProfile( );
$meta->isBusiness( );

Setting the Locale

To set the locale, for example for localization with Facebook:

$meta->locale( 'en_GB' );

Setting the URL

To set the Open Graph URL — in other words, og:url — simply do this:

$meta->ogUrl( 'https://example.com' );

Other Open Graph Tags

You can set any Open Graph tag using the openGraph( ) method:

$meta->openGraph( 'determiner', 'an' );
$meta->openGraph( 'updated_time', new \DateTime( '2018-10-12' ) );
$meta->openGraph( 'restrictions:age', 18 );

Should you be setting an Open Graph tag that can appear multiple times, pass true as the third argument. This essentially allows you to "overwrite" Open Graph tags; which might useful if, for example, you need to override information somewhere in your application.

Images

Images are important for social networks, for example when your site is shared via Facebook, or used as the basis of a Twitter Card (more on those later).

To add an image:

$meta->addImage(
	( new \Lukaswhite\MetaTags\Entities\Image( ) )
		->setUrl( 'http://example.com/image.jpeg' )
		->setSecureUrl( 'https://example.com/image.jpeg' )
		->setType( 'image/jpeg' )
		->setWidth( 500 )
		->setHeight( 300 )
		->setAlt( 'Example image' )
);

If you'd rather use your own image class, simply implement the Image contract included in this package.

It's named addImage( ) rather than, say, setImage( ) because the Open Graph protocol allows you to add multiple images.

Videos

To add a video:

$meta->addVideo(
	( new \Lukaswhite\MetaTags\Entities\Video( ) )
		->setUrl( 'http://example.com/movie.swf' )
		->setSecureUrl( 'https://example.com/movie.swf' )
		->setType( 'application/x-shockwave-flash' )
		->setWidth( 500 )
		->setHeight( 300 )
		->setImage( 'http://example.com/movie-image.jpg' )
);

If you'd rather use your own image class, simply implement the Video contract included in this package.

It's named addVideo( ) rather than, say, setVideo( ) because the Open Graph protocol allows you to add multiple videos.

Audio

To add audio:

$meta->addAudio(
	( new \Lukaswhite\MetaTags\Entities\Audio( ) )
		->setUrl( 'http://example.com/audio.mp3' )
		->setSecureUrl( 'https://example.com/audio.mp3' )
		->setType( 'audio/mp3' )
);

If you'd rather use your own image class, simply implement the Video contract included in this package.

Profiles

If a web page represents a user profile, for example in a social network, then you can do this:

$meta->profile( 
	( new \Lukaswhite\MetaTags\Entities\Profile( ) )
		->setFirstName( 'Joe' )
		->setLastName( 'Bloggs' )
		->setUsername( 'joebloggs' )
		->setGender( \Lukaswhite\MetaTags\Contracts\Profile::MALE ) );

If you'd rather use your own profile class, simply implement the Profile contract included in this package.

Books

If a web page represents a book:

$meta->book(
	( new \Lukaswhite\MetaTags\Entities\Book( ) )
		->setReleaseDate( new \DateTime( '2018-04-03 15:20' ) )
		->setAuthor( 'Joe Bloggs' )
		->setIsbn( 'ISBN-123-1234' )
		->setTag( [ 'PHP' ] )
);

If you'd rather use your own book class, simply implement the Book contract included in this package.

Note that since a book may have multiple authors, this is also permitted:

$book->setAuthor( [ 'Joe Bloggs', 'Harry Black' ] );

Contact Data

You can add contact data such as an address, e-mail address or telephone number to the Open Graph meta tags; this might be appropriate for a business listing site, for example.

Here's one way to do it:

$meta->contactData(
	( new \Lukaswhite\MetaTags\Entities\ContactData( ) )
		->setStreetAddress( '1601 S California Ave' )
		->setLocality( 'Palo Alto' )
		->setRegion( 'CA' )
		->setPostalCode( '94304' )
		->setCountryName( 'USA' )
		->setEmail( 'me@example.com' )
		->setPhone('650-123-4567')
		->setFaxNumber('+1-415-123-4567')
		->setWebsite( 'http://example.com' )
);

This will add the following meta tags:

<meta name="og:street-address" content="1601 S California Ave" />
<meta name="og:locality" content="Palo Alto" />
<meta name="og:region" content="CA" />
<meta name="og:postal-code" content="94304" />
<meta name="og:country-name" content="USA" />
<meta name="og:email" content="me@example.com" />
<meta name="og:phone_number" content="650-123-4567" />
<meta name="og:fax_number" content="http://example.com" />

Alternatively, you can use your own class by implementing the ContactData interface:

class Business implements ContactData {
	// getStreetAddress( ), getPostalCode( ) etc
}

$meta->contactData( $business );

Finally, there are also individual methods:

$meta->streetAddress( '1601 S California Ave' )
	->locality( 'Palo Alto' )
	->region( 'CA' )
	->postalCode( '94304' )
	->countryName( 'USA' )
	->email( 'me@example.com' )
	->phone('650-123-4567')
	->faxNumber('+1-415-123-4567')
	->website( 'http://example.com' )
);

Geographical Information

In addition to address information via contact data, Open Graph also allows you to specify the specific geographical location a page represents by setting the latitude and longitude, and optionally the altitude.

$meta->addGeopoint( 
	new Geopoint( 37.416343, -122.153013 ) 
);

The result:

<meta name="og:latitude" content="37.416343" />
<meta name="og:longitude" content="-122.153013" />

Rather than pass an instance of the provided Geopoint class, you can instead pass any class that implements the interface with the same name. Essentially this entails providing getLatitude( ), getLatitude( ) and getAltitude( ) methods.

Business Opening Hours

To specify a business' opening hours:

$meta->businessHours(
	new BusinessDay( 'Monday', '09:00', '17:00' ),
	new BusinessDay( 'Tuesday', '09:00', '17:00' ),
	new BusinessDay( 'Wednesday', '09:00', '17:00' ),
	new BusinessDay( 'Thursday', '09:00', '17:00' ),
	new BusinessDay( 'Friday', '09:00', '17:00' ),
	new BusinessDay( 'Saturday', '08:00', '18:00' )
);

Facebook

Facebook has a few Open Graph tags that are specific to the social network.

The App ID

To set the app ID of the site's app:

$meta->facebookAppId( '12345678910' );

The Profile IDs

To set the Facebook profile ID; that's to say, the Facebook ID of a user that can be followed:

$meta->facebookProfileId( '78676576576' );

Admins

To set the admins:

$meta->facebookAdmins(
	'12345',
	'678910'
);

// or
$meta->facebookAdmins(
	'12345'
);

Pages

To set one or more Facebook Page IDs that are associated with a URL in order to enable link editing and instant article publishing:

$meta->facebookPages(
	'76236783',
	'67873687'
);

// or
$meta->facebookPages(
	'76236783'
);

Twitter Cards

For Twitter cards, the Open Graph tags will generally suffice (previously Twitter used proprietary tags such as twitter:title, but these are now largely redundant), although you'll also want to call this:

$meta->twitterCard( );

This will add the following:

<meta name="twitter:card" content="summary" />

For a different type, just pass it as an argument:

$meta->twitterCard( MetaTags::TWITTER_CARD_SUMMARY_LARGE_IMAGE );
$meta->twitterCard( MetaTags::TWITTER_CARD_APP );
$meta->twitterCard( MetaTags::TWITTER_CARD_PLAYER );

You can also set the site and / or creator:

$meta
	->twitterSite( '@lukaswhite' )
	->twitterCreator( '@lukaswhite' );

The metadata — for example, the name and description — along with any media such as images will be fetched from the Open Graph tags.

You can, however, override these; if for any reason you want different metadata for Twitter:

$meta
	->twitterTitle( 'My Twitter' )
	->twitterDescription( 'A Twitter description' );

This will set twitter:title and twitter:description metatags respectively.

Here's a complete example:

$meta
	->title( 'My Website' )
	->description( 'A website' )
	->twitterCard( )
	->twitterSite( '@lukaswhite' )
	->twitterCreator( '@lukaswhite' );
	->addImage(
	( new \Lukaswhite\MetaTags\Entities\Image( ) )
		->setUrl( 'http://example.com/image.jpeg' )
		->setSecureUrl( 'https://example.com/image.jpeg' )
		->setType( 'image/jpeg' )
		->setWidth( 500 )
		->setHeight( 300 )
		->setAlt( 'Example image' )
);

Examples (Laravel-flavored)

These simplified examples use Laravel, but you can use this library using any framework - or indeed without a framework at all.

Registering as a Singleton

One approach you could take if you're using Laravel is to register the meta tags as a singleton; that way you can also start setting your meta tags during the bootstrap phase:

$this->app->singleton( MetaTags::class, function( $app ) {
	$meta = new MetaTags( );
	$meta->siteName( array_get( $app[ 'config' ], 'app.name' ) );
	return $meta;
} );

You can of course use a similar approach without Laravel, though the syntax will obviously be different.

Then, whenever you want to set your meta tags, for example in a controller, then you can get the instance like this:

$meta = app( )->make( MetaTags::class );

Because it's a singleton, you can build your meta tags from multiple parts of your application.

A Content Management System

This is a really basic example; it's pulling a page from the database, and setting the page title and page description from that.

class PagesController {

	public function show( Page $page )
	{
		app( )->make( MetaTags::class )
			->title( $page->title )
			->description( $page->description );
			
		// The rest of the method	
	}

}

A Restaurant Listing Site

In this example, the controller is pulling a restaurant record from the database in order to display it. It's using the meta tags library to match the title and description to the restaurant.

In addition, it's setting the Open Graph tags for the address of the restaurant, as well as pointing it to the geographical location.

class RestaurantController {

	public function show( Restaurant $restaurant )
	{
		app( )->make( MetaTags::class )
			->isBusiness( )
			->title( $restaurant->name )
			->description( $restaurant->description )
			->addGeopoint( 
				new Geopoint(
					$restaurant->lat, $restaurant->lng
				)
			)
			->streetAddress( $restaurant->street_address )
			->locality( $restaurant->locality )
			->region( $restaurant->locality )
			->postalCode( $restaurant->postal_code )
			->country( $restaurant->country )
			->phone( $restaurant->phone );
			
		// The rest of the method	
	}

}

Alternatively, if the Restaurant class implements the ContactData and Geopoint interfaces, you can simplify the code above significantly:

class RestaurantController {

	public function show( Restaurant $restaurant )
	{
		app( )->make( MetaTags::class )
			->isBusiness( )
			->title( $restaurant->name )
			->description( $restaurant->description )
			->addGeopoint( $restaurant )
			->contactData( $restaurant );
			
		// The rest of the method	
	}

}

A Social Network

This example demonstrates a simple example whereby this particular controller display's a user's profile on a social network, setting the appropriate Open Graph tags.

class User extends Model implements Profile {
	
	public class getFirstName( )
	{
		return $this->attributes[ 'first_name' ];
	}

	public class getLastName( )
	{
		return $this->attributes[ 'last_name' ];
	}

	public class getUsername( )
	{
		return $this->attributes[ 'username' ];
	}

	public class getGender( )
	{
		return $this->attributes[ 'gender' ];
	}

}

class ProfileController {

	public function show( User $user )
	{
		app( )->make( MetaTags::class )
			->profile( $user );
			
		// The rest of the method	
	}

}

Outputting the Tags

To render the tags, just call the render( ) method, for example:

<html>
	<head>
		<?php print $meta->render( ); ?>
	</head>

Or using Blade:

<html>
	<head>
		{!! $meta->render( ) !!}
	</head>

The class also implements the __toString( ) method, so you can even print it right out. It simply calls the render( ) method.

Customizing the Output

By default, the meta tag output includes newlines. You can turn this off using the verbose( ) method.

If you want even more control over the resulting string, you can set the tag prefix and tag suffix. For example, perhaps you want pretty-printed HTML:

$meta->tagPrefix( "\t\t" )
	->tagSuffix( "\n" );

The result will be along these lines:

<html>
	<head>
		<meta charset="utf-8" />
		<title>My Website</title>
		<meta name="description" content="A description here" />
	</head>

Should you want even more control over the output, or want to make any modifications, then you can call the build( ) method, which will return an array of HtmlElement instances. The documentation for that is here.

Geo Tags

You can also add geotags:

$meta->geoPosition( new Geopoint( 37.416343, -122.153013 ) )
	->geoPlaceName( 'London' )
	->geoRegion( 'GB' );

The result:

<meta name="geo.position" content="37.416343, -122.153013" />
<meta name="ICBM" content="37.416343, -122.153013" />
<meta name="geo.placename" content="London" />
<meta name="geo.region" content="GB" />	

Adding Additional Meta Tags

It's worth pointing out that you don't necessarily need to add all of your meta tags to your site using this library; indeed arguably there are some that you shouldn't.

Take this example:

<!DOCTYPE html>
<html lang="en">
	<head>
		{!! $meta !!}
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="stylesheet" href="/assets/styles.min.css">
	</head>

The meta tag and link tag are really theme-specific, so it might make things clearer if you add them to your layout in this way.

That's not to say you can't do it programmatically:

$meta->viewport( 'width=device-width, initial-scale=1' )
	->addLink( '/assets/styles.min.css', 'stylesheet' );