教科书般的PHP框架学习指南php
注:翻译水平有限,若有错误,欢迎指正css
If you’re looking for a tour through all the ingredients for setting up a very simple Slim application (this one doesn’t use Twig, but does use Monolog and a PDO database connection) then you’re in the right place. Either walk through the tutorial to build the example application, or adapt each step for your own needs.html
若是您正在寻找创建一个很是简单的Slim应用程序所需的要素(这个应用程序不使用Twig,可是使用了Mongolog和PDO数据库链接),那么您来对地方了。您能够跟着本教程来构建示例应用程序,也能够根据本身的须要调整每一个步骤。前端
Before you start: There is also a skeleton project which will give you a quick-start for a sample application, so use that if you’d rather just have something working rather than exploring how all the moving parts work.mysql
在开始以前:还有一个骨架项目,它将为示例应用程序提供一个快速启动,所以,若是您只想让某些东西工做,而不想探究全部移动部件是如何工做的,那么可使用它。nginx
Start by making a folder for your project (mine is called project, because naming things is hard). I like to reserve the top level for things-that-are-not-code and then have a folder for source code, and a folder inside that which is my webroot, so my initial structure looks like this:git
首先为您的项目建立一个文件夹(个人文件夹名为project,由于命名很困难)。我喜欢把非代码的东西放在顶层,而后一个文件夹用来放源码,另外一个文件夹用来作webroot,因此个人初始结构是这样的:web
.
├── project
│ └── src
│ └── public
复制代码
Composer is the best way to install Slim Framework. If you don’t have it already, you can follow the installation instructions, in my project I’ve just downloaded the composer.phar into my src/ directory and I’ll use it locally. So my first command looks like this (I’m in the src/ directory):sql
Composer是安装Slim框架的最佳方法。若是你尚未,你能够按照说明安装,在个人项目中,我刚刚下载了composer.phar到个人src/目录中,我将在本地使用它。因此个人第一个命令是这样的(我在src/目录中):数据库
php composer.phar require slim/slim
复制代码
This does two things:
Add the Slim Framework dependency to composer.json (in my case it creates the file for me as I don’t already have one, it’s safe to run this if you do already have a composer.json file)
向composer.json添加slim依赖(在个人例子中,它为我建立了一个文件,由于我尚未一个,若是您已经有一个composer.json,那么运行这个文件也是安全的)
Run composer install so that those dependencies are actually available to use in your application
运行composer install,以便这些依赖项实际上能够在应用程序中使用
If you look inside the project directory now, you’ll see that you have a vendor/ folder with all the library code in it. There are also two new files: composer.json and composer.lock. This would be a great time to get our source control setup correct as well: when working with composer, we always exclude the vendor/ directory, but both composer.json and composer.lock should be included under source control. Since I’m using composer.phar in this directory I’m going to include it in my repo as well; you could equally install the composer command on all the systems that need it.
若是您如今查看项目目录,您将看到您有一个包含全部库代码的vendor/文件夹。还有两个新文件:composer.json和composer.lock。这将是纠正源代码控制设置的好时机:当使用composer时,咱们老是排除vendor/目录,但把composer.json和composer.lock放在源代码控制之下。由于我用的composer.phar在当前这个目录中,我也将把它包含在个人代码库中;您一样也能够在全部须要composer命令的系统上全局安装composer命令并使用它。
To set up the git ignore correctly, create a file called src/.gitignore and add the following single line to the file:
要正确设置git ignore,请建立一个名为src/.gitignore的文件。并在文件中添加如下单行:
vendor/*
复制代码
Now git won’t prompt you to add the files in vendor/ to the repository - we don’t want to do this because we’re letting composer manage these dependencies rather than including them in our source control repository.
如今git不会提示您将vendor/中的文件添加到存储库中——咱们不想这样作,由于咱们让composer管理这些依赖项,而不是将它们包含在咱们的源代码控制存储库中。
There’s a really excellent and minimal example of an index.php for Slim Framework on the project homepage so we’ll use that as our starting point. Put the following code into src/public/index.php:
在项目主页上有一个很好的例子,它是index.php for Slim框架的一个很是小的例子,因此咱们将使用它做为咱们的起点。将如下代码放入src/public/index.php:
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../../vendor/autoload.php';
$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
$name = $args['name'];
$response->getBody()->write("Hello, $name");
return $response;
});
$app->run();
复制代码
We just pasted a load of code … let’s take a look at what it does.
咱们刚刚粘贴了一大堆代码…让咱们看看它是作什么的。
The use statements at the top of the script are just bringing the Request and Response classes into our script so we don’t have to refer to them by their long-winded names. Slim framework supports PSR-7 which is the PHP standard for HTTP messaging, so you’ll notice as you build your application that the Request and Response objects are something you see often. This is a modern and excellent approach to writing web applications.
脚本顶部的use语句只是将请求和响应类引入到脚本中,所以咱们没必要经过它们冗长的名称引用它们。Slim框架支持PSR-7,这是HTTP消息传递的PHP标准,所以在构建应用程序时,您将注意到请求和响应对象是您常常看到的对象。这是编写web应用程序的一种现代而优秀的方法。
Next we include the vendor/autoload.php file - this is created by Composer and allows us to refer to the Slim and other related dependencies we installed earlier. Look out that if you’re using the same file structure as me then the vendor/ directory is one level up from your index.php and you may need to adjust the path as I did above.
接下来,咱们将包含vendor/autoload.php文件——该文件由Composer建立,容许咱们引用前面安装的Slim和其余相关依赖项。注意,若是您使用的是与我相同的文件结构,那么vendor/目录比index.php高一级,您可能须要像我上面所作的那样调整路径。
Finally we create the app->get() call is our first “route” - when we make a GET request to /hello/someone then this is the code that will respond to it. Don’t forget you need that final $app->run() line to tell Slim that we’re done configuring and it’s time to get on with the main event.
最后,咱们建立app->get()调用是咱们的第一个“路由”——当咱们向/hello/somename发出get请求时,这是响应它的代码。不要忘记,您须要最后的$app->run()行来告诉Slim咱们已经完成了配置,如今是继续处理主事件的时候了。
Now we have an application, we’ll need to run it. I’ll cover two options: the built-in PHP webserver, and an Apache virtual host setup.
如今咱们有了一个应用程序,咱们须要运行它。我将介绍两个选项:内置的PHP web服务器和Apache虚拟主机设置。
This is my preferred “quick start” option because it doesn’t rely on anything else! From the src/public directory run the command:
这是我首选的“快速启动”选项,由于它不依赖于任何其余东西!从src/public目录运行命令:
php -S localhost:8080
复制代码
This will make your application available at http://localhost:8080 (if you’re already using port 8080 on your machine, you’ll get a warning. Just pick a different port number, PHP doesn’t care what you bind it to).
能够经过http://localhost:8080访问你的slim应用(若是您的计算机上已经使用端口8080,您将获得一个警告。只需选择一个不一样的端口号,PHP并不关心具体的端口号是什么)。
To get this set up on a standard LAMP stack, we’ll need a couple of extra ingredients: some virtual host configuration, and one rewrite rule.
要在标准LAMP堆栈上设置此功能,咱们须要一些额外的组件:一些虚拟主机配置和一个重写规则。
The vhost configuration should be fairly straightforward; we don’t need anything special here. Copy your existing default vhost configuration and set the ServerName to be how you want to refer to your project. For example you can set:
vhost配置应该至关简单;咱们这里不须要什么特别的东西。复制现有的默认vhost配置,并将ServerName设置为您但愿引用项目的方式。例如,你能够设置:
ServerName slimproject.test
or for nginx:
server_name slimproject.test;
复制代码
Then you’ll also want to set the DocumentRoot to point to the public/ directory of your project, something like this (edit the existing line):
而后,您还须要将DocumentRoot设置为指向项目的公共/目录,以下所示(编辑现有行):
DocumentRoot /home/lorna/projects/slim/project/src/public/
or for nginx:
root /home/lorna/projects/slim/project/src/public/
复制代码
Don’t forget to restart your server process now you’ve changed the configuration! I also have a .htaccess file in my src/public directory; this relies on Apache’s rewrite module being enabled and simply makes all web requests go to index.php so that Slim can then handle all the routing for us. Here’s my .htaccess file:
不要忘记从新启动您的服务器进程,如今您已经更改了配置!
个人src/public目录中也有一个.htaccess文件;这依赖于启用Apache的重写模块,并简单地将全部web请求转到index.php,这样Slim就能够为咱们处理全部路由。这是个人.htaccess文件:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
复制代码
nginx does not use .htaccess files, so you will need to add the following to your server configuration in the location block:
nginx不使用.htaccess文件,因此您须要在location块中向服务器配置添加如下内容:
if (!-e $request_filename){
rewrite ^(.*)$ /index.php break;
}
复制代码
NOTE: If you want your entry point to be something other than index.php you will need your config to change as well. api.php is also commonly used as an entry point, so your set up should match accordingly. This example assumes you are using index.php.
注意:若是但愿入口点不是index.php,还须要修改配置,api.php也一般用做入口点,所以您的设置应该相应地匹配。本例假设您正在使用index.php。
With this setup, just remember to use slimproject.test instead of http://localhost:8080 in the other examples in this tutorial. The same health warning as above applies: you’ll see an error page at slimproject.test but crucially it’s Slim’s error page. If you go to slimproject.test/hello/joebl… then something better should happen.
使用此设置,只需记住使用http://slimproject。在本教程的其余示例中,测试而不是http://localhost:8080。上面的健康警告一样适用:您将在http://slimproject中看到一个错误页面。但关键是这是Slim的错误页面。若是你访问http://slimproject.test/hello/joebloggs而后应该会发生更好的事情。
Now we’ve set up the platform, we can start getting everything we need in place in the application itself.
如今咱们已经创建了平台,咱们能够开始在应用程序中得到所需的一切。
The initial example uses all the Slim defaults, but we can easily add configuration to our application when we create it. There are a few options but here I’ve just created an array of config options and then told Slim to take its settings from here when I create it. First the configuration itself:
如今咱们已经创建了平台,咱们能够开始在应用程序自己中得到所需的一切。
向应用程序添加配置设置
最初的示例使用了全部Slim缺省值,可是咱们能够在建立应用程序时轻松地将配置添加到应用程序中。但这里我只是建立了一个配置选项数组,而后告诉Slim在我建立它时从这里获取设置。
首先配置自己:
$config['displayErrorDetails'] = true;
$config['addContentLengthHeader'] = false;
$config['db']['host'] = 'localhost';
$config['db']['user'] = 'user';
$config['db']['pass'] = 'password';
$config['db']['dbname'] = 'exampleapp';
复制代码
These string should be added into src/public/index.php before the $app = new \Slim\App line.
这些字符串应该在$app = new \Slim\ app行以前添加到src/public/index.php中。
The first line is the most important! Turn this on in development mode to get information about errors (without it, Slim will at least log errors so if you’re using the built in PHP webserver then you’ll see them in the console output which is helpful). The second line allows the web server to set the Content-Length header which makes Slim behave more predictably.
第一行是最重要的!在开发模式中打开此选项,以获取有关错误的信息(若是没有它,Slim至少会记录错误,所以若是您正在使用内置的PHP webserver,那么您将在控制台输出中看到这些错误,这颇有帮助)。第二行容许web服务器设置Content-Length报头,这使得Slim的行为更加可预测。
The other settings here are not specific keys/values, they’re just some data that I want to be able to access later.
Now to feed this into Slim, we need to change where we create the Slim/App object so that it now looks like this:
这里的其余设置不是特定的键/值,它们只是我但愿之后可以访问的一些数据。
如今,要把这个输入Slim,咱们须要改变要建立的Slim/App对象,使它看起来像这样:
$app = new \Slim\App(['settings' => $config]);
复制代码
We’ll be able to access any settings we put into that $config array from our application later on.
稍后,咱们将可以从应用程序中访问放入$config数组中的任何设置。
Composer can handle the autoloading of your own classes just as well as the vendored ones. For an in-depth guide, take a look at using Composer to manage autoloading rules.
Composer能够像处理vendored类同样处理您本身类的自动加载。要得到深刻的指导,请查看如何使用Composer管理自动加载规则。
My setup is pretty simple since I only have a few extra classes, they’re just in the global namespace, and the files are in the src/classes/ directory. So to autoload them, I add this autoload section to my composer.json file:
个人设置很是简单,由于我只有几个额外的类,它们只是在全局名称空间中,而文件在src/classes/目录中。所以,为了自动加载它们,我将这个自动加载部分添加到composer.json文件:
{
"require": {
"slim/slim": "^3.1",
"slim/php-view": "^2.0",
"monolog/monolog": "^1.17",
"robmorgan/phinx": "^0.5.1"
},
"autoload": {
"psr-4": {
"": "classes/"
}
}
}
复制代码
Most applications will have some dependencies, and Slim handles them nicely using a DIC (Dependency Injection Container) built on Pimple. This example will use both Monolog and a PDO connection to MySQL.
大多数应用程序都有一些依赖关系,Slim使用构建在Pimple上的DIC(依赖注入容器)很好地处理这些依赖关系。本例将使用monolog和MySQL的PDO链接。
The idea of the dependency injection container is that you configure the container to be able to load the dependencies that your application needs, when it needs them. Once the DIC has created/assembled the dependencies, it stores them and can supply them again later if needed.
依赖项注入容器的思想是,将容器配置为可以在应用程序须要依赖项时加载它们。一旦DIC建立/组装了依赖项,它就会存储依赖项,并在之后须要时再次提供依赖项。
To get the container, we can add the following after the line where we create $app and before we start to register the routes in our application:
要得到容器,咱们能够在建立$app的行后面,注册路由的前面添加如下内容:
$container = $app->getContainer();
复制代码
Now we have the Slim\Container object, we can add our services to it.
如今咱们有了Slim\Container对象,能够将服务添加到其中。
If you’re not already familiar with Monolog, it’s an excellent logging framework for PHP applications, which is why I’m going to use it here. First of all, get the Monolog library installed via Composer:
若是您还不熟悉Monolog,它是一个很是好的PHP应用程序日志框架,这就是为何我要在这里使用它。首先,经过Composer安装monolog库:
php composer.phar require monolog/monolog
复制代码
The dependency is named logger and the code to add it looks like this:
依赖项命名为logger,添加它的代码以下:
$container['logger'] = function($c) {
$logger = new \Monolog\Logger('my_logger');
$file_handler = new \Monolog\Handler\StreamHandler('../logs/app.log');
$logger->pushHandler($file_handler);
return $logger;
};
复制代码
We’re adding an element to the container, which is itself an anonymous function (the $c that is passed in is the container itself so you can access other dependencies if you need to). This will be called when we try to access this dependency for the first time; the code here does the setup of the dependency. Next time we try to access the same dependency, the same object that was created the first time will be used the next time.
咱们正在向容器添加一个元素,它自己是一个匿名函数(传入的$c是容器自己,所以若是须要,您能够访问其余依赖项)。当咱们第一次尝试访问这个依赖项时,它将被调用;这里的代码负责设置依赖项。下次咱们尝试访问相同的依赖项时,将使用第一次建立的相同对象。
My Monolog config here is fairly light; just setting up the application to log all errors to a file called logs/app.log (remember this path is from the point of view of where the script is running, i.e. index.php).
这里的Monolog配置至关简单;只需设置应用程序将全部错误记录到一个名为logs/app.log的文件中(请记住,该路径是从脚本运行的角度来看的,即index.php)。
With the logger in place, I can use it from inside my route code with a line like this:
有了logger,我能够在个人路由代码内这样使用它:
$this->logger->addInfo('Something interesting happened');
复制代码
Having good application logging is a really important foundation for any application so I’d always recommend putting something like this in place. This allows you to add as much or as little debugging as you want, and by using the appropriate log levels with each message, you can have as much or as little detail as is appropriate for what you’re doing in any one moment.
对于任何应用程序来讲,良好的应用程序日志记录都是很是重要的基础,因此我老是建议在适当的地方使用相似的日志记录。这容许您根据须要添加尽量多或尽量少的调试,而且经过对每一个消息使用适当的日志级别,您能够在任什么时候刻得到尽量多或尽量少的细节。
There are many database libraries available for PHP, but this example uses PDO - this is available in PHP as standard so it’s probably useful in every project, or you can use your own libraries by adapting the examples below.
有许多数据库库可供PHP使用,可是本例使用PDO—这在PHP中是标准的,因此它可能在每一个项目中都颇有用,或者您能够经过调整下面的示例来使用本身的库。
Exactly as we did for adding Monolog to the DIC(dependency injection container), we’ll add an anonymous function that sets up the dependency, in this case called db:
正如咱们在向DIC(依赖注入容器)添加Monolog时所作的那样,咱们将添加一个匿名函数来设置依赖关系,在本例中称为db:
$container['db'] = function ($c) {
$db = $c['settings']['db'];
$pdo = new PDO('mysql:host=' . $db['host'] . ';dbname=' . $db['dbname'],
$db['user'], $db['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $pdo;
};
复制代码
Remember the config that we added into our app earlier? Well, this is where we use it - the container knows how to access our settings, and so we can grab our configuration very easily from here. With the config, we create the PDO object (remember this will throw a PDOException if it fails and you might like to handle that here) so that we can connect to the database. I’ve included two setAttribute() calls that really aren’t necessary but I find these two settings make PDO itself much more usable as a library so I left the settings in this example so you can use them too! Finally, we return our connection object.
还记得咱们以前添加到应用程序中的配置吗?这就是咱们使用它的地方——容器知道如何访问咱们的设置,所以咱们能够很容易地从这里获取配置。经过配置,咱们建立了PDO对象(请记住,若是它失败,将抛出一个PDOException,您可能但愿在这里处理它),以便咱们能够链接到数据库。我已经包含了两个setAttribute()调用,这其实是没必要要的,但我发现这两个设置使PDO自己做为一个库更有用,因此我在本例中保留了这些设置,以便您也可使用它们!最后,返回connection对象。
Again, we can access our dependencies with just $this-> and then the name of the dependency we want which in this case is $this->db, so there is code in my application that looks something like:
一样,咱们能够用$this->方式访问咱们的依赖,后面跟上咱们想要的依赖名称,在本例中是$this->db,因此个人应用程序中的代码看起来像:
$mapper = new TicketMapper($this->db);
复制代码
This will fetch the db dependency from the DIC, creating it if necessary, and in this example just allows me to pass the PDO object straight into my mapper class.
这将从DIC获取db依赖项,在必要时建立它,在本例中只容许我将PDO对象直接传递到映射器类中。
“Routes” are the URL patterns that we’ll describe and attach functionality to. Slim doesn’t use any automatic mapping or URL formulae so you can make any route pattern you like map onto any function you like, it’s very flexible. Routes can be linked to a particular HTTP verb (such as GET or POST), or more than one verb.
“路由”是咱们将描述并附加功能的URL模式。Slim没有使用任何自动映射或URL公式,因此您能够将任何您喜欢的路由模式映射到任何您喜欢的函数上,这是很是灵活的。路由能够连接到特定的HTTP动做(如GET或POST),也能够连接到多个动做。
As a first example, here’s the code for making a GET request to /tickets which lists the tickets in my bug tracker example application. It just spits out the variables since we haven’t added any views to our application yet:
做为第一个示例,下面是向/tickets发出GET请求的代码,它列出了个人bug跟踪器示例应用程序中的罚单。它只是吐出变量,由于咱们尚未添加任何视图到咱们的应用程序:
$app->get('/tickets', function (Request $request, Response $response) {
$this->logger->addInfo("Ticket list");
$mapper = new TicketMapper($this->db);
$tickets = $mapper->getTickets();
$response->getBody()->write(var_export($tickets, true));
return $response;
});
复制代码
The use of $app->get() here means that this route is only available for GET requests; there’s an equivalent $app->post() call that also takes the route pattern and a callback for POST requests. There are also methods for other verbs - and also the map() function for situations where more than one verb should use the same code for a particular route.
这里使用$app->get()意味着此路由仅对get请求可用;还有一个等效的$app->post()调用,它也接受路由模式和post请求的回调。还有用于其余谓词的方法,以及map()函数,用于多个谓词对特定路由使用相同代码的状况。
Slim routes match in the order they are declared, so if you have a route which could overlap another route, you need to put the most specific one first. Slim will throw an exception if there’s a problem, for example in this application I have both /ticket/new and /ticket/{id} and they need to be declared in that order otherwise the routing will think that “new” is an ID!
Slim路由按照声明的顺序匹配,所以,若是您有一个可能与另外一个路由重叠的路由,那么您须要首先放置最具体的一个。若是有问题,Slim会抛出异常,例如在这个应用程序中,我同时拥有/ticket/new和/ticket/{id},它们须要按这个顺序声明,不然路由会认为“new”是一个id !
In this example application, all the routes are in index.php but in practice this can make for a rather long and unwieldy file! It’s fine to refactor your application to put routes into a different file or files, or just register a set of routes with callbacks that are actually declared elsewhere.
在这个示例应用程序中,全部的路由都在index.php中,但实际上这可能致使文件很长且很笨重!能够重构应用程序,将路由放入一个或多个不一样的文件中,或者只注册一组路由,而将其中的回调部分放到其余地方。
All route callbacks accept three parameters (the third one is optional):
全部路由回调都接受三个参数(第三个是可选的):
This emphasis on Request and Response illustrates Slim 3 being based on the PSR-7 standard for HTTP Messaging. Using the Request and Response object also makes the application more testable as we don’t need to make actual requests and responses, we can just set up the objects as desired.
这种对请求和响应的强调说明了Slim 3基于HTTP消息传递的PSR-7标准。使用请求和响应对象还可使应用程序更具可测试性,由于咱们不须要进行实际的请求和响应,咱们能够根据须要设置对象。
Sometimes, our URLs have variables in them that we want to use in our application. In my bug tracking example, I want to have URLs like /ticket/42 to refer to the ticket - and Slim has an easy way of parsing out the “42” bit and making it available for easy use in the code. Here’s the route that does exactly that:
有时,url中有一些变量,咱们但愿在应用程序中使用这些变量。在个人bug跟踪示例中,我但愿有像/ticket/42这样的url来引用票据——Slim有一种简单的方法来解析“42”位并使其在代码中易于使用。下面就是具体的方法:
$app->get('/ticket/{id}', function (Request $request, Response $response, $args) {
$ticket_id = (int)$args['id'];
$mapper = new TicketMapper($this->db);
$ticket = $mapper->getTicketById($ticket_id);
$response->getBody()->write(var_export($ticket, true));
return $response;
});
复制代码
Look at where the route itself is defined: we write it as /ticket/{id}. When we do this, the route will take the portion of the URL from where the {id} is declared, and it becomes available as $args['id'] inside the callback.
查看路由自己的定义位置:咱们将它写为/ticket/{id}。当咱们这样作时,该路由将从声明{id}的URL中提取部分,并在回调函数中以$args['id']的形式被使用。
Since GET and POST send data in such different ways, then the way that we get that data from the Request object differs hugely in Slim.
因为GET和POST以如此不一样的方式发送数据,因此咱们从请求对象获取数据的方式在Slim中有很大的不一样。
It is possible to get all the query parameters from a request by doing $request->getQueryParams() which will return an associative array. So for the URL /tickets?sort=date&order=desc we’d get an associative array like:
经过执行$request->getQueryParams()能够从请求中得到全部查询参数,该方法将返回一个关联数组。URL /tickes?sort=date&order=desc咱们会获得一个关联数组,以下所示:
['sort' => 'date', 'order' => 'desc']
复制代码
These can then be used (after validating of course) inside your callback.
而后能够在回调中使用它们(固然是在验证以后)。
When working with incoming data, we can find this in the body. We’ve already seen how we can parse data from the URL and how to obtain the GET variables by doing $request->getQueryParams() but what about POST data? The POST request data can be found in the body of the request, and Slim has some good built in helpers to make it easier to get the information in a useful format.
当处理传入数据时,咱们能够在主体中找到这些数据。咱们已经了解了如何解析URL中的数据,以及如何经过执行$request->getQueryParams()得到GET变量,可是POST数据呢?POST请求数据能够在请求体中找到,Slim内置一些很好的帮助程序,能够更容易地得到格式化后的有用信息。
For data that comes from a web form, Slim will turn that into an array. My tickets example application has a form for creating new tickets that just sends two fields: “title” and “description”. Here is the first part of the route that receives that data, note that for a POST route use app->get():
对于来自web表单的数据,Slim将其转换为数组。个人tickets示例应用程序有一个用于建立新罚单的表单,它只发送两个字段:“title”和“description”。这里是接收数据的路由的第一部分,注意对于POST路由使用app->get():
$app->post('/ticket/new', function (Request $request, Response $response) {
$data = $request->getParsedBody();
$ticket_data = [];
$ticket_data['title'] = filter_var($data['title'], FILTER_SANITIZE_STRING);
$ticket_data['description'] = filter_var($data['description'], FILTER_SANITIZE_STRING);
// ...
复制代码
The call to $request->getParsedBody() asks Slim to look at the request and the Content-Type headers of that request, then do something smart and useful with the body. In this example it’s just a form post and so the resulting $data array looks very similar to what we’d expect from $_POST - and we can go ahead and use the filter extension to check the value is acceptable before we use it. A huge advantage of using the built in Slim methods is that we can test things by injecting different request objects - if we were to use $_POST directly, we aren’t able to do that.
对$request->getParsedBody()的调用要求Slim查看请求和该请求的内容类型头,而后对该请求的正文作一些智能的操做。在本例中,它只是一个表单post,所以生成的$data数组与咱们指望的$_POST很是类似——在使用它以前,咱们能够继续使用filter扩展来检查值是否能够接受。使用内置Slim方法的一个巨大优点是,咱们能够经过注入不一样的请求对象来测试—若是直接使用$_POST,咱们就不能这样作。
What’s really neat here is that if you’re building an API or writing AJAX endpoints, for example, it’s super easy to work with data formats that arrive by POST but which aren’t a web form. As long as the Content-Type header is set correctly, Slim will parse a JSON payload into an array and you can access it exactly the same way: by using $request->getParsedBody().
这里真正简洁的地方是,若是您正在构建API或编写AJAX站点,那么处理经过POST到达但不是web表单的数据格式就很是容易。只要正确设置了Content-Type头部,Slim就会将JSON有效负载解析为一个数组,您可使用彻底相同的方法访问它:使用$request->getParsedBody()。
Slim doesn’t have an opinion on the views that you should use, although there are some options that are ready to plug in. Your best choices are either Twig or plain old PHP. Both options have pros and cons: if you’re already familiar with Twig then it offers lots of excellent features and functionality such as layouts - but if you’re not already using Twig, it can be a large learning curve overhead to add to a microframework project. If you’re looking for something dirt simple then the PHP views might be for you! I picked PHP for this example project, but if you’re familiar with Twig then feel free to use that; the basics are mostly the same.
Slim对您应该使用的视图没有任何意见,不过有一些选项能够插入。您最好的选择是Twig或普通的老式PHP。这两种选择都有优缺点:若是您已经熟悉Twig,那么它提供了许多优秀的特性和功能,好比布局——可是若是您尚未使用Twig,那么将其添加到微框架项目中可能须要花费大量的学习时间。若是您正在寻找一些很是简单的东西,那么PHP视图可能适合您!我选择PHP做为这个示例项目,可是若是您熟悉Twig,那么能够随意使用它;基本是同样的。
Since we’ll be using the PHP views, we’ll need to add this dependency to our project via Composer. The command looks like this (similar to the ones you’ve already seen):
因为咱们将使用PHP视图,所以须要经过Composer将此依赖项添加到项目中。命令看起来是这样的(相似于您已经看到的命令):
php composer.phar require slim/php-view
复制代码
In order to be able to render the view, we’ll first need to create a view and make it available to our application; we do that by adding it to the DIC. The code we need goes with the other DIC additions near the top of src/public/index.php and it looks like this:
为了可以呈现视图,咱们首先须要建立一个视图并使它对咱们的应用程序可用;咱们把它加到DIC中。咱们须要的代码与src/public/index.php顶部的其余DIC附加代码一块儿使用,它看起来是这样的:
$container['view'] = new \Slim\Views\PhpRenderer('../templates/');
复制代码
Now we have a view element in the DIC, and by default it will look for its templates in the src/templates/ directory. We can use it to render templates in our actions - here’s the ticket list route again, this time including the call to pass data into the template and render it:
如今DIC中有一个view元素,默认状况下,它将在src/templates/目录中查找它的模板。在咱们的控制器中咱们使用它来渲染模板,这一次包括调用传递数据到模板和渲染它:
$app->get('/tickets', function (Request $request, Response $response) {
$this->logger->addInfo('Ticket list');
$mapper = new TicketMapper($this->db);
$tickets = $mapper->getTickets();
$response = $this->view->render($response, 'tickets.phtml', ['tickets' => $tickets]);
return $response;
});
复制代码
The only new part here is the penultimate line where we set the $response variable. Now that the view is in the DIC, we can refer to it as $this->view. Calling render() needs us to supply three arguments: the $response to use, the template file (inside the default templates directory), and any data we want to pass in. Response objects are immutable which means that the call to render() won’t update the response object; instead it will return us a new object which is why it needs to be captured like this. This is always true when you operate on the response object.
这里唯一的新部分是设置$response变量的倒数第二行。既然视图在DIC中,咱们能够将其称为$this->view。调用render()须要提供三个参数:要使用的$response、模板文件(在默认模板目录中)和任何咱们但愿传入的数据。响应对象是不可变的,这意味着render()调用不会更新响应对象;相反,它将返回一个新对象,这就是为何须要像这样捕获它。当您对响应对象进行操做时,就须要这样作才行。
When passing the data to templates, you can add as many elements to the array as you want to make available in the template. The keys of the array are the variables that the data will exist in once we get to the template itself.
当将数据传递给模板时,您能够向数组中添加尽量多的元素,使其在模板中可用。数组的键将以变量的形式在模板中存在。
As an example, here’s a snippet from the template that displays the ticket list (i.e. the code from src/templates/tickets.phtml - which uses Pure.css to help cover my lack of frontend skills):
例如,下面是显示门票列表的模板代码片断(即src/templates/tickets.phtml中的代码-它使用Pure.css来帮助弥补我缺少的前端技能):
<h1>All Tickets</h1>
<p><a href="/ticket/new">Add new ticket</a></p>
<table class="pure-table">
<tr>
<th>Title</th>
<th>Component</th>
<th>Description</th>
<th>Actions</th>
</tr>
<?php foreach ($tickets as $ticket): ?>
<tr>
<td><?=$ticket->getTitle() ?></td>
<td><?=$ticket->getComponent() ?></td>
<td><?=$ticket->getShortDescription() ?> ...</td>
<td>
<a href="<?=$router->pathFor('ticket-detail', ['id' => $ticket->getId()])?>">view</a>
</td>
</tr>
<?php endforeach; ?>
</table>
复制代码
In this case, $tickets is actually a TicketEntity class with getters and setters, but if you passed in an array, you’d be able to access it using array rather than object notation here.
Did you notice something fun going on with $router->pathFor() right at the end of the example? Let’s talk about named routes next :)
在本例中,$tickets其实是一个带有getter和setter的TicketEntity类,但若是传入一个数组,则可使用数组而不是这里的对象符号访问它。
您是否注意到$router->pathFor()在示例的末尾发生了一些有趣的事情?下面让咱们讨论命名路由:)
When we create a route, we can give it a name by calling ->setName() on the route object. In this case, I am adding the name to the route that lets me view an individual ticket so that I can quickly create the right URL for a ticket by just giving the name of the route, so my code now looks something like this (just the changed bits shown here):
当咱们建立一个路由时,咱们能够经过调用route对象上的->setName()为它命名。在这种状况下,咱们经过给查看机票详情的路由起一个名字,所以仅凭这个路由名字就能够快速建立正确的URL,因此如今个人代码是这样的(只是更改的部分所示):
$app->get('/ticket/{id}', function (Request $request, Response \$response, $args) {
// ...
})->setName('ticket-detail');
复制代码
To use this in my template, I need to make the router available in the template that’s going to want to create this URL, so I’ve amended the tickets route to pass a router through to the template by changing the render line to look like this:
要在个人模板中使用这个,咱们须要用router变量来生成次URL,因此就像下面这样,我经过改变render这行代码,将路由中的router传入模板来实现:
$response = $this->view->render($response, 'tickets.phtml', ['tickets' => $tickets, 'router' => $this->router]);
复制代码
With the /tickets/{id} route having a friendly name, and the router now available in our template, this is what makes the pathFor() call in our template work. By supplying the id, this gets used as a named placeholder in the URL pattern, and the correct URL for linking to that route with those values is created. This feature is brilliant for readable template URLs and is even better if you ever need to change a URL format for any reason - no need to grep templates to see where it’s used. This approach is definitely recommended, especially for links you’ll use a lot.
因为/tickets/{id}路由有一个友好的名称,而且router对象如今能够在模板中使用,这就是使模板中的pathFor()调用起做用的缘由。经过提供id,能够在URL模式中将其用做命名占位符,而后经过这些值创建连接到该路由的正确URL。这个特性对于可读的模板URL来讲是很是棒的,若是您曾经由于一些缘由须要更改URL格式—不须要使用grep模板来查看它的使用位置,那么这个特性甚至更好。这种方法绝对值得推荐,特别是对于您将常用的连接。
This article gave a walkthrough of how to get set up with a simple application of your own, which I hope will let you get quickly started, see some working examples, and build something awesome.
本文简要介绍了如何使用您本身的简单应用程序进行设置,我但愿这将使您快速入门,看到一些工做示例,并构建一些很棒的东西。
From here, I’d recommend you take a look at the other parts of the project documentation for anything you need that wasn’t already covered or that you want to see an alternative example of. A great next step would be to take a look at the Middleware section - this technique is how we layer up our application and add functionality such as authentication which can be applied to multiple routes.
从这里开始,我建议您查看项目文档的其余部分,了解您须要的任何内容,这些内容尚未涵盖,或者您但愿看到另外一个示例。下一个重要的步骤是查看中间件部分——该技术是咱们如何分层应用程序并添加诸如身份验证之类的功能,这些功能能够应用于多个路由。