Slim是一个我很喜欢用的轻框架,我用它为我的任氏有无轩站点提供API服务。这几天趁着放假,想重新“折腾”一下我的站点,于是就开了一个虚拟机,装好了必要的软件,准备开发。
然后我发现,Slim这个框架已经升级到了4,有了重大的变化。
参照Slim官方说明创建项目后,目录结构如下(请忽略其中的nbproject目录):

-
app目录:它包含了对于整个应用来说最基本的一些文件。具体说明如下。 -
dependencies.php:它主要是创建应用全局的容器。应用安装时,会生成logger这个实例。数据库链接的实例也是在这里生成的:
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
...
PDO::class => function (ContainerInterface $c) {
$settings=$c->get('settings')['db'];
$host=$settings['host'];
$user=$settings['user'];
$pass=$settings['pass'];
$db=$settings['db'];
$dsn="mysql:host=$host;dbname=$db";
$conn=new PDO($dsn, $user, $pass);
$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $conn;
},
]);
};
注意,我在这里设置了数据库PDO链接的一些属性。以后在应用的任何地方,只要用到PDO这个类的声明,Slim都会在容器中去寻找PDO的实例,从而保证所有的数据库链接都是统一的。
middleware.php:中间件我目前在API中还不会用到。按照Slim的说明,中间件可以在应用前和后运行,修改相应的Request和Response对象。repositories.php:这里注册所有可以成为“仓库”的类。这是Slim 4新增的文件,用来统一管理MVC中的M;但Slim 4抛弃了M,而改用更轻量的仓库。这也是一个容器。如:
declare(strict_types=1);
use App\Domain\Book\BookRepository;
use App\Infrastructure\Persistence\Book\DBBookRepository;
use DI\ContainerBuilder;
return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
BookRepository::class => \DI\autowire(DBBookRepository::class),
]);
};
注:这里用到的一些类会在后续得到说明。不过可以提醒一下,Slim 4用一个接口(BookRepository)定义所有该仓库支持的操作,而用一个实例类(DBBookRepository)对这个接口进行实现。你可以把它们的这种关系类比成C++中的“hpp/cpp”的关系。
routes.php:这是常规的路由配置。请注意,Slim4放弃了一个controller中多个action的做法,而是一个action对应一个controller,如下例所示:
declare(strict_types=1);
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\App;
use Slim\Interfaces\RouteCollectorProxyInterface as Group;
use App\Application\Actions\Index\IndexAction;
use App\Application\Actions\Book;
return function (App $app) {
$app->get('/', IndexAction::class);
$app->group('/summary', function(Group $group) {
$group->get('', Book\SummaryAction::class);
});
};
于是,/summary这个路由就会调用Book\SummaryAction::class中制定的action函数。
settings.php:全局配置文件。数据库的配置也在此处出现:
return function (ContainerBuilder $containerBuilder) {
// Global Settings Object
$containerBuilder->addDefinitions([
'settings' => [
'displayErrorDetails' => true, // Should be set to false in production
'logger' => [
'name' => 'slim-app',
'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
'level' => Logger::DEBUG,
],
//Database connection
'db' => [
'host' => 'localhost',
'user' => '****',
'pass' => '****',
'db' => '****',
],
],
]);
};
总结:app目录中的,都是对应用全局产生影响的文件。
public目录:这里是应用的入口文件。一般情况下,对于一个API应用来说,这里没有什么需要修改的。src/Application目录:这里有四个目录。我们重点要关注的是src/Application/Actions目录。
根据我的理解,针对API要成立的各类实体,开发者可以进行逻辑抽象分类,形成各个action。在具体实现的时候,一般是针对每个实体实现一个抽象的类,然后对每个针对这个实体的操作定义一个action。
比如我的数据库中有“书籍(book)”这个实体,于是我就暂时定义了两个文件:BookAction.php和SummaryAction.php。
//BookAction.php
declare(strict_types=1);
namespace App\Application\Actions\Book;
use App\Application\Actions\Action;
use App\Domain\Book\BookRepository;
use Psr\Log\LoggerInterface;
use Psr\Http\Message\ResponseInterface as Response;
abstract class BookAction extends Action
{
/**
* {@inheritdoc}
*/
protected $repo;
public function __construct(BookRepository $repo, LoggerInterface $logger) {
parent::__construct($logger);
$this->repo=$repo;
}
}
注意到,这是一个抽象类,主要目的是为本类(以及后续子类)引入各种dependency。这里我引入了BookRepository和LoggerInterface。
//SummaryAction.php
declare(strict_types=1);
namespace App\Application\Actions\Book;
use Psr\Http\Message\ResponseInterface as Response;
class SummaryAction extends BookAction
{
/**
* {@inheritdoc}
*/
protected function action(): Response
{
$res=$this->repo->summary();
return $this->respondWithData($res);
}
}
SummaryAction继承了BookAction,完成了action方法。而在action方法中,通过调用repo中对应的方法而获取了数据并返回。Slim4很贴心地可以直接返回json数据,不用开发者再转换。调用的例子如下:

我们再来看src/Domain目录。
这个目录中包括用来定义各类仓库的文件。比如我们之前看到的BookRepository:
// /src/Domain/Book/BookRepository.php
declare(strict_types=1);
namespace App\Domain\Book;
interface BookRepository
{
public function summary(): array;
public function detail($id):array;
}
如前所述,这是一个接口,只是定义了各类接口,也就是这个仓库能做什么的定义。
最后来看看src/Infrastructure/Persistence目录。这个目录对上面提到的接口进行实现。
// /src/Infrastructure/Persistence/Book/DBBookRepository.php
declare(strict_types=1);
namespace App\Infrastructure\Persistence\Book;
use App\Domain\Book\BookRepository;
class DBBookRepository implements BookRepository
{
private $conn;
public function __construct(\PDO $conn) {
$this->conn=$conn;
}
public function summary(): array {
$sql='select count(*) bc, sum(kword) wc, sum(page) pc from book_book';
$res=$this->conn->query($sql)->fetch();
return $res;
}
public function detail($id): array {
}
}
请注意,这个类的构造函数中引入了\PDO,因此上文提到的dependency就起作用了。
总体感觉,Slim 4的结构还是很清晰的,耦合度也合适,确实是可以一用的轻量级框架。
本文推送到[go4pro.org]。
Leave a Reply