Project Overview
In this weekend project, you'll create a complete WordPress site with a custom theme and plugin that work together to solve a real-world problem. You'll apply George Polya's proven 4-step problem-solving methodology to guide your development process.
George Polya's Problem-Solving Approach
George Polya was a Hungarian mathematician who developed a systematic approach to problem-solving in his book "How to Solve It" (1945). His 4-step process has been widely adopted across disciplines, including software development.
Step 1: Understand the Problem
Define exactly what you're trying to solve before writing any code.
- What is the problem statement?
- What are the constraints?
- What are the desired outcomes?
- Do you have all the necessary information?
Step 2: Devise a Plan
Strategize your approach before diving into implementation.
- Break down the problem into smaller parts
- Consider alternative approaches
- Draw from similar problems you've solved
- Create a step-by-step strategy
Step 3: Execute the Plan
Implement your solution according to your plan.
- Follow your strategy step by step
- Create incremental, testable components
- Verify each step as you go
- Persist through challenges
Step 4: Review and Extend
Evaluate your solution and identify improvements.
- Test thoroughly against requirements
- Review code for quality and optimization
- Learn from the process
- Identify extensions and refinements
This methodology aligns perfectly with the software development process. When applied to WordPress development, it helps create more thoughtful, robust solutions that truly solve user problems rather than just implementing features.
The Weekend Project Challenge
Your challenge is to create a "Portfolio Showcase" WordPress site for creative professionals with the following components:
- Custom Theme: A responsive portfolio theme optimized for showcasing creative work
- Custom Plugin: A portfolio management plugin that extends WordPress with project management features
- Integration: Seamless interaction between the theme and plugin
This project will test and solidify your understanding of WordPress theme development, plugin architecture, and the integration between the two, while solving a real-world need for creative professionals.
Step 1: Understand the Problem
Following Polya's first step, let's thoroughly understand what we're trying to build.
Problem Statement
Creative professionals (designers, photographers, artists, etc.) need a clean, customizable way to showcase their work online. They need:
- An attractive, professional presentation of their portfolio
- Easy management of portfolio projects
- Categorization and filtering of projects
- Detailed project information display
- Performance optimization for visual content
Research Existing Solutions
Before building, examine existing portfolio themes and plugins to understand:
- Common features and functionality
- User experience patterns
- What works well and what could be improved
Define Requirements
Based on the problem statement and research, define specific requirements:
Theme Requirements:
- Responsive design optimized for various screen sizes
- Clean, minimal aesthetic that highlights the work
- Grid-based portfolio display with filtering capability
- Detailed single project template with image gallery support
- Customization options through the WordPress Customizer
- Support for standard WordPress features (menus, widgets, etc.)
Plugin Requirements:
- Custom Post Type for portfolio projects
- Custom taxonomies for project categories and tags
- Custom meta fields for project details (client, date, URL, etc.)
- Image gallery management for projects
- Shortcode for displaying portfolio items
- Admin interface for portfolio settings
Understanding Exercise
Before proceeding, answer these questions to ensure you fully understand the problem:
- Who is the primary user of your solution?
- What are their top three needs/pain points?
- What makes a portfolio showcase effective?
- How should the theme and plugin work together?
- What are potential technical challenges you might face?
Step 2: Devise a Plan
Now that we understand the problem, let's create a detailed plan for our development.
Theme Development Plan
- Theme Structure:
- Create a standard WordPress theme directory structure
- Implement template hierarchy with focus on portfolio templates
- Set up style organization with SASS/SCSS
- Core Files:
- style.css with theme information
- functions.php for theme setup and customizations
- index.php, header.php, footer.php as base templates
- Portfolio-Specific Templates:
- archive-portfolio.php for the portfolio grid view
- single-portfolio.php for individual project display
- taxonomy-portfolio-category.php for filtered views
- Theme Features:
- Customizer settings for colors, typography, and layout options
- JavaScript for filtering and interactive elements
- CSS Grid or Flexbox for responsive portfolio display
Plugin Development Plan
- Plugin Structure:
- Create object-oriented plugin architecture
- Set up activation, deactivation, and uninstall hooks
- Implement autoloading for classes
- Custom Post Type:
- Register 'portfolio' post type with appropriate supports
- Register 'portfolio-category' and 'portfolio-tag' taxonomies
- Add project metadata fields (client, date, URL, etc.)
- Admin Interface:
- Create settings page for plugin configuration
- Implement meta boxes for project details
- Add media management for project galleries
- Frontend Features:
- Create shortcode for portfolio display
- Implement AJAX filtering functionality
- Add template functions for theme integration
Integration Plan
- Theme Support for Plugin:
- Check if plugin is active and provide graceful fallback
- Add specific styling for plugin elements
- Implement plugin template functions in theme templates
- Plugin Support for Theme:
- Provide template functions for theme developers
- Add theme-specific hooks for customization
- Ensure plugin outputs are semantically structured for styling
Development Schedule
Break down the weekend into manageable time blocks:
Planning Exercise
Before moving to implementation, complete these planning tasks:
- Sketch the portfolio grid layout and single project view
- Create a more detailed file structure for both the theme and plugin
- List all the hooks your plugin will use and provide
- Define the database schema for your custom post type and meta fields
- Identify potential reusable components or functions
Step 3: Execute the Plan
Now it's time to implement your solution according to the plan. Below are key implementation details for both the theme and plugin.
Theme Implementation
Initial Theme Setup
// functions.php
<?php
/**
* Portfolio Theme functions and definitions
*/
// Theme setup function
function portfolio_theme_setup() {
// Add theme support
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('html5', array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
));
add_theme_support('customize-selective-refresh-widgets');
add_theme_support('responsive-embeds');
// Register menus
register_nav_menus(array(
'primary' => esc_html__('Primary Menu', 'portfolio-theme'),
'footer' => esc_html__('Footer Menu', 'portfolio-theme'),
));
// Add image sizes for portfolio
add_image_size('portfolio-thumbnail', 450, 450, true);
add_image_size('portfolio-large', 1200, 800, false);
}
add_action('after_setup_theme', 'portfolio_theme_setup');
// Enqueue scripts and styles
function portfolio_theme_scripts() {
wp_enqueue_style('portfolio-theme-style', get_stylesheet_uri(), array(), '1.0.0');
wp_enqueue_script('portfolio-theme-navigation', get_template_directory_uri() . '/js/navigation.js', array(), '1.0.0', true);
// Portfolio-specific scripts
if (is_post_type_archive('portfolio') || is_singular('portfolio') || is_tax('portfolio-category')) {
wp_enqueue_script('portfolio-theme-isotope', get_template_directory_uri() . '/js/isotope.pkgd.min.js', array('jquery'), '3.0.6', true);
wp_enqueue_script('portfolio-theme-portfolio', get_template_directory_uri() . '/js/portfolio.js', array('jquery', 'portfolio-theme-isotope'), '1.0.0', true);
}
}
add_action('wp_enqueue_scripts', 'portfolio_theme_scripts');
// Check if Portfolio plugin is active
function portfolio_theme_has_plugin() {
return class_exists('Portfolio_Plugin');
}
// Include theme files
require get_template_directory() . '/inc/customizer.php';
Portfolio Archive Template
// archive-portfolio.php
<?php
/**
* The template for displaying portfolio archives
*/
get_header();
?>
<main id="primary" class="site-main">
<header class="page-header">
<h1 class="page-title">
<?php esc_html_e('Portfolio', 'portfolio-theme'); ?>
</h1>
<?php if (portfolio_theme_has_plugin()) : ?>
<div class="portfolio-filters">
<button class="filter-button active" data-filter="*"><?php esc_html_e('All', 'portfolio-theme'); ?></button>
<?php
$categories = get_terms(array(
'taxonomy' => 'portfolio-category',
'hide_empty' => true,
));
foreach ($categories as $category) {
printf(
'<button class="filter-button" data-filter=".%s">%s</button>',
esc_attr($category->slug),
esc_html($category->name)
);
}
?>
</div>
<?php endif; ?>
</header>
<div class="portfolio-grid">
<?php
if (have_posts()) :
while (have_posts()) :
the_post();
// Get categories for filtering
$categories = '';
$terms = get_the_terms(get_the_ID(), 'portfolio-category');
if ($terms && !is_wp_error($terms)) {
$category_slugs = array();
foreach ($terms as $term) {
$category_slugs[] = $term->slug;
}
$categories = join(' ', $category_slugs);
}
?>
<article id="post-<?php the_ID(); ?>" <?php post_class('portfolio-item ' . $categories); ?>>
<a href="<?php the_permalink(); ?>" class="portfolio-link">
<div class="portfolio-thumbnail">
<?php
if (has_post_thumbnail()) {
the_post_thumbnail('portfolio-thumbnail');
} else {
echo '<img src="' . esc_url(get_template_directory_uri()) . '/images/placeholder.png" alt="Placeholder" />';
}
?>
</div>
<h2 class="portfolio-title"><?php the_title(); ?></h2>
<?php if (portfolio_theme_has_plugin() && function_exists('portfolio_get_client')) : ?>
<div class="portfolio-meta">
<span class="portfolio-client"><?php echo esc_html(portfolio_get_client(get_the_ID())); ?></span>
</div>
<?php endif; ?>
</a>
</article>
<?php
endwhile;
else :
get_template_part('template-parts/content', 'none');
endif;
?>
</div>
<?php the_posts_navigation(); ?>
</main>
<?php
get_footer();
Plugin Implementation
Main Plugin File
<?php
/**
* Plugin Name: Portfolio Plugin
* Plugin URI: https://example.com/portfolio-plugin
* Description: A portfolio management plugin for creative professionals.
* Version: 1.0.0
* Author: Your Name
* Author URI: https://example.com
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: portfolio-plugin
* Domain Path: /languages
*/
// If this file is called directly, abort.
if (!defined('WPINC')) {
die;
}
// Define plugin constants
define('PORTFOLIO_PLUGIN_VERSION', '1.0.0');
define('PORTFOLIO_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('PORTFOLIO_PLUGIN_URL', plugin_dir_url(__FILE__));
/**
* Main plugin class
*/
class Portfolio_Plugin {
/**
* Instance of this class
*/
private static $instance = null;
/**
* Get singleton instance
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
// Initialize plugin
add_action('plugins_loaded', array($this, 'load_textdomain'));
add_action('init', array($this, 'register_post_types'));
add_action('init', array($this, 'register_taxonomies'));
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
add_action('save_post_portfolio', array($this, 'save_meta_boxes'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
add_shortcode('portfolio', array($this, 'portfolio_shortcode'));
// Register activation/deactivation hooks
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
}
/**
* Load plugin text domain
*/
public function load_textdomain() {
load_plugin_textdomain('portfolio-plugin', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
/**
* Register portfolio post type
*/
public function register_post_types() {
$labels = array(
'name' => _x('Portfolio Items', 'Post type general name', 'portfolio-plugin'),
'singular_name' => _x('Portfolio Item', 'Post type singular name', 'portfolio-plugin'),
'menu_name' => _x('Portfolio', 'Admin Menu text', 'portfolio-plugin'),
'add_new' => __('Add New', 'portfolio-plugin'),
'add_new_item' => __('Add New Portfolio Item', 'portfolio-plugin'),
'edit_item' => __('Edit Portfolio Item', 'portfolio-plugin'),
'new_item' => __('New Portfolio Item', 'portfolio-plugin'),
'view_item' => __('View Portfolio Item', 'portfolio-plugin'),
'search_items' => __('Search Portfolio Items', 'portfolio-plugin'),
'not_found' => __('No portfolio items found', 'portfolio-plugin'),
'not_found_in_trash' => __('No portfolio items found in Trash', 'portfolio-plugin'),
'all_items' => __('All Portfolio Items', 'portfolio-plugin'),
'featured_image' => __('Portfolio Image', 'portfolio-plugin'),
'set_featured_image' => __('Set portfolio image', 'portfolio-plugin'),
'remove_featured_image' => __('Remove portfolio image', 'portfolio-plugin'),
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'portfolio'),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 20,
'menu_icon' => 'dashicons-portfolio',
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
'show_in_rest' => true,
);
register_post_type('portfolio', $args);
}
/**
* Register portfolio taxonomies
*/
public function register_taxonomies() {
// Register category taxonomy
$labels = array(
'name' => _x('Portfolio Categories', 'taxonomy general name', 'portfolio-plugin'),
'singular_name' => _x('Portfolio Category', 'taxonomy singular name', 'portfolio-plugin'),
'search_items' => __('Search Portfolio Categories', 'portfolio-plugin'),
'popular_items' => __('Popular Portfolio Categories', 'portfolio-plugin'),
'all_items' => __('All Portfolio Categories', 'portfolio-plugin'),
'edit_item' => __('Edit Portfolio Category', 'portfolio-plugin'),
'update_item' => __('Update Portfolio Category', 'portfolio-plugin'),
'add_new_item' => __('Add New Portfolio Category', 'portfolio-plugin'),
'new_item_name' => __('New Portfolio Category Name', 'portfolio-plugin'),
'separate_items_with_commas' => __('Separate categories with commas', 'portfolio-plugin'),
'add_or_remove_items' => __('Add or remove categories', 'portfolio-plugin'),
'choose_from_most_used' => __('Choose from the most used categories', 'portfolio-plugin'),
'menu_name' => __('Categories', 'portfolio-plugin'),
);
$args = array(
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array('slug' => 'portfolio-category'),
'show_in_rest' => true,
);
register_taxonomy('portfolio-category', array('portfolio'), $args);
// Register tags taxonomy (similar implementation)
}
/**
* Add meta boxes for portfolio items
*/
public function add_meta_boxes() {
add_meta_box(
'portfolio_details',
__('Portfolio Details', 'portfolio-plugin'),
array($this, 'render_details_meta_box'),
'portfolio',
'normal',
'high'
);
add_meta_box(
'portfolio_gallery',
__('Portfolio Gallery', 'portfolio-plugin'),
array($this, 'render_gallery_meta_box'),
'portfolio',
'normal',
'high'
);
}
/**
* Render details meta box
*/
public function render_details_meta_box($post) {
// Add nonce for security
wp_nonce_field('portfolio_details_save', 'portfolio_details_nonce');
// Get saved values
$client = get_post_meta($post->ID, '_portfolio_client', true);
$date = get_post_meta($post->ID, '_portfolio_date', true);
$url = get_post_meta($post->ID, '_portfolio_url', true);
// Output fields
echo '<p>';
echo '<label for="portfolio_client">' . esc_html__('Client:', 'portfolio-plugin') . '</label> ';
echo '<input type="text" id="portfolio_client" name="portfolio_client" value="' . esc_attr($client) . '" class="widefat" />';
echo '</p>';
echo '<p>';
echo '<label for="portfolio_date">' . esc_html__('Project Date:', 'portfolio-plugin') . '</label> ';
echo '<input type="date" id="portfolio_date" name="portfolio_date" value="' . esc_attr($date) . '" class="widefat" />';
echo '</p>';
echo '<p>';
echo '<label for="portfolio_url">' . esc_html__('Project URL:', 'portfolio-plugin') . '</label> ';
echo '<input type="url" id="portfolio_url" name="portfolio_url" value="' . esc_attr($url) . '" class="widefat" />';
echo '</p>';
}
/**
* Save meta box data
*/
public function save_meta_boxes($post_id) {
// Security checks
if (!isset($_POST['portfolio_details_nonce']) ||
!wp_verify_nonce($_POST['portfolio_details_nonce'], 'portfolio_details_save')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
// Save meta values
if (isset($_POST['portfolio_client'])) {
update_post_meta($post_id, '_portfolio_client', sanitize_text_field($_POST['portfolio_client']));
}
if (isset($_POST['portfolio_date'])) {
update_post_meta($post_id, '_portfolio_date', sanitize_text_field($_POST['portfolio_date']));
}
if (isset($_POST['portfolio_url'])) {
update_post_meta($post_id, '_portfolio_url', esc_url_raw($_POST['portfolio_url']));
}
// Gallery saving code would go here
}
/**
* Portfolio shortcode implementation
*/
public function portfolio_shortcode($atts) {
$atts = shortcode_atts(array(
'category' => '',
'count' => 6,
'columns' => 3,
), $atts, 'portfolio');
// Query parameters
$args = array(
'post_type' => 'portfolio',
'posts_per_page' => intval($atts['count']),
);
// Add category filter if specified
if (!empty($atts['category'])) {
$args['tax_query'] = array(
array(
'taxonomy' => 'portfolio-category',
'field' => 'slug',
'terms' => explode(',', $atts['category']),
),
);
}
$query = new WP_Query($args);
// Start output buffer
ob_start();
if ($query->have_posts()) {
echo '<div class="portfolio-grid columns-' . esc_attr($atts['columns']) . '">';
while ($query->have_posts()) {
$query->the_post();
echo '<div class="portfolio-item">';
echo '<a href="' . esc_url(get_the_permalink()) . '">';
if (has_post_thumbnail()) {
echo get_the_post_thumbnail(null, 'portfolio-thumbnail');
}
echo '<h3>' . esc_html(get_the_title()) . '</h3>';
echo '</a>';
echo '</div>';
}
echo '</div>';
}
wp_reset_postdata();
return ob_get_clean();
}
// Other plugin methods would go here
}
// Initialize the plugin
function portfolio_plugin() {
return Portfolio_Plugin::get_instance();
}
portfolio_plugin();
/**
* Helper function to get portfolio client
*/
function portfolio_get_client($post_id = null) {
if (!$post_id) {
$post_id = get_the_ID();
}
return get_post_meta($post_id, '_portfolio_client', true);
}
/**
* Helper function to get portfolio date
*/
function portfolio_get_date($post_id = null) {
if (!$post_id) {
$post_id = get_the_ID();
}
return get_post_meta($post_id, '_portfolio_date', true);
}
/**
* Helper function to get portfolio URL
*/
function portfolio_get_url($post_id = null) {
if (!$post_id) {
$post_id = get_the_ID();
}
return get_post_meta($post_id, '_portfolio_url', true);
}
Implementation Exercise
As you work through implementation, tackle these key tasks:
- Complete the theme's single-portfolio.php template
- Implement the gallery meta box in the plugin
- Create the portfolio.js file for handling filtering
- Develop the plugin's admin settings page
- Add appropriate styles for both the frontend and admin interfaces
Implementation Tips:
- Work incrementally, testing each component before moving to the next
- Use version control (Git) to track changes and enable rollback if needed
- Comment your code thoroughly for future maintenance
- Use WordPress coding standards for consistent, maintainable code
- Keep security in mind, validating and sanitizing all inputs
Step 4: Review and Extend
After implementing your solution, it's time to review your work and consider extensions.
Testing Checklist
Thoroughly test your portfolio theme and plugin:
- Functionality Testing:
- Can you create portfolio items with all meta fields?
- Do portfolio categories and filters work correctly?
- Does the shortcode display correctly with all attributes?
- Is theme-plugin integration seamless?
- Compatibility Testing:
- Test with different WordPress versions
- Verify compatibility with popular plugins
- Ensure fallback behavior when plugin is deactivated
- Responsiveness Testing:
- Test on multiple device sizes
- Verify portfolio grid layout adapts appropriately
- Ensure images scale properly
- Performance Testing:
- Check page load times
- Optimize image loading
- Minimize script and style impact
Documentation
Create necessary documentation for your project:
- Theme README.md: Installation, configuration, and usage instructions
- Plugin README.md: Features, shortcodes, and theme integration guide
- Code Documentation: Inline comments and function documentation
- User Guide: Step-by-step instructions for creating and managing portfolio items
Potential Extensions
Consider ways to enhance your portfolio solution:
Reflection Questions
To fully embrace Polya's fourth step, reflect on your development process:
- How well did your solution address the original problem?
- What parts of the implementation were most challenging?
- What would you do differently if starting from scratch?
- Which parts of your solution are you most proud of?
- How could you apply this approach to other WordPress development problems?
Applying Polya's Method Beyond This Project
The 4-step problem-solving approach can be applied to all WordPress development challenges:
WordPress-Specific Applications
Apply Polya's method to different WordPress development scenarios:
| Development Scenario | Understanding Phase | Planning Phase | Implementation Phase | Review Phase |
|---|---|---|---|---|
| E-commerce Site | Product needs, customer journey, payment requirements | WooCommerce integration, theme compatibility, checkout flow | Product templates, payment gateways, shipping methods | Conversion rate analysis, checkout optimization |
| Member Portal | Access levels, content restrictions, user experience | Membership plugin selection, role management, content strategy | Registration process, restricted content, member dashboard | User engagement metrics, retention optimization |
| Content Migration | Content structure, metadata, relationships | Migration tools, data mapping, testing strategy | Data extraction, transformation, loading, verification | Content integrity check, performance impact analysis |
Final Project Submission
Your weekend project submission should include:
- Project Files:
- Complete theme folder
- Complete plugin folder
- SQL export of sample data (optional)
- Documentation:
- README files for both theme and plugin
- Implementation notes following Polya's 4 steps
- Screenshots of key features
- Reflection Paper:
- Brief description of your solution
- Challenges encountered and how you overcame them
- What you learned from applying Polya's method
- Future improvements you'd like to make
Submit all files as a compressed ZIP archive through the course submission portal by Sunday at 11:59 PM.
Resources and References
Estimated Time to Complete
Based on the project scope and complexity, here's an estimated timeline:
Day 1 (Saturday)
- Morning (3-4 hours)
- Understanding the problem and requirements (1 hour)
- Planning theme and plugin architecture (1 hour)
- Setting up local WordPress development environment (1 hour)
- Initial scaffolding of theme and plugin files (1 hour)
- Afternoon (4-5 hours)
- Developing core theme files (1.5 hours)
- Building portfolio archive template (1.5 hours)
- Creating single portfolio template (1 hour)
- Basic theme styling (1 hour)
- Evening (3-4 hours)
- Setting up plugin main class and structure (1 hour)
- Implementing custom post type and taxonomies (1 hour)
- Creating meta boxes for portfolio project details (1-2 hours)
Day 2 (Sunday)
- Morning (3-4 hours)
- Implementing portfolio gallery functionality (1.5 hours)
- Creating the plugin shortcode (1 hour)
- Developing admin settings page (1.5 hours)
- Afternoon (4-5 hours)
- Theme and plugin integration (1.5 hours)
- JavaScript for portfolio filtering (1.5 hours)
- Advanced styling and responsive design (2 hours)
- Evening (3-4 hours)
- Testing and debugging (1.5 hours)
- Documentation (1 hour)
- Final refinements and optimizations (1 hour)
- Project submission (0.5 hour)
Total Estimated Time: 20-26 hours
Experience level adjustments:
- Advanced students: 16-20 hours
- Intermediate students: 20-26 hours
- Beginner students: 30-35 hours (may need to simplify aspects)