robclancy / xf-support
此包包含在XenForo中工作时删除大量模板代码的工具。
Requires
- php: >=5.3.0
- robclancy/db-connector: 1.0.*
- robclancy/schema-builder: 1.0.*
Requires (Dev)
- mockery/mockery: 0.7.2
This package is auto-updated.
Last update: 2024-09-22 04:09:28 UTC
README
此包包含在XenForo中工作时删除大量模板代码的工具。
安装
即将推出...
示例
注意:其中很多仍然是相当多的模板代码和繁琐,我将在未来实现xf-toolkit生成大量代码。
安装程序
这些使用来自illuminate/database的分支来提供模式构建器。
待办事项:更多信息以及Laravel文档链接
以下是一个示例安装程序...
class TestInstaller extends Installer { public function up1() { $this->schema->create('photos', function($table) { $table->increments('photo_id'); $table->string('filename'); }); } public function down1() { $this->schema->drop('photos'); } public function up2() { $this->schema->create('albums', function($table) { $table->increments('album_id'); $table->string('name'); }); $this->schema->table('photos', function($table) { $table->integer('album_id')->unsigned()->after('photo_id'); }); } public function down2() { $this->schema->table('photos', function($table) { $table->dropColumn('album_id'); }); $this->schema->drop('albums'); } }
数据模型
此设计是为了拥有只与数据库一起工作的方法。其他模型数据,例如调整缩略图大小,应在传统的模型中完成,因为这些模型是为了与以下详细说明的存储库一起工作而设计的。
class MyModel extends \Robbo\Support\DataModel { $this->_table = 'my_table'; $this->_key = 'table_id'; }
这就是实现你在Robbo\Support\DataModelInterface
中看到的所有内容的全部。
您仍然需要定义自己的连接、条件和顺序。
class MyModel extends \Robbo\Support\DataModel { $this->_table = 'my_table'; $this->_key = 'table_id'; const FETCH_MY_OTHER_TABLE = 0x01; public function getResourceJoinOptions(array $fetchOptions) { $selectFields = ''; $joinTables = ''; if ( ! empty($fetchOptions['join'])) { if ($fetchOptions['join'] & self::FETCH_MY_OTHER_TABLE) { $selectFields .= ', other_table.*'; $joinTables .= ' INNER JOIN other_table ON (my_table.table_id = other_table.table_id)'; } } return array('selectFields' => $selectFields, 'joinTables' => $joinTables); } public function prepareResourceConditions(array $conditions, array &$fetchOptions) { $db = $this->_getDb(); $sqlConditions = array(); if (isset($conditions['something'])) { $sqlConditions[] = 'my_table.something = ' . $db->quote($conditions['something']); } return $this->getConditionsForClause($sqlConditions); } protected function prepareResourceOrderOptions(array &$fetchOptions, $defaultOrderSql = '') { return 'TODO: add order example here'; } }
存储库
这些实际上是数据模型的包装器,用于允许更容易的单元测试和更干净的代码。存在一个具体的实现Robbo\Support\Repository
,但是您可以扩展或实现自己的,因为传递的是接口而不是具体类。
要使用具体实现,您需要给它一个要使用的数据模型。以下是在传统控制器中使用上述数据模型的一个示例。
class MyController extends XenForo_ControllerPublic_Abstract { public function actionIndex() { $repository = new \Robbo\Support\Repository($this->getModelFromCache('MyModel')); return $this->responseView('mytemplate', array( 'myData' => $repository->getAll() )); } public function actionEdit() { $repository = new \Robbo\Support\Repository($this->getModelFromCache('MyModel')); $id = $this->_input->filterSingle('id', XenForo_Input::UINT); if ( ! $resource = $repository->getById($id)) { return $this->responseNoPermission(); } return $this->responseView('mytemplate_edit', array( 'myData' => $resource )); } public function actionSave() { $repository = new \Robbo\Support\Repository($this->getModelFromCache('MyModel')); $input = $this->_input->filter(array('one' => XenForo_Input::UINT, 'two' => XenForo_Input::STRING)); $id = $this->_input->filterSingle('id', XenForo_Input::UINT); if ( ! $repository->getById($id)) { return $this->responseNoPermission(); } $repository->save($id, $input); return $this->responseRedirect('somewhere'); } }
现在那里仍然有一些模板代码,这让我想到了下一个示例...
控制器
控制器实际上只是有一些小助手。例如,您不必键入XenForo_Input::UINT
,而是可以这样做self::UINT
。
然后有在控制器生命周期早期创建存储库和模型的助手。因此,上述控制器可以简化为以下内容
class MyController extends XenForo_ControllerPublic_Abstract { protected $_dataModelName = 'MyModel'; protected $_idName = 'id'; public function actionIndex() { return $this->responseView('mytemplate', array( 'myData' => $this->repository->getAll() )); } public function actionEdit() { if ( ! $resource = $this->repository->getById($this->_id) { return $this->responseNoPermission(); } return $this->responseView('mytemplate_edit', array( 'myData' => $resource )); } public function actionSave() { if ( ! $this->repository->getById($this->_id)) { return $this->responseNoPermission(); } $this->repository->save($this->_id, $this->_input->filter(array('one' => self::UINT, 'two' => self::STRING))); return $this->responseRedirect('somewhere'); } }
数据写入器
我讨厌像为模型那样为数据写入器编写所有模板代码。因此,我添加了一些小快捷方式来使其不那么繁琐,并且更容易阅读。
class MyWriter extends \Robbo\Support\DataWriter { /* Old way protected function _getFields() { return array( 'merc_gallery_media' => array( 'media_id' => array('type' => self::TYPE_UINT, 'autoIncrement' => true), 'category_id' => array('type' => self::TYPE_UINT, 'required' => true), 'user_id' => array('type' => self::TYPE_UINT, 'required' => true), 'username' => array('type' => self::TYPE_STRING, 'required' => true, 'maxLength' => 50), 'ip_id' => array('type' => self::TYPE_UINT, 'default' => 0), 'image' => array('type' => self::TYPE_STRING, 'default' => '', 'maxLength' => 50), 'video' => array('type' => self::TYPE_STRING, 'default' => '', 'maxLength' => 255), 'title' => array('type' => self::TYPE_STRING, 'required' => true, 'maxLength' => 100), 'description' => array('type' => self::TYPE_STRING, 'default' => ''), 'media_state' => array('type' => self::TYPE_STRING, 'default' => 'visible', 'allowedValues' => array('visible', 'moderated', 'deleted') ), 'added_date' => array('type' => self::TYPE_UINT, 'default' => XenForo_Application::$time), 'upload_date' => array('type' => self::TYPE_UINT, 'default' => XenForo_Application::$time), 'view_count' => array('type' => self::TYPE_UINT, 'default' => 0), 'likes' => array('type' => self::TYPE_UINT_FORCED, 'default' => 0), 'like_users' => array('type' => self::TYPE_SERIALIZED, 'default' => 'a:0:{}'), ) ); } protected function _getExistingData($data) { if ( ! $id = $this->_getExistingPrimaryKey($data)) { return false; } return array('merc_gallery_media' => $this->getModelFromCache('Merc_Gallery_Model_Media')->getMediaById($id)); } protected function _getUpdateCondition($tableName) { return 'media_id = ' . $this->_db->quote($this->getExisting('media_id')); }*/ // New way starting here... protected $_table = 'merc_gallery_media'; protected $_key = 'media_id'; protected function _setFields() { $this->_field('media_id')->uinteger()->auto(); $this->_filed('category_id')->uinteger()->required(); $this->_field('user_id')->uinteger()->required(); $this->_field('username')->string(50)->required(); $this->_field('ip_id')->uinteger()->default(0); $this->_field('image')->string(50); $this->_field('video')->string(255); // And so on... this is still fairly tedious so I will be looking at ways to improve it } protected function _getExistingData($data) { return $this->_genericExistingData( 'merc_gallery_media', 'media_id', $this->_createRepository($this->getModelFromCache('MyModel')), $data ); } protected function _getUpdateCondition($tableName) { return $this->_genericUpdateCondition($tableName, 'media_id'); } }
路由前缀
我发现我的大多数路由前缀都是重复的。因此,我使其能够仅定义几个变量。
旧前缀...
class Merc_Sidebar_Route_PrefixAdmin_Blocks implements XenForo_Route_Interface { public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router) { $action = $router->resolveActionWithIntegerParam($routePath, $request, 'block_id'); return $router->getRouteMatch('Merc_Sidebar_ControllerAdmin_Block', $action, 'sidebars'); } public function buildLink($originalPrefix, $outputPrefix, $action, $extension, $data, array &$extraParams) { return XenForo_Link::buildBasicLinkWithIntegerParam($outputPrefix, $action, $extension, $data, 'block_id', 'title'); } }
现在,通过扩展支持类来执行相同的事情...
class Merc_Sidebar_Route_PrefixAdmin_Blocks extends \Robbo\Support\RoutePrefix { protected $_controller = 'Merc_Sidebar_ControllerAdmin_Block'; protected $_id = 'block_id'; protected $_group = 'sidebars'; }