It is not very complicated to understand it, if you follow the simple rules then you will be able to understand my 3 simple steps on how I did implement in Drupal 8 my Resume Profile Page. Source code can be downloaded for free from my Github repo, to do so please refers to my Drupal 8 Installation Guide.
Go to /admin/config/people/accounts/fields and add all the fields that you want to appears inside the Snippets such as:
LABEL | MACHINE NAME | FIELD TYPE |
---|---|---|
Body | field_body | Text (formatted, long) |
First name | field_first_name | Text (plain) |
Job Title | field_job_title | Text (plain) |
Last name | field_last_name | Text (plain) |
Meta Tags | field_meta_tags | Text (plain) |
Picture | user_picture | Image |
Right Body | field_right_body | Text (formatted, long) |
Telephone | field_telephone | Text (plain) |
Work For | field_work_for | Text (plain) |
Web site | field_web_site | Link |
When we display the User page, we want to generate this HTML:
<article typeof="schema:Person" about="/company" class="user-full-view clearfix">
<section class="col-sm-7">
<div class="region region-content">
<h1 class="page-header">Senior Drupal 8 Developer</h1>
<div class="block-3 clearfix">
<figure class="img-polaroid"><img src="/sites/default/files/pictures/pasquale-laudonio.jpg" width="256" height="207" alt="Senior Drupal 8 Developer" title="Freelancer Senior Drupal 8 Developer in London" property="schema:image" class="img-responsive"></figure>
<div class="block-3 personal-details clearfix">
<div class="col-sm-12 col-md-6">
<div class="field field--label-inline">
<div class="field--label">First name</div>
<div property="schema:givenName" class="field--item">Pasquale</div>
</div>
<div class="field field--label-inline">
<div class="field--label">Last name</div>
<div property="schema:familyName" class="field--item">Laudonio</div>
</div>
<div class="field field--label-inline">
<div class="field--label">Job Title</div>
<div property="schema:jobTitle" class="field--item">Senior Drupal 8 Developer</div>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div class="field field--label-inline">
<div class="field--label">Telephone</div>
<div property="schema:telephone" class="field--item">+44 (0)7414982245</div>
</div>
<div class="field field--label-inline">
<div class="field--label">Work for</div>
<div property="schema:worksFor">
<span property="schema:name" typeof="schema:Organization" about="http://maria-consulting.co.uk/">Maria Consulting LTD</span>
</div>
</div>
<div class="field field--label-inline">
<div class="field--label">Web site</div>
<div class="field--item">
<a href="http://maria-consulting.co.uk/">http://maria-consulting.co.uk/</a>
</div>
</div>
</div>
</div>
</div>
</section>
<aside class="col-sm-5" role="complementary">
<h2 class="block-title">Pasquale Laudonio</h2>
<div class="block-5">
<p class="lead" property="schema:description">Reliable, organised, hard working, highly skilled, enthusiastic and self-motivated; good communication skills; system constantly improved; eager to learn and keep up to date with new technologies.</p>
<p property="schema:knowsAbout">20 years experience in IT industry as Senior Full Stack Developer with great expertise in Drupal 7 and 8, Custom Modules, Linux, Apache, MySQL, PHP, REST APIs, VueJS, Angular, jquery, Ajax and HTML/CSS.</p>
<p property="schema:alumniOf" typeof="schema:OrganizationRole"><span property="schema:alumniOf" typeof="schema:CollegeOrUniversity"> Computer Science Degree at <span property="schema:name">University of Sannio</span> <link property="schema:sameAs" href="https://en.wikipedia.org/wiki/University_of_Sannio">
</span> from <span property="schema:startDate">1995</span> to <span property="schema:startDate">2001</span></p>
</div>
<h2 class="block-title">Most recent roles</h2>
<div class="organization-role" property="schema:alumniOf" typeof="schema:OrganizationRole">
<span property="schema:alumniOf" typeof="schema:Organization">
<span class="company-name" property="schema:name">Atelier7</span>
<span property="schema:address" typeof="schema:PostalAddress">
<span property="schema:streetAddress"> 19 Hatfields</span>
<span property="schema:addressLocality"> London</span>
<span property="schema:postalCode"> SE1 8DJ</span>
</span>
</span>
<span class="job-period">
From <span property="schema:startDate">2020-01-07</span>
to <span property="schema:endDate">2020-04-07</span>
</span>
<span property="schema:roleName" class="hidden">Senior Drupal 8 Developer</span>
<span property="schema:sameAs" class="hidden">/drupal-8-developer-april-20-atelier7</span>
</div>
</aside>
</article>
Please do not worry too much about the complexity of the HTML because Drupal adds some extra DIVs inside the Output, in general to create a Resume Snippet in any language and in any CMS rather than Drupal you must simply focus on the most important HTML elements. For example try to understand Basic elements such as:
<article typeof="schema:Person" about="/company">
<section class="col-sm-7">
<h1 class="page-header">Senior Drupal 8 Developer</h1>
<div class="block-3 clearfix">
<figure class="img-polaroid"><img src="/sites/default/files/pictures/pasquale-laudonio.jpg" width="256" height="207" alt="Senior Drupal 8 Developer" title="Freelancer Senior Drupal 8 Developer in London" property="schema:image" class="img-responsive"></figure>
<div class="block-3 personal-details clearfix">
<div class="col-sm-12 col-md-6">
First name: <span property="schema:givenName">Pasquale</span>
Last name: <span property="schema:familyName">Laudonio</span>
Job Title: <span property="schema:jobTitle">Senior Drupal 8 Developer</span>
Telephone: <span property="schema:telephone">+44 (0)7414982245</span>
Work for: <span property="schema:worksFor">Maria Consulting LTD</span>
Web site: <a href="http://maria-consulting.co.uk/">http://maria-consulting.co.uk/</a>
</div>
</div>
</section>
</article>
The concept is very simple, first you have to define the Type of Person as main container. Then inside this container you can add all the Properties which are allowed for this Particular Type: Person.
In my case I added my Photo, Given Name, Family Name, Job title, Telephone, Work For. As you can see all properties as defined as: property="schema:image", do not forget the add "schema:" otherwise the property will no be recognised.
For the interest of Drupal Themers this is how it looks like the code inside my custom Twig: user--full.html.twig, please note there are different way in Drupal 8 to add a Schema property to a field, in my case I preferred to use the "Twig Field Value" contributed module to extra label and value using the field_label and field_value provided from this module which allows Drupal Themers to print field labels and field values individually.
For people who are not used to created custom Twig Templates, you can also use the rdf_ui module which allows to associate a schema property to a field by creating a Mapping into the Entity Definition, but obviously if you do not implement your own twig then you got the standard output printed by Drupal which has got some limitations in what you can print unless you use extra Tools such as Display Suite or Panels. For me the best way I assume would be to use both modules (twig_field_value and drf_ui) plus implementing a custom template such as mine for example:
{% if content.field_body %}
{%- for key, field_body_item in content.field_body['#items'] -%}
{%- if key == 0 -%}
<div class="block-3 clearfix">
<figure class="img-polaroid">
{{- content.user_picture -}}
</figure>
<div class="field--item">
{{- content.field_body[key] -}}
</div>
</div>
<div class="block-3 personal-details clearfix">
<div class="col-sm-12 col-md-6">
<div class="field field--label-inline">
<div class="field--label">{{- content.field_first_name|field_label -}}</div>
<div property="schema:givenName"
class="field--item">{{- content.field_first_name|field_value -}}</div>
</div>
<div class="field field--label-inline">
<div class="field--label">{{- content.field_last_name|field_label -}}</div>
<div property="schema:familyName"
class="field--item">{{- content.field_last_name|field_value -}}</div>
</div>
<div class="field field--label-inline">
<div class="field--label">{{- content.field_job_title|field_label -}}</div>
<div property="schema:jobTitle"
class="field--item">{{- content.field_job_title|field_value -}}</div>
</div>
</div>
<div class="col-sm-12 col-md-6">
<div class="field field--label-inline">
<div class="field--label">{{- content.field_telephone|field_label -}}</div>
<div property="schema:telephone"
class="field--item">{{- content.field_telephone|field_value -}}</div>
</div>
<div class="field field--label-inline">
<div class="field--label">Work for</div>
<div property="schema:worksFor">
<span property="schema:name" typeof="schema:Organization"
about="{{- content.field_web_site[0]['#url'] -}}">{{- content.field_web_site[0]['#title'] -}}</span>
</div>
<div about="{{- content.field_web_site[0]['#url'] -}}">
<span property="schema:address" class="field PostalAddress"
typeof="schema:PostalAddress">
<span property="schema:streetAddress">1 Chelsfield House</span>,
<span property="schema:addressLocality">London UK</span>
<span property="schema:postalCode">SE17 1SX</span>
</span>
</div>
</div>
<div class="field" property="schema:alumniOf" typeof="schema:OrganizationRole">
<span property="schema:alumniOf" typeof="schema:CollegeOrUniversity">
Computer Science Degree at <span property="schema:name">University of Sannio</span>
<link property="schema:sameAs" href="https://en.wikipedia.org/wiki/University_of_Sannio">
</span> from <span property="schema:startDate">1995</span> to <span property="schema:startDate">2001</span>
</div>
<div class="field field--label-inline">
<div class="field--label">{{- content.field_web_site|field_label -}}</div>
<div class="field--item">
<a href="{{- content.field_web_site[0]['#url'] -}}">{{- content.field_web_site[0]['#url'] -}}</a>
</div>
</div>
</div>
</div>
{%- else -%}
<div class="field--item">
{{- content.field_body[key] -}}
</div>
{%- endif -%}
{%- endfor -%}
{% endif %}
As you can see, the most interesting things inside this Twig are:
- I created a relationship between the Company Type ("Work for") and the Company Postal Address Type:
The property address does not belong to the Person but to the Company, to do that I did use about="<company_utl>", and you can see the Company has got the typeOf="schema:Organization". This means the HTML element with property workfor inside contains another element which is a typeOf rather than a simple atomic value. - I created also a relationship between the Person (me) and the University where I did study using the property="schema:alumniOf". This property can contains inside a typeof="schema:OrganizationRole" which can be an property="schema:alumniOf" typeof="schema:CollegeOrUniversity". Which means in few words: I was student (role) in University of Sannio during that Role period (property="schema:startDate" and property="schema:endtDate").
By Using the Google Structured Data Testing Tool, we can see Google Recognised successfully all the elements inside my Resume:
@type
|
Person
|
@id
|
|
name
|
Pasquale Laudonio
|
image
|
|
givenName
|
Pasquale
|
familyName
|
Laudonio
|
jobTitle
|
Senior Drupal 8 Developer
|
telephone
|
+44 (0)7414982245
|
description
|
Reliable, organised, hard working, highly skilled, enthusiastic and self-motivated; good communication skills; system constantly improved; eager to learn and keep up to date with new technologies.
|
knowsAbout
|
20 years experience in IT industry as Senior Full Stack Developer with great expertise in Drupal 7 and 8, Custom Modules, Linux, Apache, MySQL, PHP, REST APIs, VueJS, Angular, jquery, Ajax and HTML/CSS.
|
worksFor
|
|
@type
|
Organization
|
name
|
Maria Consulting LTD
|
alumniOf
|
|
@type
|
OrganizationRole
|
startDate
|
1995
|
startDate
|
2001
|
alumniOf
|
|
@type
|
CollegeOrUniversity
|
name
|
University of Sannio
|
sameAs
|
|
alumniOf
|
|
@type
|
OrganizationRole
|
startDate
|
2020-01-07
|
endDate
|
2020-04-07
|
roleName
|
Senior Drupal 8 Developer
|
sameAs
|
maria-consulting.co.uk/drupal-8-developer-april-20-atelier7
|
alumniOf
|
|
@type
|
Organization
|
name
|
Atelier7
|
address
|
|
@type
|
PostalAddress
|
streetAddress
|
19 Hatfields
|
addressLocality
|
London
|
postalCode
|
SE1 8DJ
|
Understanding how to create Relationship between a Person and OrganizationRole / Organization is essential to create a complete Resume Snippet. The most important part of the CV, usually are the work Experiences and the Education displayed in a chronological order showing date periods.
It is very rewarding to see that when you put all your meta data in the correct way, then Google can recognise all your Roles in all your different Organisations, Addresses and also all the Date Periods. To see how I did implement these complex Relationships please let's see more details in the section 3.
I added a new Entity called Work Experience with the following fields:
LABEL | MACHINE NAME | FIELD TYPE |
---|---|---|
Body | body | Text (formatted, long, with summary) |
Company details | field_company_details | Text (formatted) |
Job title | field_job_title | Text (plain) |
Period | field_period | Date range |
Project Description | field_project_description | Text (formatted, long) |
Responsibility | field_responsibility | Text (formatted, long) |
Technologies | field_technologies | Text (formatted, long |
I added all my Work Experience into the Drupal CMS and then I created a custom View to show all of them. In order to create the relationship I created this Custom Twig Template:
{% if title %}
<h3>{{ title }}</h3>
{% endif %}
{% for row in rows %}
{%
set row_classes = [
default_row_class ? 'views-row',
]
%}
<div{{ row.attributes.addClass(row_classes) }}>
{% if row.full_name %}
<div class="organization-role" property="schema:alumniOf" typeof="schema:OrganizationRole">
<span property="schema:alumniOf" typeof="schema:Organization">
<span class="company-name" property="schema:name">{{ row.company_details.company }}</span>
<span property="schema:address" typeof="schema:PostalAddress">
<span property="schema:streetAddress">{{ row.company_details.address }}</span>
<span property="schema:addressLocality">{{ row.company_details.city }}</span>
<span property="schema:postalCode">{{ row.company_details.post_code }}</span>
</span>
</span>
<span class="job-period">
From <span property="schema:startDate">{{ row.company_details.start }}</span>
to <span property="schema:endDate">{{ row.company_details.end }}</span>
</span>
<span property="schema:roleName" class="hidden">{{ row.company_details.job_title }}</span>
<span property="schema:sameAs" class="hidden">{{ row.company_details.company_url }}</span>
</div>
{% endif %}
{{- row.content -}}
</div>
{% endfor %}
As I said in the previous section, when I implemented my web site I was a big fan of custom templates because it gives the possibility to output whatever you like and I avoided the more complex but more "configurable" approach of using Panel and Display Suite because as Drupal Themer and super administrator I do not have any limitation on what can be the HTML output of my web pages.
Obviously if I was building this site for someone less technical than me then I had to spend more time and create something more generic and easier to maintain. I believe this code is a fantastic starting point if you wish to extend it and make a custom mode more re-usable and generic to create a Resume snippet.
Here, Im creating hard coded relationships between Person and OrganizationRole using property="schema:alumniOf" then inside the Role you have the Organization, and the Organization has got inside a type of PostalAddress. Role has got inside also other properties for startDate, endDate, RoleName. SameAs means that this is a preview to a Page where I put all the Details about this role.