用户不再希望将他们的内容(照片、音乐和文档)限制在一个桌面或笔记本计算机中。他们希望可从任何地方访问它,从其移动设备和办公桌面都能进行访问。为了满足这个要求,涌现出了各种各样的云存储服务,每种服务都提供了存储和同步工具,以便用户能随时随地访问他们的数据。
通过 developerWorks Premium 会员计划在云中快速投入生产。在它的许多特别待遇中,您会获得 Bluemix 的 12 个月订阅,以及用于使用 Bluemix 服务(比如 Watson、IoT 和移动)开发应用程序的 240 美元贷款。了解 developerWorks Premium 会员的所有权益。
如果您是一位正考虑构建这样一个服务的开发人员,那么现在是开始行动的好时机。云基础架构已变得更加经济和开发人员友好,而且没有损失任何稳定性和可伸缩性。此外,构建原生移动和 Web 应用程序的工具能够广泛可用,这意味着开发、测试和部署新的移动友好的应用程序的任务比过去简单得多。
在本教程中,我讲介绍构建一个简单的笔记本应用程序的过程,该应用程序允许用户使用移动或桌面 Web 浏览器在云中存储和搜索自由形式文本笔记。我还将介绍如何在 IBM Bluemix® 云平台上部署和运行该应用程序。
示例笔记本应用程序允许用户使用无限多个文本笔记,还允许运行他们编辑、搜索和删除笔记。此外,用户可以对笔记进行颜色编码,以方便分类或识别。
在客户端上,我将使用 Bootstrap 为应用程序创建一个移动友好的用户界面。在服务器上,我将使用 Slim (一个 PHP 微型框架)管理应用程序流,连接 MongoDB 并从中检索数据。
要完成本文中的步骤,您需要:
“ 这个笔记本应用程序使用 MongoDB 提供快速的、可扩展的文档存储,使用 Slim PHP 微型框架提供业务逻辑,使用 Bootstrap 提供一个响应式、移动友好的用户界面。 ”
在 GitHub 上获取代码
{ "require": { "slim/slim": "2.*" } }
shell> php composer.phar install
/index
、 /view
、 /save
和 /delete
,如下所示。将此脚本保存为 <$APP_ROOT> /index.php。 <?php // use Composer autoloader require 'vendor/autoload.php'; require 'config.php'; // configure Slim application instance // initialize application $app = new /Slim/Slim(array( 'debug' => true, 'templates.path' => './views' )); $app->config = $config; // index page handlers $app->get('/', function () use ($app) { $app->redirect($app->urlFor('index')); }); // handler to list available notes in database // if query string included // filter results to match query string $app->get('/index', function () use ($app) { // code here })->name('index'); // handler to display add/edit form $app->get('/save(/:id)', function ($id = null) use ($app) { // code here }); // handler to process form input // save note content to database $app->post('/save', function () use ($app) { // code here }); // handler to delete specified note $app->get('/delete/:id', function ($id) use ($app) { // code here }); // handler to display specified note $app->get('/view/:id', function ($id) use ($app) { // code here }); // hook to add request URI path as template variable $app->hook('slim.before.dispatch', function() use ($app) { $app->view()->appendData(array( 'baseUri' => $app->request()->getRootUri() )); }); $app->run();
'slim.before.dispatch'
hook,它获取当前请求 URL(包含任何子目录路径),并提供该请求作为一个名为 $baseUri
的模板变量。这最大化了可移植性,因为它允许您将应用程序移动到 Web 服务器上的不同目录,无需重写您的视图中的 URL 路径。您可以在源代码存储库中的各种模板中看到这种可移植性的实际运用。 点击查看代码清单
关闭 [x]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Cloud Notepad</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <div class="panel panel-default"> <div class="panel-heading clearfix"> <h4 class="pull-left">Notes</h4> </div> </div> <!-- page content here --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> </body> </html>
所有部分准备就绪后,现在可以开始构建应用程序了。
基本上讲,一个笔记包含三个属性: 'title'
、 'body'
和 'color'
。这些值由用户提供。每个笔记还包含两个额外的属性:一个唯一的 'id'
和一个 'updated'
属性,前者在 MongoDB 集合中标识该笔记,后者存储最后修改该笔记的时间。
构建一个表单来与这些属性进行匹配非常容易。以下是该表单的外观:
点击查看代码清单
关闭 [x]
<form method="post" action="<?php echo $this->data['baseUri']; ?>/save"> <input name="id" type="hidden" value="<?php echo $this->data['note']['_id']; ?>" /> <div class="form-group"> <label for="title">Title</label> <input type="title" class="form-control" id="title" name="title" placeholder="Title" value="<?php echo htmlspecialchars($this->data['note']['title']); ?>"> </div> <div class="form-group"> <label for="color">Color</label> <input type="color" class="form-control" id="color" name="color" placeholder="Color" value="<?php echo$this->data['note']['color']; ?>"> </div> <div class="form-group"> <label for="body">Content</label> <textarea name="body" id="body" class="form-control" rows="3"><?php echo htmlspecialchars($this->data['note']['body']); ?></textarea> </div> <div class="form-group"> <button type="submit" class="btn btn-default">Save</button> </div> </form>
请注意,该表单使用了新的 HTML5 'color'
输入类型,该类型会自动生成一个调色板或颜色滑块,允许用户从一系列颜色中为每个笔记选择颜色。选择的颜色会作为十六进制值返回。
要减少重复,可以重用这个表单来编辑现有笔记。这就是该表单包含一个隐藏的 'id'
字段的原因,对于新笔记,该字段将保留为空。系统可以利用此标识符的存在与否来确定是在数据库中创建新笔记还是更新现有笔记。
这是使用来自 <$APP_ROOT> /config.php 文件的信息初始化数据库连接的代码段:
<?php // attach configuration to application $app->config = $config; // extract database name from URI // initialize PHP Mongo client $dbn = substr(parse_url($app->config['db_uri'], PHP_URL_PATH), 1); $mongo = new MongoClient($app->config['db_uri'], array("connectTimeoutMS" => 30000)); $db = $mongo->selectDb($dbn);
在将该表单提交到 /save
端点后,表单处理器必须首先验证和整理 POST 输入,然后将其保存到数据库中。这是该过程的回调函数:
<?php // handler to process form input // save note content to database $app->post('/save', function () use ($app, $db) { $collection = $db->notes; $id = trim(strip_tags($app->request->post('id'))); $note = new stdClass; $note->title = trim(strip_tags($app->request->post('title'))); $note->body = trim(strip_tags($app->request->post('body'))); $note->color = trim(strip_tags($app->request->post('color'))); $note->updated = time(); if (!empty($id)) { $note->_id = new MongoId($id); } $collection->save($note); $app->redirect($app->urlFor('index'));
上面的代码整理了 POST 输入并将 'updated'
属性的值设置为当前时间。根据 POST 输入是否包含标识符,它为笔记创建一个新的 MongoDB 文档,或使用该标识符使用修改后的内容更新现有文档。
这是添加一个新笔记的示例:
能够添加和更新笔记只是部分功能,您还需要能够列出和搜索笔记。列出它们很简单:只需更新对 /index
路线的回调,允许使用 MongoDB 客户端的 find()
方法检索集合中的所有文档,将它们交给视图并按更新日期从新到旧进行排序。
<?php // handler to list available notes in database $app->get('/index', function () use ($app, $db) { $collection = $db->notes; $notes = $collection->find()->sort(array('updated' => -1)); $app->render('index.tpl.php', array('notes' => $notes)); })->name('index');
这是输出的示例:
如果您有大量笔记,全部列出它们有些不太实际。在理想情况下,您还想采用某种方法在笔记内容中搜索一个或多个关键词,以便可以快速找到要寻找的信息。
<div class="panel panel-default"> <form method="get" action="<?php echo $this->data['baseUri']; ?>/index"> <div class="input-group"> <input type="text" name="q" class="form-control" placeholder="Search for..."> <span class="input-group-btn"> <button type="submit" class="btn btn-default">Go!</button> </span> </div> </form> </div>
'title'
或 'body'
属性包含与搜索词汇匹配的值的文档。这是修改后的代码: <?php // handler to list available notes in database // if query string included // filter results to match query string $app->get('/index', function () use ($app, $db) { $collection = $db->notes; $q = trim(strip_tags($app->request->get('q'))); $where = array(); if (!empty($q)) { $where = array( '$or' => array( array( 'title' => array('$regex' => new MongoRegex("/$q/i"))), array( 'body' => array('$regex' => new MongoRegex("/$q/i"))) ) ); } $notes = $collection->find($where)->sort(array('updated' => -1)); $app->render('index.tpl.php', array('notes' => $notes)); })->name('index');
如上述代码所示,当对 /index
路线的请求包含一个查询字符串时,该处理程序将生成一个额外条件,该条件要求仅返回其标题或正文中包含该搜索词汇(表示为 PHP MongoRegex 对象)的笔记。这个额外的条件在 $where
变量中表示,它作为一个额外传输传递给 find()
方法。像之前一样将结果数据传递给该视图并显示它们。
这是它的一个实际应用示例:
从上一幅图中可以注意到,列表中的每个笔记都包含一个 View 按钮。此按钮超链接到 /view
路线,并包含相应笔记的文档 ID 作为一个请求参数。 /view
回调处理程序仅需使用 MongoDB 客户端的 findOne()
方法从数据库获取指定的笔记并显示它,如下面的代码中所示:
<?php // handler to display specified note $app->get('/view/:id', function ($id) use ($app, $db) { $collection = $db->notes; $note = $collection->findOne(array('_id' => new MongoId($id))); $app->render('view.tpl.php', array('note' => $note)); });
这是输出的示例:
类似地, /delete
处理程序收到一个文档 ID 作为请求参数,使用 MongoDB 客户端的 remove()
方法从数据库中删除相应的笔记。
<?php // handler to delete specified note $app->get('/delete/:id', function ($id) use ($app, $db) { $collection = $db->notes; $collection->remove(array('_id' => new MongoId($id))); $app->redirect($app->urlFor('index')); });
--- applications: - name: notes-[initials] memory: 256M instances: 1 host: notes-[initials] buildpack: https://github.com/cloudfoundry/php-buildpack.git stack: cflinuxfs2
{ "WEB_SERVER": "httpd", "PHP_EXTENSIONS": ["bz2", "zlib", "curl", "mcrypt", "mongo"] }
shell> cf api https://api.ng.bluemix.net shell> cf login shell> cf push
构建受云基础架构和存储支持的桌面或移动 Web 应用程序从来没像现在这么轻松过。通过将 Bluemix PaaS 基础架构与 MongoDB、PHP、Slim 框架和 Bootstrap 相结合,您就拥有一组完整的工具来快速高效地创建、部署和扩展您自己的基于云的应用程序。