📌 Originally published on 4wp.dev → MVC Pattern in WordPress.
Long read on LinkedIn: MVC in WordPress: the pattern everyone learns, few truly apply.

Every WordPress developer has heard of MVC. Very few plugins actually follow it.

WordPress isn't an MVC framework — but its building blocks map cleanly onto Model · View · Controller once you know where to look.

👉 Interactive diagram with all examples: 4wp.dev/architectures/mvc

1. Controller — the traffic director

Receives the request, validates it, calls the Model, picks the View. No SQL. No HTML.

In WordPress, Controllers are:

  • hook callbacks (init, save_post)
  • REST API route handlers
  • wp_ajax_* handlers
// Controller — REST endpoint register_rest_route( 'myplugin/v1', '/products', [ 'methods' => 'GET', 'permission_callback' => fn() => current_user_can( 'read' ), 'callback' => function ( WP_REST_Request $req ) { $products = get_all_products( $req->get_param( 'category' ) ); // → Model return rest_ensure_response( $products ); }, ] ); 

➡️ More Controller examples (hooks, AJAX): 4wp.dev/architectures/mvc/#mvc-detail-controller

2. Model — the data layer

Owns data and business rules. Returns clean data. Never renders HTML.

In WordPress, Models are:

  • Custom Post Types, taxonomies, registered meta (schema)
  • $wpdb data-access functions/classes
  • Options API settings classes
// Model — the only layer that touches options class PluginSettings { private const KEY = 'myplugin_settings'; public static function get( string $key, $default = null ) { return ( get_option( self::KEY, [] ) )[ $key ] ?? $default; } } 

➡️ CPT schema and $wpdb examples: 4wp.dev/architectures/mvc/#mvc-detail-model

3. View — the presentation layer

Renders data it receives. No queries. No business logic.

In WordPress, Views are:

  • theme templates (single.php, archive.php)
  • shortcode output
  • Gutenberg block save() / render templates
<!-- View — escape, display, nothing else --> <article class="product"> <h1><?php echo esc_html( $product['title'] ); ?></h1> <p class="price"><?php echo esc_html( $product['price'] ); ?></p> </article> 

➡️ Shortcode and block examples: 4wp.dev/architectures/mvc/#mvc-detail-view

4. Real plugin: 4WP Weather

4WP Weather (GitHub) is built along the same lines:

Layer In the plugin
Controller REST route forwp-weather/v1/weather — the block calls it, PHP does the remote request
Model Provider (OpenWeatherMap), API key in Options API, transient cache
View Gutenberg block with Small / Compact / Advanced templates

Result: the API key never reaches the browser, cache lives in one place, and the block only renders.

Another example of the 4WP approach: 4WP SEO Helper (GitHub).

5. Quick checklist

  • [ ] Controllers are thin: validate → call Model → return/render
  • [ ] Only the Model touches $wpdb, options, meta
  • [ ] Views contain only escaping and markup
  • [ ] Nonces and capability checks live in the Controller

- [ ] Templates never call WP_Query directly

Go deeper

- 🔌 4WP plugins

Written by Anatoliy Dovgun · 4wp.dev — FSE WordPress development.
Follow: GitHub · X · DEV · Medium · Hashnode · YouTube