Recipe plugin for Craft CMS
A comprehensive recipe FieldType for Craft CMS that includes metric/imperial conversion, portion calculation, and JSON-LD microdata support
Related: Recipe for Craft 2.x
Requirements
This plugin requires Craft CMS 3.0.0 or later or Craft CMS 4.0.0 or later.
Installation
To install Recipe, follow these steps:
- Install with Composer via
composer require nystudio107/craft-recipe
- Install the plugin via
./craft install/plugin recipe
via the CLI, or in the Control Panel, go to Settings → Plugins and click the “Install” button for Recipe.
You can also install Recipe via the Plugin Store in the Craft AdminCP.
Recipe Overview
Recipe adds a 'Recipe’ FieldType for Craft CMS that you can add to any of your Sections.
In encapsulates everything you need for a recipe, including the ingredients, a photo of the recipe, directions, cooking time, ratings, and even nutritional information. It handles converting between Imperial and Metric units, outputs 'pretty’ fractions for Imperial units, and can output correct ingredient portions for any number of servings.
Recipe also generates the JSON-LD microdata for your recipes, which allows it to be displayed in the Google knowledge panel for search results.
We hope Recipe makes it easier for you to create and share some yummy recipes!
Configuring Recipe
Create a Recipe field via Settings->Fields and you can set the Asset Sources that are used for the recipe images
Using Recipe
Once you have created the Recipe field, add it to your Section Entry Types, and fill in what recipe information is appropriate. Nothing other than the name is required, so feel free to leave anything blank that you’re not using.
Using Recipe in your Templates
To display information about a recipe in your templates, you just use familiar Twig code. Let’s assume the field handle for your Recipe field is someRecipe
; this is what you’d use to output information about it:
Basic Info
entry.someRecipe.name
- the name of the recipeentry.someRecipe.description
- the description of the recipeentry.someRecipe.recipeCategory
- The category of the recipe—for example, appetizer, entree, etc.entry.someRecipe.recipeCuisine
- The cuisine of the recipe (for example, French or Ethiopian).entry.someRecipe.skill
- the skill level required to make this recipeentry.someRecipe.serves
- the raw number of how many people the recipe servesentry.someRecipe.getServes()
- how many people the recipe serves combined with theentry.someRecipe.servesUnit
entry.someRecipe.getImageUrl()
- a URL to the image for the recipe; you can pass in an optional image transform or image transform handle here as well:entry.someRecipe.getImageUrl('display')
entry.someRecipe.getVideoUrl()
- a URL to the video for the recipeentry.someRecipe.prepTime
- the prep time for the recipe in minutesentry.someRecipe.cookTime
- the cooking time for the recipe in minutesentry.someRecipe.totalTime
- the total time for the recipe in minutes
Nutritional Facts
For a nutrition facts label, you can use:
{{ entry.someRecipe.renderNutritionFacts() }}
Which will output a responsive nutrition facts label embed based on the data in the Nutrition tab:
The percentages are based on US Recommended Dietary Allowances, but you can pass in your own values as well:
{{ entry.someRecipe.renderNutritionFacts({
'calories': 2000,
'carbohydrateContent': 275,
'cholesterolContent': 300,
'fatContent': 78,
'fiberContent': 28,
'proteinContent': 50,
'saturatedFatContent': 20,
'sodiumContent': 2300,
'sugarContent': 50,
}) }}
To control the way the template looks, you can put your own frontend template in recipe/recipe-nutrition-facts
and Recipe will use it
Ingredients
For a list of ingredients, do the following (adding whatever output markup you want):
{% set ingredients = entry.someRecipe.getIngredients('imperial', 1) %}
{% for ingredient in ingredients %}
{{ ingredient }}
{% endfor %}
The first parameter is the units you’d like to use ('imperial'
or 'metric'
). The second parameter is how many people you’d like the recipe portions to be sized for. By default, it will use 'imperial'
and the serving size in the recipe if you don’t pass these parameters in, for example: entry.someRecipe.getIngredients()
Directions
For a list of directions, do the following (adding whatever output markup you want):
{% set directions = entry.someRecipe.getDirections() %}
{% for direction in directions %}
{{ direction }}
{% endfor %}
Equipment
For a list of equipment, do the following (adding whatever output markup you want):
{% set equipment = entry.someRecipe.getEquipment() %}
{% for item in equipment %}
{{ item }}
{% endfor %}
Ratings
For a list of the ratings, do the following (adding whatever output markup you want):
{% set ratings = entry.someRecipe.ratings %}
{% for rating in ratings %}
{{ rating.rating }} {{ rating.review }} {{ rating.author }}
{% endfor %}
For the aggregate (average) rating for this recipe, do the following (adding whatever output markup you want):
{{ entry.someRecipe.getAggregateRating() }}
Nutritional Information
To output the nutritional information for the recipe, do the following:
entry.someRecipe.servingSize
- The serving size, in terms of the number of volume or massentry.someRecipe.calories
- The number of calories per servingentry.someRecipe.carbohydrateContent
- The number of grams of carbohydrates per servingentry.someRecipe.cholesterolContent
- The number of milligrams of cholesterol per servingentry.someRecipe.fatContent
- The number of grams of fat per servingentry.someRecipe.fiberContent
- The number of grams of fiber per servingentry.someRecipe.proteinContent
- The number of grams of protein per servingentry.someRecipe.saturatedFatContent
- The number of grams of saturated fat per servingentry.someRecipe.sodiumContent
- The number of milligrams of sodium per servingentry.someRecipe.sugarContent
- The number of grams of sugar per servingentry.someRecipe.transFatContent
- The number of grams of trans fat per servingentry.someRecipe.unsaturatedFatContent
- The number of grams of unsaturated fat per serving
Image Asset ID
To do any further manipulation of the Recipe Image (perhaps a transform) you can get the Asset ID for it:
entry.someRecipe.imageId
- the Asset ID of the image for the recipe
Multi-Component Recipes
The Recipe field conceptually encompasses a single recipe. However, if you require multiple components in a recipe you can still use Recipe.
An example of a multiple component recipe might be a dish that requires a sauce that’s prepared separately from the main dish.
What you can do is create a Matrix field that contains a Recipe field.
Then the content author can create as many separate recipe components as they like, each with their own separate recipe in it.
You can also add whatever other Craft fields you might like to the Matrix field that apply to all of the recipe components.
For example, you might have a Recipe Name field that is the aggregate name of the whole recipe.
Rendering Recipe JSON-LD Microdata
Using SEOmatic
If you are using the SEOmatic plugin, you can create a MetaJsonLd model from the Recipe field data:
{% do recipeMetaJsonLd = entry.someRecipe.createRecipeMetaJsonLd() %}
This creates the MetaJsonLd model, and by default adds it to the container so that SEOmatic will render it on the page.
You can modify the MetaJsonLd before it renders, just like you can any SEOmatic MetaJsonLd item. Extensive examples can be found in the Annotated JSON-LD Structured Data Examples article.
If you’re adding a single recipe to the page, and it should be the Main Entity of the page, pass in mainEntityOfPage
as the first key
parameter:
{% do recipeMetaJsonLd = entry.someRecipe.createRecipeMetaJsonLd('mainEntityOfPage') %}
If you just want to create the MetaJsonLd object but not add it to the container, you can pass in false
as the second parameter:
{% do recipeMetaJsonLd = entry.someRecipe.createRecipeMetaJsonLd(null, false) %}
You might do this if you wanted to create one or more Recipe MetaJsonLd items to be added a sub-properties of another MetaJsonLd object.
Manually rendering
If you’re not using SEOmatic, Recipe can manually render JSON-LD microdata for you, which allows it to be displayed in the Google knowledge panel for search results:
{{ entry.someRecipe.renderRecipeJSONLD() }}
Typically you would want to render the JSON-LD before the </body>
tag.
Importing Recipes with Feed Me
Recipes can be imported using the first-party Feed Me plugin by Pixel & Tonic. Ingredients, directions and ratings can be repeated as shown below.
XML
<Recipe>
<Name>Dough</Name>
<Description>Simple dough recipe.</Description>
<Ingredients>
<Row>
<Quantity>1</Quantity>
<Units>cups</Units>
<Ingredient>Lukewarm water</Ingredient>
</Row>
<Row>
<Quantity>0.5</Quantity>
<Units>cups</Units>
<Ingredient>Unsalted butter</Ingredient>
</Row>
<Row>
<Quantity>2</Quantity>
<Units>cups</Units>
<Ingredient>Flour</Ingredient>
</Row>
</Ingredients>
<Directions>
<Row>
<Direction>Mix and stir.</Direction>
</Row>
</Directions>
<Reviews>
<Row>
<Rating>5</Rating>
<Review>Works a charm.</Review>
<Author>Arthur</Author>
</Row>
<Row>
<Rating>3</Rating>
<Review>It's just dough.</Review>
<Author>Alice</Author>
</Row>
</Reviews>
</Recipe>
JSON
{
"Recipe": {
"Name": "Recipe #1",
"Description": "Simple dough recipe.",
"Ingredients": [
{
"Quantity": 1,
"Units": "cups",
"Ingredient": "Lukewarm water"
},
{
"Quantity": 0.5,
"Units": "cups",
"Ingredient": "Unsalted butter"
},
{
"Quantity": 2,
"Units": "cups",
"Ingredient": "Flour"
}
],
"Directions": [
{
"Direction": "Mix and stir."
}
],
"Ratings": [
{
"Rating": 5,
"Review": "Works a charm.",
"Author": "Arthur"
},
{
"Rating": 3,
"Review": "It's just dough.",
"Author": "Alice"
}
]
}
}
Fetching Nutritional Information from an API
Nutritional information for recipe ingredients can be fetched in the Nutrition tab using the Edamam Nutrition Analysis API. You must first get an API application ID and key and enter them in the plugin settings. A Fetch Nutritional Information
button will then appear in the Nutrition
tab of the recipe field.
A console command to generate nutritional information from the API for all entries in a specific section is also available.
./craft recipe/nutrition-api/generate --section=recipes --field=recipe
Both the section
(section handle) and field
(recipe field handle) options are required. Note that this command will overwrite the nutritional information for every entry in the given section.
Brought to you by nystudio107