webCOMAND

Standalone MVC Apps

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 Type Tutorial.  We will be able to preview changes to a staging environment optimized for debugging, and publish to a production environment optimized for speed and security.

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 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 Folder

First, create a new "Standalone MVC App" folder to organize and manage all of the application files.

  1. Navigate to the Root Folder (or your preferred parent folder).
  2. Click the New Menu (New Menu) in the Contents field and select "Folder" in the drop-down.
  3. Enter the Title "Standalone MVC App".
  4. Click Approve.

Content Settings

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

  1. Click the New Menu (New Menu) in the Contents field and select "Content Settings" in the drop-down.
  2. Enter the Label "Version Control"
  3. Select the "Standalone MVC App" Folder.
  4. Select "Content" Content Type.
  5. Select "8", or your preferred Maximum Number of Versions to keep.
  6. Click Approve and Back.

Folder Settings

In the future, a Staging and Production file system path can be defined in Folder Settings where File+ objects in this folder will write when the are saved and approved.  This will eliminate the need to create the the Publication List, Procedure, Publications and Publish Settings toward the end of this Tutorial.

Router

A critical 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 the New Menu (New Menu) in the Contents field and select "PHP File" in the drop-down.
  2. Enter the Filename "index.php".
  3. Enter the Text:
    <?php
    // enable the COMAND auto-loader
    require_once('/var/www/webcomand/comand.php');
    
    // require webCOMAND login to access the staging environment
    #IF($IsStaging)
    $framework = new \com_webcomand\framework();
    $framework->require_login();
    #ENDIF
    
    // 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']
    ]);
    
  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 these files outside of the Document Root.

  1. Click the New Menu (New Menu) in the Contents field and select "Text File" in the drop-down.
  2. Enter the Filename ".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 .* index.php [QSA,L]
  4. Click Approve.

Models

This app will work with a 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: http://www.comandmarket.com/packages/com_presidentsdemo_www/install/data.js

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, but that is covered in another 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 the New Menu (New Menu) in the Contents field and select "cMVC View" in the drop-down.
  2. Enter the Filename "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 "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 "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 in the public folder.

  1. Click the New Menu (New Menu) in the Contents field 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 the New Menu (New Menu) in the Contents field and select "cMVC Controller" in the drop-down.
  2. Enter the Filename "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.

Publication List

TODO

Publication Procedure

TODO

Publication

TODO

Publish Settings

TODO

Conclusion

The Presidents app uses the MVC framework and Publication features to make it easier to build, stage, debug and release large scale web applications from webCOMAND.

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