Published On: May 12th, 2020
With WordPress, it’s a good policy to keep your plugin use to a minimum. Plugins tend to be bloated, cause conflicts, and, when you use them, you are completely at the whim of the developer when it comes to updates/fixes/improvements/support. Not to mention, they can bring about some serious security risks.
Therefore, I (like most in the WordPress world) cannot recommend Advanced Custom Fields (“ACF”) enough. Advanced Custom Fields is a developers dream because it is trusted and can do the jobs of many plugins with just one.
The project in this tutorial was for a long-time pizza shop client. When we first built their website in 2012, we used a popular plugin to handle their food menu, but over time, the plugin was abandoned by its developers and lost compatibility with newer versions of WordPress. To prevent this from happening again, I decided to build a own menu system using ACF Pro*. The results: A menu that looks identical to the old one on the front-end, but that’s easier to manage and has tons of flexibility for the client.
Want to do it yourself? Here are the steps:
With a good idea of what will be displayed and how to display it, let’s put together a structure tree that will help us execute the plan. Remember that we’re working with multiple locations, each with different menus at each location. Therefore, we’ll be using two pages.
Here’s an overall look at what we’ll be creating:
<?php
/*
* Template Name: Menu Page
* Template Post Type: page
*/
get_header(); ?>
<div class="menuPage"> <!-- Wrapping the page in a menuPage class -->
<!-- This code opens the WordPress loop so that we can work with the information from the page manager. -->
<?php if(have_posts()) : ?><?php while(have_posts()) : the_post(); ?>
<div class="title">
<h1><?php the_title(); ?></h1> <!-- Adds the Page Title -->
<p>
<!-- This is our first ACF code. It says that if there is anything in the page_description field, it will go here. Since it's wrapped in <div class="title><p>, it will take on that styling -->
<?php if( get_field('page_description') ): ?>
<?php the_field('page_description'); ?>
<?php endif; ?>
</p>
</div>
<!-- The code below closes the WordPress Loop -->
<?php endwhile; ?>
<?php endif; ?>
</div>
<div class="menuBody">
<!-- Start by checking if there are any Sections -->
<?php if( have_rows('section') ): ?>
<!-- If there are, we return the following for each -->
<?php while( have_rows('section') ): the_row(); ?>
<div class="menuSection">
<h2>
<?php the_sub_field('section_name'); ?>
</h2>
<h4>
<?php if( get_sub_field('section_description') ): ?> // We'll put an 'if' statement here since some sections may not have a description
<?php the_sub_field('section_description'); ?> // ...But if they do, it'll appear.
<?php endif; ?>
</h4>
<!-- This 'if' statement checks if any Items are present... -->
<?php if( have_rows('item') ): ?>
<!-- ...and returns the following list with each instance of Item appearing as an <li> -->
<ul>
<?php while( have_rows('item') ): the_row(); ?>
<li class="menuItem">
<div class="itemTop">
<h3>
<?php if( get_sub_field('name') ): ?>
<?php the_sub_field('name'); ?>
<?php endif; ?>
</h3>
<p class="itemPrice">
<?php if( get_sub_field('price') ): ?> // If statement here in case we want to keep the price off a certain item
<?php the_sub_field('price'); ?>
<?php endif; ?>
</p>
</div>
<p>
<?php if( get_sub_field('description') ): ?> // If statement in case item does not need description
<?php the_sub_field('description'); ?>
<?php endif; ?>
</p>
</li>
<!-- This code closes out the Items list -->
<?php endwhile; ?>
</ul>
<?php endif; ?>
</div>
<!-- This code closes out the rest -->
<?php endwhile; ?>
<?php endif; ?>
</div>
All together, our new menu.php page will look like this:
<?php
/*
* Template Name: Menu Page
* Template Post Type: page
*/
get_header(); ?>
<div class="menuPage"> <!-- Wrapping the page in a menuPage class -->
<!-- This code opens the WordPress loop so that we can work with the information from the page manager. -->
<?php if(have_posts()) : ?><?php while(have_posts()) : the_post(); ?>
<div class="title">
<h1><?php the_title(); ?></h1> <!-- Adds the Page Title -->
<p>
<!-- This is our first ACF code. It says that if there is anything in the page_description field, it will go here. Since it's wrapped in <div class="title><p>, it will take on that styling -->
<?php if( get_field('page_description') ): ?>
<?php the_field('page_description'); ?>
<?php endif; ?>
</p>
</div>
<div class="menuBody">
<!-- Start by checking if there are any Sections -->
<?php if( have_rows('section') ): ?>
<!-- If there are, we return the following for each -->
<?php while( have_rows('section') ): the_row(); ?>
<div class="menuSection">
<h2>
<?php the_sub_field('section_name'); ?>
</h2>
<h4>
<?php if( get_sub_field('section_description') ): ?> // We'll put an 'if' statement here since some sections may not have a description
<?php the_sub_field('section_description'); ?> // ...But if they do, it'll appear.
<?php endif; ?>
</h4>
<!-- This 'if' statement checks if any Items are present... -->
<?php if( have_rows('item') ): ?>
<!-- ...and returns the following list with each instance of Item appearing as an <li> -->
<ul>
<?php while( have_rows('item') ): the_row(); ?>
<li class="menuItem">
<div class="itemTop">
<h3>
<?php the_sub_field('name'); ?>
</h3>
<p class="price">
<?php if( get_sub_field('price') ): ?> // If statement here in case we want to keep the price off a certain item
<?php the_sub_field('price'); ?>
<?php endif; ?>
</p>
</div>
<p>
<?php if( get_sub_field('description') ): ?> // If statement in case item does not need description
<?php the_sub_field('description'); ?>
<?php endif; ?>
</p>
</li>
<!-- This code closes out the Items list -->
<?php endwhile; ?>
</ul>
<?php endif; ?>
</div>
<!-- This code closes out the rest -->
<?php endwhile; ?>
<?php endif; ?>
</div>
<!-- The code below closes the WordPress Loop -->
<?php endwhile; ?>
<?php endif; ?>
</div>
<?php get_footer(); ?>
With that, we have our structure in place, so let’s jump back to the WordPress admin to add some content and see if things are working correctly. In the WordPress admin area, return back to the Location #1 page. A reminder that, at this point, it should look like this:
<p class="intro">Items are available for dine-in, pick up, and delivery (within a 5 mile radius).<br>
Call <a href="tel:XXXXXXXXXX">[PHONE NUMBER]</a> to order now</p>
This will allow to give the “intro” class some styling later and include a link so customers can easily tap to call and place their order.
At this point, your Page Editor for Location #1 should look like this.
.menuPage {
/* This is needed for my theme, you may not need it */
padding-top:75px;
}
.menuPage h1 {
/* This is the page title */
text-align: center;
border-top: 1px solid #7e0e0a;
border-bottom: 1px solid #7e0e0a;
color: #7e0e0a;
margin-bottom: 10px;
}
.menuPage p.intro {
/* This is page_description */
text-align:center;
font-size:21px;
line-height:42px;
}
.menuSection {
/* A little space between menu sections */
margin-bottom:100px;
}
.menuSection h2 {
/* This is section_name */
/* Background gradient generated via https://www.colorzilla.com/gradient-editor/ */
background: rgb(117,11,11);
background: -moz-linear-gradient(top, rgba(117,11,11,1) 1%, rgba(142,13,13,1) 100%);
background: -webkit-linear-gradient(top, rgba(117,11,11,1) 1%,rgba(142,13,13,1) 100%);
background: linear-gradient(to bottom, rgba(117,11,11,1) 1%,rgba(142,13,13,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#750b0b', endColorstr='#8e0d0d',GradientType=0 );
border-radius: 20px;
margin: 25px 50px 15px;
padding: 0.25em 0;
text-shadow: 1px 1px 1px #000;
font-size: 3em;
line-height: 1em;
text-align: center;
font-size: 36px;
color: #ffffff;
-webkit-box-shadow: 1px 1px 2px 2px rgba(1, 1, 1, .3);
box-shadow: 1px 1px 2px 2px rgba(1, 1, 1, .3);
}
.menuSection h4 {
/* This is section_description */
text-align: center;
color: #8e0d0d;
font-size: 18px;
line-height: 28px;
margin: 0 2em 2em;
font-style: italic;
}
.itemTop {
/* Flexbox is a quick way to align our title on the left and price on the right */
display:flex;
justify-content:space-between;
}
.itemTop h3 {
/* This is the Item Name */
color: #8e0d0d;
font-variant: small-caps;
font-family: 'Forum', cursive;
font-size: 32px;
line-height: 36px;
display: inline;
clear: none;
border-bottom: 1px dotted #8e0d0d;
}
.itemTop p.price {
/* This is the item price */
display: inline;
clear: none;
text-align: right;
font-style: italic;
font-weight: 800;
font-size: 0.9em;
padding-right: 10px;
}
That’s all you should need to get the following:
Was this tutorial helpful to you? Do you have any tips to make it better? Did I make a (gasp!) mistake? I love to hear feedback of all sorts, so click here to Contact Me.