一、Bookmarker案例
本教程将引导您完成一个简单的书签应用程序(Bookmarker)的创建。 首先,我们将安装CakePHP,创建我们的数据库,并使用CakePHP提供的工具快速完成我们的应用程序。
1.数据库服务器。
我们将在本教程中使用MySQL服务器, 您将需要足够了解SQL以创建数据库:CakePHP将从那里接管,因为我们使用MySQL,还要确保在PHP中启用了pdo_mysql 。
2.基本PHP知识。
php -v
你至少应该有安装PHP 5.6.0(CLI)或更高。 你的网络服务器的PHP版本必须是5.6.0或更高,应该 相同版本您的命令行接口(CLI)是 PHP版本,如果你想看到完整的应用程序CakePHP /书签。让我们开始吧。
二、获取CakePHP
安装CakePHP的最简单的方法是使用Composer。 Composer是从终端或命令行提示符安装CakePHP的简单方法。 首先,您需要下载并安装Composer,如果您还没有这样做的话。 如果你安装了curl,它就像运行下面一样简单:
curl -s https://getcomposer.org/installer | php
默认windows下是没有curl这个命令,所以你要下载curl.exe(可以从这下载),并把路径配置到path环境变量中,然后才能执行该命令。
或者,您可以从Composer网站下载composer.phar
。
然后只需在安装目录中在终端中键入以下行,即可在bookmarker目录中安装CakePHP应用程序框架:
php composer.phar create-project --prefer-dist cakephp/app bookmarker
报错:Could not open input file: composer.phar
把这三个文件(从这下载),放到目录下,就可以了
如果您下载并运行了Composer Windows Installer ,则从安装目录(即C:\ wamp \ www \ dev \ cakephp3)在终端中键入以下行:
php composer self - update && composer create - project - prefer - dist cakephp / app bookmarker
使用Composer的优点是它将自动完成一些重要的设置任务,如设置正确的文件权限和为您创建config / app.php文件。
还有其他方法来安装CakePHP。 如果您不能或不想使用Composer,请查看安装部分。
无论下载和安装CakePHP的方式如何,一旦设置完成,您的目录设置应如下所示:
/bookmarker
/bin
/config
/logs
/plugins
/src
/tests
/tmp
/vendor
/webroot
.editorconfig
.gitignore
.htaccess
.travis.yml
composer.json
index.php
phpunit.xml.dist
README.md
检查我们的安装
我们可以通过检查默认主页快速检查我们的安装是否正确。 在您可以这么做之前,您需要启动开发服务器:
bin/cake server
▲ 注意: 对于Windows,该命令需要是bin\cake server (注意反斜杠)。
这将在端口8765上启动PHP的内置Web服务器。在Web浏览器中打开http://localhost:8765以查看欢迎页面。
所有的项目符号都应该是CakePHP之外的其他能够连接到你的数据库的复选标记。 如果没有,您可能需要安装其他PHP扩展,或设置目录权限。
三、创建数据库
接下来,让我们为我们的书签应用程序设置数据库。 如果您还没有这样做,请创建一个空数据库供您在本教程中使用,并使用您选择的名称,例如cake_bookmarks
。 您可以执行以下SQL来创建必要的表:
▲ 注意: 这里我用的是本地安装好的数据库,你也可以把项目复制到集成好的环境中去。所以这里不用启动Apache服务。
您可以执行以下SQL来创建必要的表:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
created DATETIME,
modified DATETIME
);
CREATE TABLE bookmarks (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(50),
description TEXT,
url TEXT,
created DATETIME,
modified DATETIME,
FOREIGN KEY user_key (user_id) REFERENCES users(id)
);
CREATE TABLE tags (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
created DATETIME,
modified DATETIME,
UNIQUE KEY (title)
);
CREATE TABLE bookmarks_tags (
bookmark_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (bookmark_id, tag_id),
FOREIGN KEY tag_key(tag_id) REFERENCES tags(id),
FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id)
);
执行完后
您可能已经注意到bookmarks_tags
表使用复合主键。 CakePHP几乎支持复合主键,使得更容易构建多租户应用程序。
我们使用的表和列名称不是任意的。 通过使用CakePHP的命名约定 ,我们可以更好地利用CakePHP,并避免配置框架。CakePHP是足够灵活的,以适应甚至不一致的遗留数据库模式,但遵守约定将节省您的时间。
接下来,让我们告诉CakePHP我们的数据库在哪里,以及如何连接到它。 对许多人来说,这将是第一次和最后一次你需要配置任何东西。
配置应该非常简单:只需将config/app.php文件中的Datasources.default数组中的值替换为适用于您的设置的值。 示例完成的配置数组可能如下所示:
return [
// More configuration above.
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'cakephp',
'password' => 'AngelF00dC4k3~',
'database' => 'cake_bookmarks',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
],
],
// More configuration below.
];
一旦你保存了config / app.php文件,你应该看到'CakePHP能够连接到数据库'部分有一个复选标记。
▲ 注意: CakePHP的默认配置文件的副本可以在config/app.default.php中找到。
五、生成脚本代码
因为我们的数据库遵循CakePHP约定,我们可以使用控制台应用程序快速生成基本应用程序。 在命令行中运行以下命令:
// On Windows you'll need to use bin\cake instead.
bin/cake bake all users
bin/cake bake all bookmarks
bin/cake bake all tags
windows下执行:
bin\cake bake all users
bin\cake bake all bookmarks
bin\cake bake all tags
http://localhost:8765/users
http://localhost:8765/bookmarks
http://localhost:8765/tags
▲ 注意: 如果看到“未找到”(404)页面,请确认已加载Apache mod_rewrite模块。
六、添加密码散列
当您创建用户时(通过访问http://localhost:8765/users),您可能注意到密码以纯文本格式存储。
从安全的角度来看,这是很糟糕的,所以让我们来解决这个问题。
这也是讨论CakePHP中的模型层的好时机。 在CakePHP中,我们将操作对象集合的方法和单个对象分成不同的类。 对实体集合进行操作的方法放在Table类中,而属于单个记录的要素放在Entity类上。
例如,对单个记录进行密码散列,因此我们将在实体对象上实现此行为。 因为,我们要在每次设置密码时哈希密码,我们将使用mutator/setter方法。 CakePHP将在任何时候在您的一个实体中设置属性时调用基于约定的setter方法。 让我们为密码添加一个setter。 在src / Model / Entity / User.php中添加以下内容:
namespace App\Model\Entity;
use Cake\Auth\DefaultPasswordHasher; //include this line
use Cake\ORM\Entity;
class User extends Entity
{
// Code from bake.
protected function _setPassword($value)
{
$hasher = new DefaultPasswordHasher();
return $hasher->hash($value);
}
}
现在更新您之前创建的其中一个用户,如果更改其密码,您应该会在列表或查看页面上看到一个散列的密码,而不是原始值。 CakePHP默认使用bcrypt密码。
如果使用现有的数据库,也可以使用sha1或md5。
▲ 注意: 如果密码没有得到哈希,请确保您为该类的密码成员遵循相同的情况,同时命名setter函数
七、使用特定标记获取Bookmarks
现在我们安全地存储密码,我们可以在我们的应用程序中建立一些更有趣的功能。 一旦您收集了一系列书签,就可以通过标签进行搜索。 接下来,我们将实现一个路由,控制器动作和finder方法来通过标签搜索书签。
理想情况下,我们会有一个URL为http://localhost:8765/bookmarks/tagged/funny/cat/gifs 。 这将让我们找到所有带有“funny”,“cat”或“gifs”标签的书签。 在我们实现之前,我们将添加一条新路线。 你的config/routes.php应该看起来像:
<?php
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\Router;
Router::defaultRouteClass(DashedRoute::class);
// New route we're adding for our tagged action.
// The trailing `*` tells CakePHP that this action has
// passed parameters.
Router::scope(
'/bookmarker',
['controller' => 'Bookmarks'],
function ($routes) {
$routes->connect('/tagged/*', ['action' => 'tags']);
}
);
Router::scope('/', function ($routes) {
// Connect the default home and /pages/* routes.
$routes->connect('/', [
'controller' => 'Pages',
'action' => 'display', 'home'
]);
$routes->connect('/pages/*', [
'controller' => 'Pages',
'action' => 'display'
]);
// Connect the conventions based default routes.
$routes->fallbacks();
});
上面定义了一个新的“路由”,它将/bookmarks/tagged/ 连接到BookmarksController::tags() 。 通过定义路由,您可以区分网址的外观,以及它们的实现方式。 如果我们访问http://localhost:8765/bookmarker/tagged ,我们会看到一个来自CakePHP的有用的错误页面,通知您控制器操作不存在。
让我们现在实现那个缺少的方法。 在src / Controller/BookmarksController.php中添加以下内容:
public function tags()
{
// The 'pass' key is provided by CakePHP and contains all
// the passed URL path segments in the request.
$tags = $this->request->getParam('pass');
// Use the BookmarksTable to find tagged bookmarks.
$bookmarks = $this->Bookmarks->find('tagged', [
'tags' => $tags
]);
// Pass variables into the view template context.
$this->set([
'bookmarks' => $bookmarks,
'tags' => $tags
]);
}
八、创建Finder方法
在CakePHP中,我们喜欢保持我们的控制器行动苗条,并将我们应用程序的大部分逻辑放在模型中。 如果你现在访问/bookmarks/tagged URL,你会看到一个错误, findTagged()方法还没有实现,所以让我们这样做。 在src / Model/Table/BookmarksTable.php中添加以下内容:
// The $query argument is a query builder instance.
// The $options array will contain the 'tags' option we passed
// to find('tagged') in our controller action.
public function findTagged(Query $query, array $options)
{
$bookmarks = $this->find()
->select(['id', 'url', 'title', 'description']);
if (empty($options['tags'])) {
$bookmarks->leftJoinWith('Tags', function ($q) {
return $q->where(['Tags.title IS ' => null]);
});
} else {
$bookmarks->innerJoinWith('Tags', function ($q) use ($options) {
return $q->where(['Tags.title IN' => $options['tags']]);
});
}
return $bookmarks->group(['Bookmarks.id']);
}
我们只是实现了一个自定义搜索方法。这是一个非常强大的概念 CakePHP允许你包起来再使用的查询。查找方法总是查询生成器对象和选择数组作为参数。使得人可以操纵的查询和添加任何要求的条件或标准。当完成时,查找方法必须返回 修改查询对象。在我们的查找中,我们利用了distinct()和matching()方法,它们允许我们找到具有“匹配”标签的不同书签。 matching()方法接受一个接收查询构建器作为其参数的匿名函数 。 在回调中,我们使用查询构建器来定义将过滤具有特定标记的书签的条件
九、创建视图
现在,如果您访问/bookmarker/tagged URL,CakePHP将显示一个错误,让您知道您没有创建视图文件。 接下来,让我们为tags()动作构建视图文件。 在src/Template/bookmarker/tags.ctp中放入以下内容:
<h1>
Bookmarks tagged with
<?= $this->Text->toList(h($tags)) ?>
</h1>
<section>
<?php foreach ($bookmarks as $bookmark): ?>
<article>
<!-- Use the HtmlHelper to create a link -->
<h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4>
<small><?= h($bookmark->url) ?></small>
<!-- Use the TextHelper to format text -->
<?= $this->Text->autoParagraph(h($bookmark->description)) ?>
</article>
<?php endforeach; ?>
</section>
在上面的代码中,我们使用Html和Text帮助器来帮助生成我们的视图输出。 我们还使用h
快捷函数来对输出进行HTML编码。你应该记住在输出用户数据时总是使用h()
来防止HTML注入问题。
我们刚刚创建的tags.ctp文件遵循视图模板文件的CakePHP约定。 约定是使模板使用控制器操作名称的小写和下划线版本。
您可能会注意到,我们可以在我们的视图中使用$tags
和$bookmarks
变量。 当我们在我们的控制器中使用set()
方法时,我们设置要发送到视图的特定变量。 该视图将使所有传递的变量在模板中作为局部变量可用。
您现在应该可以访问http://localhost:8765/bookmarker/tagged/funny网址,并查看所有标有“funny”的书签。
到目前为止,我们已经创建了一个基本的应用程序来管理书签,标签和用户。 但是,每个人都可以看到其他人的标签。 在下一章中,我们将实现身份验证,并将可见书签限制为仅属于当前用户的书签。