Ga naar inhoud

WordPress Plugins

Table of Contents


Advanced Custom Fields (ACF)

Advanced Custom Fields is a premium WordPress plugin that allows you to add custom fields to posts, pages, custom post types, and more.

Installation from Lemone Repository

At Lemone, we maintain ACF Pro in our own Codepot repository for version control and consistent deployments.

Steps:

  1. Go to the ACF repository and check the most recent version.

  2. Add this configuration to your composer.json:

{
  "repositories": [
    {
      "type": "package",
      "package": {
        "name": "paid-plugin/advanced-custom-fields-pro",
        "version": "6.3.3",
        "type": "wordpress-muplugin",
        "source": {
          "type": "git",
          "url": "git@codepot.nl:developers/plugins/paid/advanced-custom-fields.git",
          "reference": "tags/v6.3.3"
        }
      }
    }
  ],
  "require": {
    "paid-plugin/advanced-custom-fields-pro": "v6.3.3"
  }
}

Important: Make sure the version number matches the most recent version (or the specific version you need).

  1. Install with Composer:
composer install

ACF Best Practices

Field groups organization: - Group related fields together logically - Use descriptive field names (not just "content" or "text") - Set proper field keys for consistency across environments

Field naming convention:

// Good: descriptive and clear
$hero_title = get_field('hero_title');
$cta_button_text = get_field('cta_button_text');

// Bad: vague and unclear
$content = get_field('content');
$text = get_field('text');

Location rules: - Be specific with location rules to avoid clutter - Use post types and page templates appropriately - Consider user role restrictions for admin-only fields

Common ACF Usage Patterns

Getting field values:

<?php
// Simple field
$title = get_field('title');

// With default fallback
$subtitle = get_field('subtitle') ?: 'Default Subtitle';

// Image field (returns array)
$image = get_field('hero_image');
if ($image) {
    echo wp_get_attachment_image($image['id'], 'large');
}

// Repeater field
if (have_rows('team_members')) {
    while (have_rows('team_members')) {
        the_row();
        $name = get_sub_field('name');
        $role = get_sub_field('role');
        echo "<p>{$name} - {$role}</p>";
    }
}
?>

Flexible content:

<?php
if (have_rows('content_blocks')) {
    while (have_rows('content_blocks')) {
        the_row();

        switch (get_row_layout()) {
            case 'text_block':
                get_template_part('blocks/text', null, [
                    'content' => get_sub_field('content')
                ]);
                break;

            case 'image_block':
                get_template_part('blocks/image', null, [
                    'image' => get_sub_field('image')
                ]);
                break;

            case 'cta_block':
                get_template_part('blocks/cta', null, [
                    'title' => get_sub_field('title'),
                    'button' => get_sub_field('button')
                ]);
                break;
        }
    }
}
?>

Options page:

<?php
// Get option from ACF Options page
$site_phone = get_field('phone_number', 'option');
$footer_text = get_field('footer_copyright', 'option');
?>

ACF JSON Sync

Enable JSON sync for version control and team collaboration.

In your theme's functions.php:

// Save ACF field groups to theme
add_filter('acf/settings/save_json', function($path) {
    return get_stylesheet_directory() . '/acf-json';
});

// Load ACF field groups from theme
add_filter('acf/settings/load_json', function($paths) {
    unset($paths[0]);
    $paths[] = get_stylesheet_directory() . '/acf-json';
    return $paths;
});

Create the directory:

mkdir -p your-theme/acf-json

Workflow: 1. Create/edit field groups in WordPress admin 2. Field groups are automatically saved to acf-json/ folder 3. Commit the JSON files to git 4. On other environments, ACF will detect and sync changes


Lemone MU-Plugin

The Lemone MU-plugin is our custom must-use plugin that provides shared functionality across all Lemone WordPress projects.

Architecture

The plugin uses a Service Provider pattern for organizing functionality into modular services.

Directory structure:

lemone-mu-plugin/
├── lemone-mu-plugin.php (main file)
├── src/
│   ├── ServiceProvider.php (abstract base class)
│   ├── Services/
│   │   ├── ACF.php
│   │   ├── Assets.php
│   │   ├── Authentication.php
│   │   ├── Cleanup.php
│   │   ├── CustomPostTypes.php
│   │   ├── DisableEditor.php
│   │   ├── DisablePlugins.php
│   │   ├── EmailSMTP.php
│   │   ├── ErrorLogging.php
│   │   ├── Media.php
│   │   ├── Menus.php
│   │   ├── SEO.php
│   │   ├── Security.php
│   │   ├── SVG.php
│   │   └── Theme.php
│   └── Plugin.php (main plugin class)

Service Provider Pattern

Each service extends the ServiceProvider abstract class:

<?php
namespace Lemone\Services;

use Lemone\ServiceProvider;

class YourService extends ServiceProvider
{
    /**
     * Register the service
     * Called when service is instantiated
     */
    public function register(): void
    {
        // Register hooks, filters, etc.
        add_action('init', [$this, 'init']);
        add_filter('some_filter', [$this, 'filterCallback']);
    }

    /**
     * Initialize the service
     */
    public function init(): void
    {
        // Initialization logic
    }

    /**
     * Example filter callback
     */
    public function filterCallback($value)
    {
        return $value;
    }
}

Registering Services

Services are registered in Plugin.php:

<?php
namespace Lemone;

class Plugin
{
    /**
     * Required services (always loaded)
     */
    protected array $services = [
        Services\Security::class,
        Services\Cleanup::class,
        Services\Theme::class,
    ];

    /**
     * Optional services (can be disabled by themes)
     */
    protected array $optionalServices = [
        Services\ACF::class,
        Services\EmailSMTP::class,
        Services\Media::class,
        Services\SVG::class,
    ];

    /**
     * Boot the plugin
     */
    public function boot(): void
    {
        $this->registerServices();
        $this->registerOptionalServices();
    }

    /**
     * Register required services
     */
    protected function registerServices(): void
    {
        foreach ($this->services as $service) {
            (new $service())->register();
        }
    }

    /**
     * Register optional services
     */
    protected function registerOptionalServices(): void
    {
        foreach ($this->optionalServices as $service) {
            if ($this->isServiceEnabled($service)) {
                (new $service())->register();
            }
        }
    }

    /**
     * Check if service is enabled
     */
    protected function isServiceEnabled(string $service): bool
    {
        $className = class_basename($service);
        return apply_filters("lemone/service/{$className}/enabled", true);
    }
}

Disabling Optional Services

Themes can disable optional services using filters in functions.php:

<?php
// Disable specific optional service
add_filter('lemone/service/EmailSMTP/enabled', '__return_false');

// Disable multiple services
add_filter('lemone/service/ACF/enabled', '__return_false');
add_filter('lemone/service/Media/enabled', '__return_false');

Common Services

Security Service (Required): - Removes WordPress version from head - Disables XML-RPC - Adds security headers - Removes unnecessary meta tags

Cleanup Service (Required): - Removes emoji scripts - Cleans up <head> section - Disables RSS feeds (if not needed) - Removes unnecessary WordPress features

Theme Service (Required): - Registers theme support features - Manages image sizes - Handles theme customizations

ACF Service (Optional): - Sets up ACF options pages - Configures ACF settings - Registers ACF blocks

EmailSMTP Service (Optional): - Configures SMTP for transactional emails - Integrates with Postmark or similar services

Media Service (Optional): - Handles media library customizations - Image optimization settings - Custom media handling

SVG Service (Optional): - Enables SVG uploads - Sanitizes SVG files for security - Adds SVG mime type support

Creating a New Service

  1. Create new file in src/Services/YourService.php:
<?php
namespace Lemone\Services;

use Lemone\ServiceProvider;

class YourService extends ServiceProvider
{
    /**
     * Register the service
     */
    public function register(): void
    {
        add_action('init', [$this, 'init']);
    }

    /**
     * Initialize
     */
    public function init(): void
    {
        // Your logic here
    }
}
  1. Add to Plugin.php:
// For required service:
protected array $services = [
    // ...
    Services\YourService::class,
];

// For optional service:
protected array $optionalServices = [
    // ...
    Services\YourService::class,
];

Service Examples

Custom Post Type Service:

<?php
namespace Lemone\Services;

use Lemone\ServiceProvider;

class CustomPostTypes extends ServiceProvider
{
    public function register(): void
    {
        add_action('init', [$this, 'registerPostTypes']);
    }

    public function registerPostTypes(): void
    {
        register_post_type('project', [
            'labels' => [
                'name' => 'Projects',
                'singular_name' => 'Project',
            ],
            'public' => true,
            'has_archive' => true,
            'menu_icon' => 'dashicons-portfolio',
            'supports' => ['title', 'editor', 'thumbnail'],
        ]);
    }
}

Disable Editor Service:

<?php
namespace Lemone\Services;

use Lemone\ServiceProvider;

class DisableEditor extends ServiceProvider
{
    protected array $disabledTemplates = [
        'template-homepage.php',
        'template-contact.php',
    ];

    public function register(): void
    {
        add_action('init', [$this, 'disableEditorForTemplates']);
    }

    public function disableEditorForTemplates(): void
    {
        $pageTemplate = get_page_template_slug();

        if (in_array($pageTemplate, $this->disabledTemplates)) {
            remove_post_type_support('page', 'editor');
        }
    }
}

Benefits of Service Provider Pattern

  1. Modularity: Each feature is self-contained
  2. Reusability: Services can be easily reused across projects
  3. Maintainability: Easy to locate and modify functionality
  4. Testability: Services can be unit tested independently
  5. Flexibility: Optional services can be enabled/disabled per theme
  6. Organization: Clear structure for team collaboration

See Also