webCOMAND

Standalone MVC Apps Tutorial

In this tutorial we will build a "standalone" MVC App using the cMVC Framework to view and manage the presidents database created in the Content Types Tutorial.  We will be able to preview changes to a staging environment to test and review changes, and later publish to a production environment to release tested updates for public consumption.

The more basic MVC Apps Tutorial leverages the Package and Files features to automatically create the package folder and files in the file system, and the built-in webCOMAND package router to route requests to the package before the cMVC router takes over.  It is a simpler approach, but does not provide a staging and production environment or performance and security optimizations.

App Package

First, create a new "Standalone MVC App" Package to organize and manage application files.

  1. Launch the Content Manager (CMS) App.
  2. Click Add Content ("New Menu") in the Collection Bar and select "Folder" in the drop-down.
  3. Enter the Title "Packages".
  4. Click Approve.
  5. Click Add Content ("New Menu") in the Contents field and select "Package" in the drop-down.
  6. Enter the Title "Standalone MVC App".
  7. Under the Packages tab, enter the Namespace "com_presidentsdemo_www".
  8. Click Approve.
  9. Click back to the Contents tab.

Version Settings

Next, enable version control, so we can restore previous versions of our files if needed.

  1. Click Add Content ("New Menu") in the Contents field and select "Version Settings" in the drop-down.
  2. Enter Title: Version Control
  3. For Folders, check "Standalone MVC App" under "Packages".
  4. For Content Types, check "Content".
  5. For Max. Versions in History, select "8" or your preferred Maximum Number of Versions to keep.
  6. Click Approve and Back.

File Settings

Next, configure how various files should be written to the file system.

As an alternative to File Settings, Publications can be used to publish files to the file system instead.  Publications are more elaborate to set up, but offer additional flexibility.  To learn more, see the Publications Tutorials.

webCOMAND Hostnames

For this tutorial, you can host the staging and live versions of this app at the corresponding hostnames that came with your account:

  • live.<account>.webcomand.com
  • staging.<account>.webcomand.com

Custom Hostnames

As an alternative, you can use hostnames based on domains you control with the following steps.

  1. Add CNAME records that point to <account>.webcomand.com for the desired live and staging hostnames.  If you're not sure how to do that, see your DNS provider for instructions.
  2. For each hostname configured:
    • Click Add Content ("New Menu") in the Contents field and select "Web Route" in the drop-down.
    • For Hostname, enter the configured hostname.
    • Click Approve and expand logged events to see if the SSL certificate was updated properly.
    • Click Back

If you set up custom hostnames, use those instead of live.<account>.webcomand.com and staging.<account>.webcomand.com in the subsequent steps below.

File System Folder

With the live and staging hostnames set up, we can configure webCOMAND to write Files (content in the webCOMAND repository of the File Content Type, or a Content Type that extends the File Content Type) in our new package to folders in the server's file system that will contain the application files and web server Document Root (htdocs) to serve files for the hostnames.

  1. Click Add Content ("New Menu") in the Contents field and select "File Settings" in the drop-down.
  2. Enter Title: App Files
  3. For Folders, check "Standalone MVC App" under "Packages".
  4. For Content Types, check "File".
  5. Enter Approve Path: /publications/live.<account>.webcomand.com
  6. Enter Save Path: /publications/staging.<account>.webcomand.com
  7. Leave the Store Path blank, because we don't need to update files as we type.
    In some cases it is nice to have another environment for testing during development, before saving to staging.  In those cases, this field can be used to write/update files in your development environment, and as a bonus, no need to save before testing.
  8. Click Approve and Back.

For more information, see the File Settings Content Type.

Router

A key component in the 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. Click Add Content ("New Menu") in the Contents field and select "PHP File" in the drop-down.
  2. Enter the Filename "htdocs/router.php".
  3. Enter the Text:
    <?php
    namespace com_presidentsdemo_www;
    
    // enable the COMAND auto-loader
    require_once('/var/www/webcomand/comand.php');
    
    // use standard routing with a few tweaks:
    \io_comand_mvc\router::route([
        // define the base namespace for cMVC files to auto-load
        'namespace' => __NAMESPACE__,
        // where to find files in the namespace (relative to this router)
        'namespace_path' => '../cmvc/',
        // process just the URL path segments after the package name
        'request' => $_COMAND['REQUEST_URI'],
        // set default controller for when no controller is specified
        'default' => 'presidents',
    ]);
    
  4. Click Approve.

Web Server Configuration

In order for the router to receive all appropriate web requests, we need to configure the web server to execute the router PHP file for requests to a specific URL and its sub-paths.

Apache (.htaccess)

Apache web server can be configured with an .htaccess file like the following, which uses Rewrite Rules to direct all requests to a specific path to the router.  The router (index.php) must live within the Document Root, but the MVC application files it references may be located anywhere the web server has access to, so it is a best practice to locate those files outside of the Document Root.

  1. Click the New Menu ("New Menu") in the Contents field and select "Apache Config File" in the drop-down.
  2. Enter the Filename "htdocs/.htaccess".
  3. Enter the Text:
    # Direct all requests that are not for a real file or
    # folder in Document Root to the cMVC Router.
    # To support symbolic links, uncomment the next line.
    # RewriteCond %{REQUEST_FILENAME} !-l
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule .* router.php [QSA,L]
  4. Click Approve.

Models

This app will work with the Presidents database, which can be created by following the Content Types Tutorial, or the steps below.

  1. Click "Standalone MVC App" in the folder tree on the left.
  2. Click the Import button ("Import Button") in the Contents field.
  3. Click URL and enter:
    https://www.webcomand.com/packages/com_presidentsdemo_www/install/presidents.json

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 the Presidents content like we will from our views and controllers below.  More advanced models can be defined to extend and override the automatic model, which is covered in the COMAND Content Type Classes Topic.

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 the New Menu ("New Menu") in the Contents field and select "cMVC View" in the drop-down.
  2. Enter the Filename "cmvc/views/presidents.php".
  3. Enter the Text:
    <ol>
    <?php foreach($presidents as $p): ?>
        <li value="<?= $p->Number ?>">
            <a href="edit/<?= $p->OID ?>"><?= $p->Name ?></a>
        </li>
    <?php endforeach; ?>
    </ol>
  4. Click Approve and then Back.

president (edit view)

  1. Click the New Menu ("New Menu") in the Contents field and select "cMVC View" in the drop-down.
  2. Enter the Filename "cmvc/views/president.php".
  3. Enter the Text:
    <form action="update" 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 the New Menu ("New Menu") in the Contents field and select "cMVC View" in the drop-down.
  2. Enter the Filename "cmvc/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="../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 under the Document Root.

  1. Click the New Menu ("New Menu") in the Contents field and select "CSS File" in the drop-down.
  2. Enter the Filename "htdocs/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 the New Menu ("New Menu") in the Contents field and select "cMVC Controller" in the drop-down.
  2. Enter the Filename "cmvc/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], FALSE);
            $this->view('layout', [
                'base_url'=>$this->base_url,
                'body'=>$body,
                'msg'=>$msg]);
        }
    
        public function web__edit($oid) {
            $p = $this->repo()->get_first($oid);
            $foid = $p->Type->get_field('Photo')->OID;
            $img_url = "/com_webcomand/img/$oid/$foid/96x96/4";
            $body = $this->view('president', [
                'oid'=>$oid,
                'p'=>$p,
                'img_url'=>$img_url], FALSE);
            $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_first($oid);       
            if(!$p || $p->Type->Identifier != 'President') {
                $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 uses the cMVC framework and File Settings to make it easier to build, stage, test, debug and release large scale web applications from webCOMAND.