webCOMAND

MVC Apps Tutorial

In this tutorial we will build on the Presidents Demo Package created in the Packages Tutorial, but build the web application using the cMVC Framework to view and manage the presidents database created in the Content Types TutorialModel-view-controller (MVC) is a good way to organize web applications to make its components easier to understand, reuse and maintain.

Router

A key component in an MVC framework is the router.  The router receives all web requests to the application and routes them to a controller based on the requested URL.  The COMAND MVC router makes this easy with defaults suitable for most applications.  Since the router brokers all web requests, we define the router in index.php directly under the package folder.  When an index.php file is directly under the package folder, it will be executed for all requests that do not correspond to a file in the public subfolder.

  1. Launch the Content Manager (CMS) App.
  2. In the folder on the left, expand and click: Bases / Presidents / "Presidents Demo".
  3. Click Add Content (New Menu) in the Collection Bar and select "cMVC Router" in the drop-down.
  4. Enter the Filename "index.php".
  5. Enter the Text:
    <?php
    namespace com_presidentsdemo_www;
    
    // use standard routing with a couple tweaks:
    \io_comand_mvc\router::route([
        // set default controller for when no controller is specified
        'default'=>'presidents',
        // process just the URL path segments after the package name
        'request'=>$_COMAND['REQUEST_URI']
    ]);
    
  6. Click Approve and then Back.
For more information, see cMVC Router.

Models

A model is automatically available when a new content type is created, so there is no need to create one to query, access and edit our Presidents content like we will from our views and controllers below.  More advanced models can be defined to extend and override the automatic model, but that will be covered in a more advanced tutorial.

Views

Views are PHP files that describe how to display the app interface, components and content, typically in HTML.  Standard PHP tags and short tags are used to define the view logic and insert variables passed to the view from a controller.

Our app will have 3 views:

  • presidents - to display a list of presidents
  • president - to display a single president in a form for editing
  • layout - to wrap the views above in a common web page "wrapper"

presidents (list view)

  1. Click Add Content (New Menu) in the Collection Bar and select "cMVC View" in the drop-down.
  2. Enter the Filename "views/presidents.php".
  3. Enter the Text:
    <ol>
    <?php foreach($presidents as $p): ?>
        <li value="<?= $p->Number ?>">
            <a href="presidents/edit/<?= $p->OID ?>"><?= $p->Name ?></a>
        </li>
    <?php endforeach; ?>
    </ol>
  4. Click Approve and then Back.

president (edit view)

  1. Click Add Content (New Menu) in the Collection Bar and select "cMVC View" in the drop-down.
  2. Enter the Filename "views/president.php".
  3. Enter the Text:
    <form action="presidentsupdate" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="oid" value="<?= $oid ?>" />
    
        <label for="number">Number</label>
        <input type="number" name="number" value="<?= $p->Number ?>" />
    
        <label for="name">Name</label>
        <input type="text" name="name" value="<?= $p->Name ?>" />
    
        <label for="photo">Photo</label>
        <img src="<?= $img_url ?>" />
        <input type="file" name="photo" />
    
        <input type="submit" name="op" value="Update" />
        <input type="submit" name="op" value="Cancel" />
    </form>
  4. Click Approve and then Back.

layout (common wrapper)

  1. Click Add Content (New Menu) in the Collection Bar and select "cMVC View" in the drop-down.
  2. Enter the Filename "views/layout.php".
  3. Enter the Text:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>Presidents MVC App</title>
        <base href="<?= $base_url ?>" />
        <link type="text/css" rel="stylesheet" href="<?= $base_url ?>css/web.css" />
    </head>
    <body>
    <h1>Presidents</h1>
    <?php if( $msg ): ?>
        <div class="msg"><?= $msg ?></div>
    <?php endif; ?>
    <?= $body ?>
    </body>
    </html>
  4. Click Approve and then Back.

web.css

The layout view references an external CSS file, so we can create that as a simple static file in the public folder.

  1. Click Add Content (New Menu) in the Collection Bar and select "CSS File" in the drop-down.
  2. Enter the Filename "public/css/web.css".
  3. Enter the Text:
    .msg {border: 1px solid #888; padding: 4px; background-color: #eee;}
    label, input, img {display: block; margin: 4px 0;}
    input {margin-bottom: 16px; padding: 4px;}
    input[type="submit"] {display: inline;}
  4. Click Approve and then Back.

Controllers

Controllers are PHP files that respond to web requests.  A controller's public methods with names that begins with "web__" will be called automatically based on the URL of the web request.  We will add a single controller with 3 "web__" methods:

  • web__index() - Called for requests that don't target a specific method.  Ours will list all presidents.
  • web__edit() - Display a specific president in a form to edit that president's field values.
  • web__update() - Update a president's field values based on the edit form submission.

presidents (controller)

  1. Click Add Content (New Menu) in the Collection Bar and select "cMVC Controller" in the drop-down.
  2. Enter the Filename "controllers/presidents.php".
  3. Enter the Text:
    <?php
    namespace com_presidentsdemo_www\controllers;
    
    class presidents extends \io_comand_mvc\controller {
    
        public function web__index($msg = '') {
            $p = $this->repo()->get("FROM President ORDER BY Number");
            $body = $this->view('presidents', ['presidents'=>$p], TRUE);
            $this->view('layout', [
                'base_url'=>$this->base_url,
                'body'=>$body,
                'msg'=>$msg
            ]);
        }
    
        public function web__edit($oid) {
            $p = $this->repo()->get_object($oid, 'President');
            if(!$p) {
                $this->web__index('Invalid President OID');
            }
            $foid = $p->Type->get_field('Photo')->OID;
    
            // image URL API for 96x96 photo with a 4x4 background grid
            $img_url = "/com_webcomand/img/$oid/$foid/96x96/4";
    
            $body = $this->view('president', [
                'oid'=>$oid,
                'p'=>$p,
                'img_url'=>$img_url
            ], TRUE);
            $this->view('layout', [
                'base_url'=>$this->base_url,
                'body'=>$body
            ]);
        }
    
        public function web__update() {
            if($this->request->post('op') == 'Cancel') {
                $this->web__index();
            }
            $oid = $this->request->post('oid');
            $p = $this->repo()->get_object($oid, 'President');       
            if(!$p) {
                $this->web__index('Invalid President OID');
            }
            $p->Name = $this->request->post('name');
            $p->Number = $this->request->post('number');
            $photo = $this->request->file('photo');
            if($photo) {
                $p->Photo = $photo;
            }
            try {
                $p->approve();
            } catch(Exception $e) {
                $this->web__index('Update error: ' . $e->getMessage());
            }
            $this->web__index('Updated President ' . $p->Name);
        }
    }
  4. Click Approve and then Back.

Conclusion

The Presidents app now uses the MVC framework to make it easier to extend, update and reuse.

https://<account>.webcomand.com/com_presidentsdemo_www/

This tutorial leverages Package and Files features to automatically create the package folder and files in the file system.  The built-in webCOMAND package router is also used to route requests to package before the cMVC router takes over.  As an alternative, it is possible to create or publish the package folder and files anywhere and configure a web server to route requests directly to the cMVC router.  This is slightly more efficient because it bypasses the webCOMAND package router, and can be configured to write/publish to development, staging and live environments.  To see how that works, see the Standalone MVC Apps Tutorial.