用于Yii的CRUD小部件生成器

购买汽车时,有关哈布雷(Habré)文章的评论和其他选择有什么共同点?



从数据建模的角度来看,它们都是“嵌套”实体,它们在与父对象隔离方面没有独立的意义。

Yii( php framework )具有Gii,它是一个内置代码生成器,可让您通过单击几下鼠标的数据模型来创建基本的CRUD接口,这可以显着加快开发速度,但仅适用于独立实体,例如上述示例中的文章或机器。

能够为“嵌套的”数据对象生成类似的东西真是太好了,对吗? 现在-您可以,欢迎来到kat了解详情。

对于本文最后最不耐烦的人,给出了快速入门的说明。

对于那些对本文感兴趣的人,请考虑从业务应用程序到内部设备的各个方面:

  • 业务案例:按主题发布
    • 主要主题列表
    • 相关职位清单
  • 引擎盖下:基于CRUD的Gii生成器
    • Gii发电机模板
    • 窗口小部件基类
    • 集成式立面控制器
  • 快速上手
    • 关于支持与发展

业务案例:按主题发布


也许对哈伯评论和一个不好的例子,因为 通常,它比文章本身更有用,但是,在任何情况下,开发应用程序时,通常情况下,数据模型的某个对象作为独立实体对用户都不感兴趣。

考虑一个简化的业务任务:创建一个网站来发布按各种主题分组的消息。

该站点应具有以下接口:

  1. 主页-将来应该支持各种小部件,但是在实现的当前阶段只有一个:按某种标准过滤的主题列表。
  2. 主题的完整列表-表格形式的主题的完整列表;
  3. 主题页面-有关主题的信息以及其中发布的帖子的列表。

很标准吧?

让我们看一下数据模型:



也没有惊喜。 两类模型将包含我们的业务逻辑:

  • Topic类-有关主题的数据,验证,其中的帖子列表以及一个单独的方法,该方法返回按主页上的小部件的条件过滤的主题列表。
  • Post类仅是数据和验证。

该应用程序将由两个控制器提供服务:

  • SiteController-标准页面(关于我们,联系方式等),授权(技术规范不要求,但我们知道一些)和索引-主页。 因为 我们假设将来有许多不同的小部件,将主页保留在此控制器中而不将其转移到特定于该模型的模型是有意义的。
  • TopicController是一组标准操作:列出,创建,编辑,查看和删除主题。

潜在地,您还可以生成一个PostController-用于管理目的和/或将代码片段复制粘贴到自定义小部件中,但这不在本文讨论范围之内。
到目前为止,大多数代码都可以使用gii生成,这可以加快开发速度并降低风险(更少的手动代码=更少的出错机会)。

剩下两个问题:

  1. 如何在主页上显示主题的过滤列表?
  2. 如何按主题显示帖子列表?

如果您可以使用自动生成器解决问题,那么这将是一项可靠的成就。

主要主题列表


站点/索引地址所服务的主页应包含按预定条件过滤的主题列表。 作为业务逻辑的一部分,过滤条件已包含在模型中。

为了显示,有几个实现选项。

首先,肮脏又快速,是直接在视图文件( views / site / index.php )中执行所有操作:

  1. 创建ActiveDataProvider ;
  2. Topic模型中的数据填充它;
  3. 使用标准的ListView / GridView小部件显示,手动指定必填字段。

您可以进一步将其打包到一个单独的视图文件中,例如views / site / _topic-list-widget.php ,从主文件中调用其渲染。 这将提供更多的可管理性和可扩展性,但看起来仍然很脏。

我们大多数人可能会根据所有规则在一个单独的命名空间(基本模板的app \ widgetapp \组件 -取决于您使用的版本)中创建一个单独的widget,在那里他们按模型封装ActiveDataProvider的创建并显示在单独的位置提交。 剩下的就是从主页调用此小部件。 从类分解,代码的可管理性和可扩展性的角度来看,此解决方案是最正确的。

但是,就该小部件的代码而言,是否在处理actionIndex()方面非常重复TopicController代码? 手动编写此代码很烦人。

自动生成此代码,然后仅调用完成的小部件会更好:

<?= \app\widgets\TopicControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => app\models\Topic::findBySomeSpecificCriteria() ], ]) ?> 

相关职位清单

用于查看主题/查看地址的主题的页面应包含有关主题本身的信息以及其中发布的消息的列表。 如果我们正确配置了表之间的关系,那么我们将自动获得模型中该主题的消息列表,从而仅保留显示问题。

与筛选的主题列表类似,我们有几乎相同的选项。

首先是在视图文件的代码中执行所有操作以查看主题( views / topic / view.php ):

  1. 创建ActiveDataProvider ;
  2. 用模型$ model-> getPosts()中的数据填充它;
  3. 使用标准的ListView / GridView小部件显示,手动指定必填字段。

第二个方法是将此代码隔离到一个单独的演示文稿文件中: views / topic / _posts-list-widget.php ,以免引起麻烦 -在某个地方重用它仍然会失败。

第三个是功能完善的小部件,它将在actionIndex ()部分中很大程度上复制条件PostController的代码,但是是手动编写的。

或自动生成代码并调用完成的小部件:

 <?= app\widgets\PostControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => $model->getPosts(), ], ]) ?> 

引擎盖下:基于CRUD的Gii生成器


定义了业务任务,概述了生成的窗口小部件的需求,我们将弄清楚如何精确地生成它。 Gii已经有一个用于CRUD控制器的生成器。 对于CRUD小部件,我们需要基于现有的小部件创建一个新的生成器。

在开始之前,有几个指向文档的链接-如果您决定编写自己的扩展名,该链接也将非常有用:


显然,所有功能都打包在Yii扩展程序中,该扩展程序是通过composer安装的,并进入了项目的vendor文件夹。

该扩展包括三个部分:

  1. 包含gii生成器模板的templates / crud目录
  2. Controller.php文件-用于小部件调用的内置Facade控制器;
  3. Widget.php文件是所有生成的窗口小部件的基类。



Gii发电机模板


该扩展必须生成代码,因此其核心部分是Gii生成器。

最初,假设要实现扩展,就足以为内置CRUD-Controller生成器编写您自己的模板。 顺便说一下,这就是为什么该目录称为模板而不是生成器的原因。 但是事实证明,CRUD-Controller生成器对输入数据进行了非常密集的验证,这不允许实现许多要求,例如,更改继承类。 因此,扩展名包含一个完整的生成器,而不仅仅是一个模板。

gii生成器由以下部分组成(所有部分都在templates / crud目录中):

  • 默认目录是所有魔术都发生的模板:该目录中的每个文件将对应于项目中一个生成的文件;
  • 文件form.php-您可能会从名称中猜出来,这是一种用于输入生成参数(类名等)的表格;
  • File Generator.php-一代乐团,从表单接收数据,对其进行验证,然后顺序调用模板文件以创建结果。

Generator.phpform.php文件相对于CRUD生成器中的原始文件主要包含外观上的更改:文件名,验证,描述和提示文本等。

模板文件负责生成的视图和窗口小部件代码本身。 首先,文件模板/crud/default/controller.php很重要,它负责直接从原始生成器生成与控制器类相对应的小部件类。

该小部件应具有与CRUD控制器相同的操作,但生成方式有所不同。 以下示例显示带有注释的生成结果:

  • actionIndex-该方法接受$查询参数,而不是所有模型的无条件输出;

     public function actionIndex($query) { $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } 
  • actionCreate和actionUpdate-如果成功,则无需返回重定向,它们仅返回成功代码,内置的Facade控制器提供进一步的处理;

     public function actionCreate() { $model = new Post(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return 'success'; } return $this->render('create', [ 'model' => $model, ]); } 

  • actionDelete-支持用于显示删除小部件的GET方法(默认情况下-一个按钮)和执行操作的POST; 如果成功,它也不执行重定向,而是返回代码。

     public function actionDelete($id) { $model = $this->findModel($id); if (Yii::$app->request->method == 'GET') { return $this->render('delete', [ 'model' => $model, ]); } else { $model->delete(); return 'success'; } } 

最后,查看文件包含以下基本编辑:

  • 所有标头都转换为h2而不是h1;
  • 删除了负责显示页面标题和面包屑的代码-小部件不应影响这些内容;
  • 使用模态窗口(内置“模态”小部件)完成模型的创建和编辑;
  • 添加了删除小部件模板-带有一个大红色按钮。

窗口小部件基类


生成器完成工作后,将在应用程序名称空间中创建小部件类。 继承链如下所示:为应用程序生成的窗口小部件继承自基本扩展窗口小部件类\ ianikanov \ wce \ Widget ,而后者又继承自基本Yii小部件(类\ yii \ base \ Widget)

扩展小部件的基类解决以下任务:

  1. 定义两个主要字段:$ action和$ params,通过它们将控制权从调用视图转移到窗口小部件;
  2. 定义许多可以在生成的类中覆盖的标准参数,例如,小部件的视图文件的路径,Facade控制器的名称和路径(以下有关它)以及错误消息;
  3. 渲染视图时定义标准参数:render和renderFile;
  4. 提供类似于控制器基础结构的事件基础结构,以便标准过滤器(如AccessControlVerbFilter)起作用
  5. 定义将所有这些收集在一起的run方法。

集成式立面控制器

显示这些数据没有问题-小部件是为此目的而设计的。 但是无论如何要进行编辑,都需要一个控制器。 为每个小部件生成一个唯一的控制器-失去了其全部本质。 使用标准CRUD并不总是相关的,我也不想依赖于gii的额外发布。 因此,该选件与通用的集成控制器外观一起使用。

该控制器通过配置文件在应用程序映射中注册,并且仅包含一个方法-actionIndex,该方法执行以下操作:

  1. 接受客户的请求;
  2. 将控制权转移到相应的小部件类;
  3. 处理由于小部件导致的业务错误;
  4. 重定向回主应用程序。

可能更重要的是指出该控制器不具备的功能:

  1. 它不检查访问级别-此逻辑属于特定的小部件;
  2. 它不执行任何输入操作-参数按原样传递给窗口小部件;
  3. 除了检查预定义的成功代码外,它不会处理输出。

这种方法使您可以维护外观的多功能性,而无需执行业务需求,包括安全需求,应用程序应用程序代码。

快速上手

业务挑战显而易见,准备开始了吗? 使用扩展名包括四个步骤:

  1. 安装;
  2. 配置;
  3. 代;
  4. 应用程序。

使用composer完成扩展的安装:

 php composer.phar require --prefer-dist ianikanov/yii2-wce "dev-master" 

接下来,您需要对应用程序配置文件进行几处更改。

首先,添加对gii生成器的引用:

 if (YII_ENV_DEV) { $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', 'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'], 'generators' => [ //here 'widgetCrud' => [ 'class' => '\ianikanov\wce\templates\crud\Generator', 'templates' => [ 'WCE' => '@vendor/ianikanov/yii2-wce/templates/crud/default', // template name ], ], ], ]; } 

其次,将集成的外观控制器添加到地图中:

 $config = [ ... 'controllerMap' => [ 'wce-embed' => '\ianikanov\wce\Controller', ], ... ]; 

这样就完成了安装和配置。

生成小部件:

  1. 打开gii;
  2. 选择“ CRUD Controller Widget”;
  3. 填写表格字段;
  4. 查看并生成代码。

此外,要使用小部件,必须通过指定操作和参数来调用它-与调用控制器几乎相同。

查看模型列表的小部件:

 <?= app\widgets\PostControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => $otherModel->getPosts(), ], ]) ?> 

用于查看一个模型的小部件:

 <?= app\widgets\PostControllerWidget::widget(['action' => 'view', 'params' => ['id' => $post_id]]) ?> 

模型创建小部件(按钮+形式包装在Modal中):

 <?= app\widgets\PostControllerWidget::widget(['action' => 'create']) ?> 

模型更改小部件(按钮+形式包装在Modal中):

 <?= app\widgets\PostControllerWidget::widget(['action' => 'update', 'params'=>['id' => $post_id]]) ?> 

模型删除小部件(按钮):

 <?= app\widgets\PostControllerWidget::widget(['action' => 'delete', 'params'=>['id' => $post_id]]) ?> 

小部件和所有视图的代码属于应用程序,可以轻松更改-一切与生成控制器时完全相同。

关于支持与发展


关于如何支持和开发扩展的几句话。 我有主要工作,还有一些我的“附带”项目(宠物项目)。 因此,此扩展是我的辅助项目中的一个辅助项目,因此我将仅针对我的项目需求对其进行改进。

按照开源的最佳传统,代码可在github上获得,我将在修复错误的方面提供支持,并且如果有人要发送拉取请求,我也会尝试及时进行审查,以便谁在乎,请加入。

Source: https://habr.com/ru/post/zh-CN450696/


All Articles