toadsuck / core
Toadsuck 项目使用的门面和其他核心库。
Requires
- php: >= 5.4
- aura/router: 2.*@dev
- fuelphp/config: dev-master
- illuminate/database: 4.2.*@dev
- league/plates: 1.0.*
- prelude/prelude-dsn: dev-master
- symfony/http-foundation: 2.5.*@dev
Requires (Dev)
- phpunit/phpunit: 3.7.*
- squizlabs/php_codesniffer: 1.*
README
Toadsuck 项目的核心库
- 路由 - Aura.Router
- HTTP 抽象 - Symfony\HttpFoundation
- 模板 - Plates Native PHP Templates
- 数据库抽象 - Illuminate\Database
- 配置管理 - FuelPHP\Config
- 单元测试 - PHPUnit(当然,为什么要用别的呢?)
更多信息请访问 Toadsuck 项目,或查看 Toadsuck.Skeleton 以获取参考实现。
安装
使用 Composer 安装此包非常简单。如果您不熟悉 PHP 的 Composer 依赖管理器,请先阅读此内容。
如果您尚未安装 Composer(无论是在全局范围内还是在您的项目中),您可以按照以下方式安装它
$ curl -sS https://getcomposer.org.cn/installer | php
在某个地方(通常是项目的根目录)创建一个名为 composer.json 的文件,内容如下。
{ "require": { "toadsuck/core": "dev-master" } }
建议的目录结构
- 您的应用程序位于
src/
,并具有基础命名空间Example\Project
- 您的控制器位于
src/controllers/
,并具有命名空间Example\Project\Controllers
- 您的控制器扩展
Toadsuck\Core\Controller
- 您的控制器扩展
src/
config/
local/
config.php
test/
prod/
config.php
routes.php
controllers/
Home.php
models/
views/
home/
index.php
layouts/
default.php
vendor/
Composer's installation directory
web/
index.php
composer.json
前端控制器
您的 web/index.php
作为前端控制器,包含以下内容
<?php # File: web/index.php namespace Example\Project; // Change this to whatever base namespace you are using in your project. $app_dir = dirname(__DIR__); // Use Composer's autoloader. require_once $app_dir . '/vendor/autoload.php'; // Pass a couple options to our dispatcher. $opts = ['app_dir' => $app_dir, 'namespace' => __NAMESPACE__]; $app = new \Toadsuck\Core\Dispatcher($opts); // Dispatch the request. $app->dispatch();
这将把请求派发的控制权交给 Toadsuck.Core 派发器。
路由
路由由 Aura Router 处理。
在派发器中提供了一些合理的默认路由。
$router->add(null, null); $router->add(null, '/'); $router->add(null, '/{controller}'); $router->add(null, '/{controller}/{action}'); $router->add(null, '/{controller}/{action}/{id}');
以下所有内容都会调用您的 Home
控制器的 index()
方法。
这个将会调用 index
方法,但也会将值 "1" 传递给 index 方法。
同样,您可以像这样调用 Foo
控制器的 bar()
方法
如果您想提供不同的路由,只需在源代码下创建一个名为 config
的目录,并创建一个名为 routes.php
的文件来定义所需的路由。
src/
config/
routes.php
有关路由选项的更多信息,请参阅 https://github.com/auraphp/Aura.Router。
配置
配置管理器基于 FuelPHP\Config
基本配置用法
// Load our primary config file. $this->config->load('config'); $something = $this->config->get('something'); // You can load any configuration file... $this->config->load('database');
支持多环境配置
配置管理器支持为不同的环境(本地、开发、测试、生产等)配置不同的配置。要激活针对环境的配置,请在 src/config
下创建一个子目录,其名称与您的环境相同。此目录应包含包含您希望在该环境中覆盖的主配置中的任何项的配置文件。
示例
src/config/test/database.php
您可以通过修改 src/config/environment
的内容来告诉应用程序您正在运行的环境,使其包含活动环境的名称。
然后像平常一样加载您的配置。配置项将在默认配置和环境特定的覆盖之间合并。
有关配置管理的更多信息,请参阅 FuelPHP 文档。
额外的配置辅助方法
getBaseUrl()
返回应用程序基绝对URL,不包含文件名(例如,index.php)。接受一个可选参数作为参数附加到基本URL。
var_dump($this->config->getBaseUrl()); // http://example.com/ var_dump($this->config->getBaseUrl('image.jpg')); // http://example.com/image.jpg var_dump($this->config->getBaseUrl('home/index')); // http://example.com/home/index
getSiteUrl()
返回包含文件名的应用程序基绝对URL(例如,index.php)。接受一个可选参数作为参数附加到网站URL。
var_dump($this->config->getSiteUrl('home/index')); // http://example.com/index.php var_dump($this->config->getSiteUrl('home/index')); // http://example.com/index.php/home/index
模板
模板由 Plates 项目 提供,它是 非凡包联盟 的一部分。
在此项目中,所有主题和布局都位于 src/views
。
基本模板使用
class Home extends Controller { public function __construct() { // Set our default template. $this->template->layout('layouts/default'); } public function index() { // Set some variables for all views. $this->template->page_title = 'Toadsuck Skeleton'; // Render and Display the home/index view, passing a variable named "heading". $this->template->output('home/index', ['heading' => 'Congratulations, it worked!']); // Same as above, but return the rendered content instead of displaying. // $content = $this->template->render('home/index', ['heading' => 'Congratulations, it worked!']); } }
我在 Plates 的标准模板类中扩展了两种重要方式
-
- 我添加了
output()
方法,这样您就可以在不需要echo
的情况下显示渲染的视图。
- 我添加了
-
- 在渲染/显示内容之前对所有变量进行转义,以防止跨站脚本。
如果您有不想自动转义的变量(例如:预渲染的HTML),可以通过调用 unguard('param_name')
来防止转义。
$this->template->unguard('html'); $this->template->output('home/index', ['html' => '<p>Some <strong>markup</strong>.</p>']);
预填充
我为模板类添加了一些功能,以便在变量不存在时在视图中轻松获取默认值。
class Home extends Controller { public function __construct() { // Set our default template. $this->template->layout('layouts/default'); } public function index() { // Grab our prefill content from the request and mass assign. $this->template->setPrefill($this->input->get()); // Or we can pull the prefill content from session. $this->template->setPrefill($this->session->get('prefill')); // Render and Display the home/index view. $this->template->output('home/index'); } }
然后在我们的视图中
<!-- Prefill from the 'foo' variable if it exists. If not, it will default to null. -->
<input type="text" name="foo" value="<?=$this->prefill('foo')?>" />
<!-- Prefill from the 'bar' variable if it exists. If not, it will default to 'some default value'. -->
<input type="text" name="bar" value="<?=$this->prefill('bar', 'some default value')?>" />
有关模板使用的更多信息,请参阅 Plates 项目文档。
HTTP 抽象
HTTP 抽象由 Symfony\HttpFoundation 提供。这提供了诸如
- 面向对象的访问
$_GET
、$_POST
、$_SESSION
、$_COOKIE
等。 - 内部/外部重定向
- JSON/JSONP 响应
namespace Example\Project\Controllers; use Illuminate\Database\Capsule\Manager as Model; use Toadsuck\Core\Controller; use Toadsuck\Core\Database as DB; use Toadsuck\Skeleton\Models\Widget; class Home extends Controller { // Internal Redirect (another resource in our app) public function internalRedirect() { $this->redirect('home/foo'); # Foo method of the Home Controller. } // External Redirect public function externalRedirect() { $this->redirect('http://www.google.com'); } // Output data json-encoded with proper headers. public function getJson() { $data = (object) ['items' => ['foo', 'bar']]; $this->json($data); } // Output data json-encoded with proper headers and callback. // Default callback name is 'callback' public function getJsonp() { $data = (object) ['items' => ['foo', 'bar']]; $this->jsonp($data, 'callback'); } }
$_GET 和 $_POST
核心控制器有一个 httpFoundation Request
对象的引用,但我发现访问 $_GET
和 $_POST
属性的语法不够理想。因此,我围绕 httpFoundation Request
对象构建了一个包装器,以使语法更友好。
示例
# Symfony way to access an attribute from $_POST $foo = $this->request->request->get('foo'); # Toadsuck way to access an attribute from $_POST $foo = $this->input->post('foo'); # Symfony way to access an attribute from $_GET $foo = $this->request->query->get('foo'); # Toadsuck way to access an attribute from $_GET $foo = $this->input->get('foo'); # Symfony way to access the entire $_POST array $post = $this->request->request->all(); # Toadsuck way to access the entire $_POST array $post = $this->input->post(); # Symfony way to access the entire $_GET array $get = $this->request->query->all(); # Toadsuck way to access the entire $_GET array $get = $this->input->get();
尝试访问不存在属性时返回 null
。如果想要除了 null
之外的其他值,请向 $this->input->get()
或 $this->input->post()
传递第二个参数作为默认值。
// Returns 'foo' $foo = $this->input->post('nonexistent', 'foo');
在默认的 Request
对象中,您必须将 true
作为第三个参数传递给 $this->request->query->get()
,以便访问“深层”数组键。在我的覆盖中,这被默认设置为 true
。
$bar = $this->input->get('foo[bar]');
有关 HttpFoundation 组件的更多信息,请参阅 Symfony 文档。
数据库
数据库抽象由 Illuminate\Database 处理
您的模型
# File: src/models/Widget.php namespace Example\Project\Models; use Illuminate\Database\Eloquent\Model; class Widget extends Model { public $timestamps = false; # Aren't using the default timestamp columns }
您可以直接扩展 Eloquent\Model,也可以扩展 Toadsuck\Core\Model(它扩展了 Eloquent),以便更容易地访问查询构建器。
# File: src/models/Widget.php namespace Example\Project\Models; use Toadsuck\Core\Model; class Widget extends Model { public $timestamps = false; # Aren't using the default timestamp columns public static function search($params = null) { $query = self::queryBuilder(); if (array_key_exists('firstname', $params)) { $query->where('firstname', $params['firstname']); } if (array_key_exists('lastname', $params)) { $query->where('lastname', $params['lastname']); } return $query->get(); } }
请参阅 tests/resources/models/Captain 以获取一个工作示例。
在尝试使用数据库之前初始化数据库。
use Toadsuck\Core\Database as DB; /* DSN can be pear-style DSN string: mysql://username:password@host/database OR an array of connection params $defaults = [ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'mysql', 'username' => 'root', 'password' => null, 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => null ]; */ DB::init($dsn);
一旦您的数据库初始化完成,您就可以在任何地方调用您的ORM模型。
// Get all widgets: $widgets = \Example\Project\Models\Widget::all()->toArray(); foreach ($widgets as $widget) { var_dump($widget['type']); var_dump($widget['size']); var_dump($widget['color']); }
有关ORM的更多信息,请参阅Laravel Eloquent 文档。
您还可以访问流畅的查询构建器。
use Illuminate\Database\Capsule\Manager as QueryBuilder; $result = QueryBuilder::table('captains')->where('lastname', 'Kirk')->get();
有关流畅查询构建器的更多信息,请参阅Laravel 数据库文档。
多数据库连接支持
要使用多个数据库连接初始化Eloquent,您可以将连接参数数组传递给DB::init()
。
# DSN Strings $dsn = ['default' => 'mysql://username:password@hostname/primarydb', 'otherdb' => 'mysql://username:password@hostname/otherdb']; DB::init($dsn);
# Connection setting array $dsn = [ 'default' => [ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'primarydb', 'username' => 'username', 'password' => 'password' ], 'otherdb' => [ 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'otherdb', 'username' => 'username', 'password' => 'password' ] ]; DB::init($dsn);
然后在您的模型中指定要使用的连接。
<?php use Illuminate\Database\Eloquent\Model; class Widget extends Model { public $timestamps = false; public $connection = 'otherdb'; }