werx / core
Werx 项目使用的入口控制器和其他核心库。
Requires
- php: >= 5.5
- aura/router: ~2.0
- illuminate/database: ~4.2
- league/plates: ~2.1
- nyholm/dsn: ~1.0
- symfony/http-foundation: ~2.6
- werx/config: ~1.0
- werx/url: ~1.2
Requires (Dev)
- phpunit/phpunit: ~4.4
- squizlabs/php_codesniffer: ~2.2
README
Werx 项目的核心库
- 路由 - Aura.Router
- HTTP 抽象 - Symfony\HttpFoundation
- 模板 - Plates Native PHP Templates
- 数据库抽象 - Illuminate\Database
- 配置管理 - werx\Config
- 单元测试 - PHPUnit(当然,为什么还要用别的呢?)
更多了解请访问 Werx 项目 或查看 werx\Skeleton 以获取参考实现。
安装
使用 Composer 安装此包非常简单。如果您不熟悉 PHP 的 Composer 依赖管理器,您应该先阅读这篇文档。
{ "require": { "werx/core": "dev-master" }, "minimum-stability": "dev" }
建议的目录结构
- 您的应用程序位于
src/
,并具有基本命名空间Example\Project
- 您的控制器位于
src/controllers/
,并具有命名空间Example\Project\Controllers
- 您的控制器扩展
werx\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 \werx\Core\Dispatcher($opts); // Dispatch the request. $app->dispatch();
这将使请求分发控制权转到 werx.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。
配置
配置管理器基于 werx\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
文件的内容来指定应用程序正在运行的环境,只需在其中包含您活动环境的名称。
然后,按正常方式加载您的配置。配置项将在默认配置和环境特定覆盖之间合并。
有关配置管理的更多信息,请参阅werx\Config 文档。
额外的配置辅助方法
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
模板
在此项目中,所有主题和布局都位于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 = 'Werx 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 werx\Core\Controller; use werx\Core\Database as DB; use werx\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'); # Werx 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'); # Werx 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(); # Werx way to access the entire $_POST array $post = $this->input->post(); # Symfony way to access the entire $_GET array $get = $this->request->query->all(); # Werx way to access the entire $_GET array $get = $this->input->get();
尝试从 get/post 访问不存在的属性时返回 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,也可以扩展 werx\Core\Model(它扩展了 Eloquent),以获得对查询构建器的轻松访问。
# File: src/models/Widget.php namespace Example\Project\Models; use werx\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 werx\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'; }