alhimik1986/yii2_crud_module

出色的yii2 Gii CRUD代码生成器,用于创建ajax应用程序。

1.0.1 2018-08-11 19:50 UTC

This package is auto-updated.

Last update: 2024-08-29 04:19:57 UTC


README

Latest Stable Version Latest Unstable Version License Total Downloads Monthly Downloads Daily Downloads

出色的Gii CRUD模块,用于生成基于Yii2框架的单页ajax应用程序代码。

演示: http://tfb7950x.bget.ru/application/web/index.php?r=film

Видео

特性

  • 使用ajax方法添加、读取、删除、更新记录,无需重新加载页面
  • 可以批量删除记录
  • 使用ajax而不是pjax(甚至支持IE8)
  • 有分页器,可以设置每页显示的记录数(Gii的所有扩展中都没有这项功能)
  • 可以同时按多个列排序,例如,先按职位排序,然后按姓氏对每个组进行排序
  • 可以按关联表排序(下面将演示如何操作)
  • 在文本字段中搜索时不需要按Enter键,当用户停止输入时自动启动搜索
  • 有快捷键(Insert、Ctrl+Enter、Ctrl+Delete、Esc,分别:添加、保存、删除、取消)
  • Esc键可以清除搜索文本框(虽然很小,但仍然让人感到愉悦)
  • 直接从服务器输出验证错误,而不是从客户端脚本输出(即可以通过ajax输出错误,例如,关于错误密码等)
  • 如果数据不符合指定格式,则在屏幕上输出数据(通常是服务器内部错误,如果不这样做,则错误可能根本不会被注意到,即使注意到了,也必须打开调试器)
  • 易于学习,生成的代码易于用于创建ajax页面。

限制

  • 当使用jQuery 3及以上版本时,触摸屏(移动设备)上的表单移动将停止工作。因此,建议使用jQuery 1或2。

待办事项

  • 添加在表格内部显示详细信息的功能(如这里的“展开”按钮:http://demos.krajee.com/grid-demo
  • 实现导出为html、csv、text、excel、pdf、json

安装

使用composer下载。在应用程序文件夹中的composer.json文件中添加以下行

    "require": {
		"alhimik1986/yii2_crud_module": "^1.0"
    },

或在命令行中输入

$ composer require alhimik1986/yii2_crud_module

然后在config/web.php文件中添加以下行

// Добавляю генератор кода ajax_form_crud
if (YII_ENV_DEV) {
	$config['modules']['gii']['generators']['ajax_form_crud_generator'] = [
		'class' => 'alhimik1986\yii2_crud_module\generators\crud\Generator',
		'templates' => [
			'ajax_form_template' => '@vendor/alhimik1986/yii2_crud_module/generators/crud/default',
		],
	];
}

访问地址: https:///index.php?r=giihttps:///gii 。然后点击“AjaxForm CRUD Generator”链接。

生成的代码的扩展使用

ajax-form.js库的哲学

让我们看看文件views/@conroller_id/index/_js_table.php。我们看到

new ajaxForm({
	...
});

所以,这个“类实例”传递了参数。这些参数覆盖了默认参数。默认参数可以在ajax-form.js库中看到,每个参数的用途在注释中指明。传递给这个类实例的值覆盖了默认参数,并放在“settings”变量中。经常使用“settings”变量,并将其作为参数传递给许多函数。顺便说一下,默认参数包含了ajax-form.js库的所有系统功能,因此,通过传递参数给实例可以完全改变这个库的行为。

为了让大家明白其结构,我将介绍这个库的哲学。

让我们假设如果我们手动使用jQuery实现CRUD会怎样工作

  • 需要指定选择器和触发表单出现的事件。在本例中,这是“添加”按钮(.ajax-form-button-create)和“click”事件。
  • 事件触发时,需要发送ajax请求以获取表单内容并将该表单输出到特定位置。
  • 获取到的表单需要设置为中心屏幕位置,使其可移动和可拉伸(处理表单1)。
  • 获取到的表单需要绑定“表单数据提交按钮”和“取消”按钮的事件(处理表单2)。
  • 当触发表单数据提交事件时,需要发送表单并获取一个结果(内部服务器错误、验证错误或成功结果)。
  • 在成功结果的情况下,需要删除表单并发送ajax请求以更新表格。

为此,我们需要3个带有回调函数的ajax请求,这些回调函数仅在成功结果的情况下依次调用。但由于我们还需要传递参数(例如,按钮选择器、请求url等),因此我们将它们封装在3个相应的对象中。

  1. 用于显示表单的名称为create。
  2. 用于提交表单的名称为submit。
  3. 用于在成功提交表单后更新表格的名称为afterSubmit。

在代码中,它们将如下所示。

new ajaxForm({
	create: {
		...
	},
	submit: {
		...
	},
	afterSubmit: {
		...
	},
});

ajaxForm()类中传递的对象的说明。

create对象。

每个对象将包含一些字段,让我们来考虑一下可能有哪些。我们需要指定触发ajax请求的条件,通常这是点击按钮。因此,我们需要指定选择器(例如,.ajax-form-button-create)和事件类型(通常为“click”)。此外,如果愿意,可以指定委托者,但默认值为document,因此如果未指定,则不会发生任何问题。因此,字段将具有以下名称:“delegator”,“selector”,“on”——分别代表委托者选择器和事件,在jQuery中它们将如下所示

$(delegator).on(on, selector, function(){
});

当条件满足时,将执行ajax请求,因此我们还需要指定此请求的参数。这些参数在“ajax”字段中设置。参数名称与常规jQuery方法$.ajax({})中的参数名称相同。但为了增加灵活性,在“ajax”字段中需要传递一个函数,该函数返回一个对象。如果函数返回值为false,则不会执行ajax请求,从而也不会执行后续链(submit和afterSubmit)。在代码中将大致如下所示

new ajaxForm({
	create: {
		delegator: document,
		selector: '.ajax-form-button-create',
		on: 'click',
		ajax: function(settings) {
			return {
				url: 'https:///controller_id/form'
			};
		}
	},
	submit: {
		...
	},
	afterSubmit: {
		...
	},
});

其中settings,如前所述,是传递参数和默认参数的混合。即,我们可以在回调函数中使用在ajaxForm中传递的所有内容。换句话说,通过变量settings,我们可以访问任何参数。例如,settings.create.selector - 我们设置的选择器。要访问触发函数调用的元素,它位于settings.create.$中。正如我们所理解的,我们可以像操作普通jQuery元素一样操作它,例如,settings.create.$.css({color: 'green'});

在成功请求的情况下,将调用回调函数“success”,它具有data和settings参数,其中data是接收到的数据。应注意的是,data只包含响应部分中的字段content的值。响应部分的格式将在下面描述。

return $($(data)).appendTo('<?=$wrapper_selector?>');

还应注意的是,success方法必须返回接收到的表单的jQuery对象。接下来,此对象将包含在settings.form.$中,它用于内部处理(例如,定位在屏幕中心,使表单可移动,绑定点击“保存”、“取消”、“关闭”按钮的事件等)。

在success方法之后,将调用afterSubmit方法。它用于处理表单的后续操作,例如,我们希望使用select2.js插件初始化我们的表单中的下拉列表,而文本区域(textarea)则使用WYSIWYG编辑器。代码示例

new ajaxForm({
	create: {
		...
		afterSuccess: function(settings){
			var $form = settings.form.$;
			$form.find('select').select2();
			$form.find('textarea').froalaEditor();
		}
	},
	submit: {
		...
	},
	afterSubmit: {
		...
	},
});

如果我们不想使我们的表单可移动和可拉伸,则需要将_methodForm()方法重写为空函数。示例

new ajaxForm({
	create: {
		...
		_handleForm: function(){},
	},
	submit: {
		...
	},
	afterSubmit: {
		...
	},
});

同样,您也可以重写任何其他系统功能:_success、_afterSuccess 等。要了解更多关于这些函数的作用,请查看 ajax-form.js 文件。

submit 对象

submit 对象具有大致相同的“字段”和“方法”

new ajaxForm({
	create: {
		...
	},
	submit: {
		// delegator: document, // Задать делегатор нельзя, делегатором является форма
		selector: '.ajax-form-button-submit, .ajax-form-button-delete', // селектор кнопок, по нажатию которых происходит отправка данных формы
		ajax: function(settings) {
			var $form = settings.submit.$.parents('form');
			// Если нажата кнопка "Удалить", то спрашивать подтверждение
			if ( settings.submit.$.hasClass('ajax-form-button-delete')) {
				if ( ! confirm('Удалить эту запись безвозвратно?'); ?>'))
					return false; // отправка данных не произойдет
			}
			
			var $_return = {};
			
			var url = $form.attr('action');
			var data = $form.serializeArray();
			
			// Если была нажата кнопка "Удалить", то меняем адрес и параметры запроса на те, которые удалят эту запись
			if ( settings.submit.$.hasClass('ajax-form-button-delete')) {
				url = <?= Url::to(['delete']); ?>';
				data = {
					<?= $className; ?>: {
						id: settings.create.$.attr('data_id')
					}
				};
			}
			$_return['url'] = url;
			$_return['data'] = data;
			$_return['type'] = 'post';
			
			return $_return;
		}
	},
	afterSubmit: {
		...
	},
});

如果点击了“保存”按钮,则替换一个地址和参数,如果点击了“删除”按钮,则替换其他地址和请求参数。

还有以下方法:notValid(data, settings) - 在验证出错时调用,error(xhr, settings) - 在服务器内部出错时调用。它们对应相应的系统方法:_notValid(data, settings) 和 _error(xhr, exception)。因此,如果我们想禁用验证错误或服务器内部错误的输出,以下是一个示例代码

new ajaxForm({
	submit: {
		...
		_notValid: function(){}, // отключаю вывод ошибок валидации
		_error: function(){},    // отключаю вывод внутренней ошибки сервера
	},
});

如果我们需要只在第一个遇到的字段上显示验证错误,建议使用以下代码

// Эффект тряски и навести фокус на указанный элемент
window.shakeAndFocus = function($elem) {
	if ($elem.length > 0) {
		$('html, body').stop().animate({scrollTop: $elem.offset().top - 90}, 200, function(){
			<?php if ($this->isMobileOrTablet()): ?>
				$elem.focus().shake(3, 5, 100);
			<?php else: ?>
				$elem.focus();
			<?php endIf; ?>
		});
	}
};

new ajaxForm({
	submit: {
		...
		notValid: function(data, settings){
			var $form = settings.create.$.parents('form');
			$form.find('.error:not(:first)').parent().html('');
			$form.find('.input-error:not(:first)').removeClass('input-error');
			$form.find('.label-error:not(:first)').removeClass('label-error');
			$form.find('.has-error').removeClass('has-error');
			// Получаю самое первое поле с ошибкой
			var i, j, k, id;
			for(i in data) {
				for(j in data[i]) {
					for(k in data[i][j]) {
						id = i+'_'+j;
						break;
					}
					break;
				}
				break;
			}
			
			var $input = $form.find('#'+id).first();
			window.shakeAndFocus($input);
		},
	},
});

afterSubmit 对象

在 afterSubmit 对象中使用 fewer 参数。它仅在提交成功后调用。

new ajaxForm({
	create: {
		...
		_handleForm: function(){},
	},
	submit: {
		...
	},
	afterSubmit: {
		ajax: function(settings) {
			$(settings.form.selector).remove(); // Закрываю форму только после удачной записи и обновлении таблицы
			$('<?=$table_id; ?>').trigger('search'); // Обновляю таблицу (поиск в таблице)
			return false; // Не делать ajax-запрос, т.к. форма обновляется вызовом триггера "search"
		},
		success: function(data, settings) {}
	},
});

按照逻辑,我们应该使用给定的地址和参数执行 ajax-请求以更新表格。但有一个更简单、更正确的方法:我们需要重新在表格中启动搜索,而不需要发送任何 ajax-请求。执行表格搜索的脚本位于模板 @vendor/alhimik1986/yii2_js_view_module/views/jsPlugins/_ajaxTable.php 中。如果在某个时刻需要启动表格搜索,则在该表格中启动 'search' 触发器。

$('#table_id').trigger('search');

ajaxForm 的其他对象

如果您不使用 ActiveForm 并且启用了 enableCsrfValidation,则服务器可能会抱怨没有传递 csrf-令牌。为了避免这种情况,您可以在 ajaxForm 中传递这些令牌。

我们经常需要看到数据上传或服务器处理数据的过程。为此,我们需要一个加载指示器,它在发送 ajax-请求时出现,并在请求完成后消失。在 _js_table.php 模板中,我们指定需要放置该指示器的元素(loadingElem),并设置该指示器的 CSS 样式(loadingStyle)。总的来说,我们的其他 ajaxForm 对象看起来大致如下

new ajaxForm({
	loadingElem: loadingElem,
	loadingStyle: loadingStyle,
	csrf: csrf,
	create: {
		...
		_handleForm: function(){},
	},
	submit: {
		...
	},
	afterSubmit: {
		...
	},
});

服务器对 ajax-请求的响应格式

在 ajax-请求中,服务器应使用 JSON 格式生成响应。该对象应包含以下部分

{
	status: 'success' | 'error', // статус ответа (success - результат успешный и цепочка вызовов будет продолжена, error - ошибка валидации)
	content: 'text',             // Содержимое ответа, которое передедается в аргумент data.
	message: '',                 // flash-сообщения, которые используется в yii-фреймворке и задаются таким образом: Yii::$app->session->setFlash('success', 'Сохранено успешно!')
}

为了避免处理格式,有一个名为 alhimik1986\yii2_crud_module\web\JsonController 的类,它继承自 yii\web\Controller。其中已经内置了用于生成响应的必要方法。我们可以在生成的控制器中看到实现示例。示例

return $this->renderJson('view_name', ['model' => $model]);  // Тоже самое, что и $this->renderPartial('view_name', ['model'=>$model]), только в нужном формате для ajax-form.js. {status: 'success', content: 'содержимое вьюшки', message: ''}
return $this->checkErrorsAndDisplayResult($model); // Делает всю грязную работу: проверяет модель на наличие ошибок валидации и, если они есть, выводит их в нужном формате; если ошибок нет, то выводит {status: 'success', content: 'ok', message: ''}

如果模型包含验证错误,则 ajax-form.js 将找到包含错误的字段,并在父元素 (.form-group .field-modelName-fieldName) 中添加 .has-error 类。如果表单中不存在父类 (.form-group .field-modelName-fieldName),则为了安全起见,ajax-form.js 在文本字段中添加 .input-error 类,在其标签 (label) 中添加 .label-error 类,但为了使它们用红色突出显示,需要在 CSS 中添加 .input-error, .label-error {color: #a94442;}

如果服务器的响应不符合指定的格式(例如,在服务器内部出错时),则 ajax-form.js 将在弹窗中输出它接收到的所有内容,即输出屏幕上的整个错误文本。

在同一页面上使用多个 crud

是的,有这样的可能性。为此,需要为每个 crud 分别生成。然后需要将 index.php 模板的内容合并到一个,并将两个 crud 的 _js_table.php 和 _js_plugins.php 包含进来。然后,在 _js_table.php 模板中,需要找到生成 URL 地址的方法:Url::to(['create']) 并将相应的控制器添加到地址中,例如,Url::to(['/myController/create'])

请记住,这些 crud 不会相互干扰,因为它们位于不同的 $wrapper_selector(请参阅 _js_table.php)上,即不同的具有不同 id 的标签。请记住,一个 crud 的表单不会删除另一个 crud 的表单,因为它们的表单具有唯一的 id,并且在 ajaxForm 中进行了指定。

new ajaxForm({
	form: {
		selector: '#<?= $className; ?>-ajax-form',
	},
	create: {
		...
	},
	submit: {
		...
	},
	afterSubmit: {
		...
	},
});